代码之家  ›  专栏  ›  技术社区  ›  Wobbles

具有可绑定选定项的多选列表框DP未绑定回源

  •  1
  • Wobbles  · 技术社区  · 6 年前

    我根据这个答案改编了一些代码( https://stackoverflow.com/a/51254960/3797778 )希望添加SelectedValuePath功能,因为我要存储的集合实际上是包含在listbox实际绑定到的对象集合中的属性值的集合。所以基本上我想把选中的项绑定到一个ID列表,而不是完整的对象。

    我为列表框修改的代码如下:

    public class MultipleSelectionListBox : ListBox, INotifyPropertyChanged
    {
        public static readonly DependencyProperty BindableSelectedItemsProperty =
            DependencyProperty.Register("BindableSelectedItems",
                typeof(IEnumerable<dynamic>), typeof(MultipleSelectionListBox),
                new FrameworkPropertyMetadata(default(IEnumerable<dynamic>),
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBindableSelectedItemsChanged));
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        public IEnumerable<dynamic> BindableSelectedItems
        {
            get => (IEnumerable<dynamic>)GetValue(BindableSelectedItemsProperty);
            set {
                SetValue(BindableSelectedItemsProperty, value);
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("BindableSelectedItems"));
            }
        }
    
        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);
            BindableSelectedItems = SelectedItems.Cast<dynamic>();
        }
    
        private static void OnBindableSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is MultipleSelectionListBox listBox)
            {
                List<dynamic> newSelection = new List<dynamic>();
                if (!string.IsNullOrWhiteSpace(listBox.SelectedValuePath))
                    foreach (var item in listBox.BindableSelectedItems)
                    {
                        var collectionValue = item.GetType().GetProperty(listBox.SelectedValuePath).GetValue(item, null);
                        foreach (var lbItem in listBox.Items)
                        {
                            if (lbItem.GetType().GetProperty(listBox.SelectedValuePath).GetValue(lbItem, null) == collectionValue)
                                newSelection.Add(lbItem);
                        }
                    }
                else
                    newSelection = listBox.BindableSelectedItems as List<dynamic>;
    
                listBox.SetSelectedItems(listBox.BindableSelectedItems);
            }
        }
    }
    

    我的物品在磅:

    public class DeviceChannelInfo
    {
        public DeviceStateInfo parentDeviceState { get; set; }
        public string name { get; set; }
        public string displayName { get; set; }
        public int id { get; set; }
    }
    

    我的LB代码:

    <uc:MultipleSelectionListBox ItemsSource="{Binding Source={x:Static local:SharedProperties.deviceChannelInfos}, Mode=OneWay}" SelectionMode="Extended" SelectedValuePath="name" IsSynchronizedWithCurrentItem="True" BindableSelectedItems="{Binding MyCollectionOfSelectedIDs, Mode=TwoWay}">
    

    绑定似乎从未与我的“MyCollectionofSelecteds”属性通信。

    2 回复  |  直到 6 年前
        1
  •  1
  •   mm8    6 年前

    请参考以下示例代码。

    public class MultipleSelectionListBox : ListBox
    {
        public static readonly DependencyProperty BindableSelectedItemsProperty =
            DependencyProperty.Register("BindableSelectedItems",
                typeof(IEnumerable<dynamic>), typeof(MultipleSelectionListBox),
                new FrameworkPropertyMetadata(default(IEnumerable<dynamic>),
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBindableSelectedItemsChanged));
    
        public IEnumerable<dynamic> BindableSelectedItems
        {
            get => (IEnumerable<dynamic>)GetValue(BindableSelectedItemsProperty);
            set => SetValue(BindableSelectedItemsProperty, value);
        }
    
        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);
            BindableSelectedItems = SelectedItems.Cast<dynamic>();
        }
    
        private static void OnBindableSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is MultipleSelectionListBox listBox)
            {
                List<dynamic> newSelection = new List<dynamic>();
                if (!string.IsNullOrWhiteSpace(listBox.SelectedValuePath))
                    foreach (var item in listBox.BindableSelectedItems)
                    {
                        var collectionValue = item.GetType().GetProperty(listBox.SelectedValuePath).GetValue(item, null);
                        foreach (var lbItem in listBox.Items)
                        {
                            if (lbItem.GetType().GetProperty(listBox.SelectedValuePath).GetValue(lbItem, null) == collectionValue)
                                newSelection.Add(lbItem);
                        }
                    }
                else
                    newSelection = listBox.BindableSelectedItems as List<dynamic>;
    
                listBox.SetSelectedItems(listBox.BindableSelectedItems);
            }
        }
    }
    

    视图模型:

    public class ViewModel : INotifyPropertyChanged
    {
        public ViewModel()
        {
            //select 1 and 4 initially:
            MyCollectionOfSelectedIDs = new List<dynamic> { Items[0], Items[3] };
        }
    
        public IList<DeviceChannelInfo> Items { get; } = new List<DeviceChannelInfo>()
        {
            new DeviceChannelInfo{ name = "1", displayName = "1", id =1 },
            new DeviceChannelInfo{ name = "2", displayName = "2", id =2 },
            new DeviceChannelInfo{ name = "3", displayName = "3", id =3 },
            new DeviceChannelInfo{ name = "4", displayName = "4", id =4 }
        };
    
        private IEnumerable<dynamic> _mCollectionOfSelectedIDs;
        public IEnumerable<dynamic> MyCollectionOfSelectedIDs
        {
            get { return _mCollectionOfSelectedIDs; }
            set { _mCollectionOfSelectedIDs = value; NotifyPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp4"
            mc:Ignorable="d"
            Title="MainWindow" Height="300" Width="300">
        <Window.DataContext>
            <local:ViewModel />
        </Window.DataContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <local:MultipleSelectionListBox ItemsSource="{Binding Items}" 
                                            SelectionMode="Extended"
                                            DisplayMemberPath="displayName"
                                            SelectedValuePath="name" 
                                            BindableSelectedItems="{Binding MyCollectionOfSelectedIDs}" />
    
            <TextBlock Grid.Row="1" Text="{Binding MyCollectionOfSelectedIDs.Count}" />
        </Grid>
    </Window>
    
        2
  •  1
  •   Wobbles    6 年前

    我打碎了这个鸡蛋。

    对我来说,最重要的是避免使用行为或代码,我相信我已经完成了这项工作(可能需要一些测试,但到目前为止仍在工作)

    public class MultipleSelectionListBox : ListBox
    {
        internal bool processSelectionChanges = false;
    
        public static readonly DependencyProperty BindableSelectedItemsProperty =
            DependencyProperty.Register("BindableSelectedItems",
                typeof(object), typeof(MultipleSelectionListBox),
                new FrameworkPropertyMetadata(default(ICollection<object>),
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBindableSelectedItemsChanged));
    
        public dynamic BindableSelectedItems
        {
            get => GetValue(BindableSelectedItemsProperty);
            set => SetValue(BindableSelectedItemsProperty, value);
        }
    
    
        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);
    
            if (BindableSelectedItems == null || !this.IsInitialized) return; //Handle pre initilized calls
    
            if (e.AddedItems.Count > 0)
                if (!string.IsNullOrWhiteSpace(SelectedValuePath))
                {
                    foreach (var item in e.AddedItems)
                        if (!BindableSelectedItems.Contains((dynamic)item.GetType().GetProperty(SelectedValuePath).GetValue(item, null)))
                            BindableSelectedItems.Add((dynamic)item.GetType().GetProperty(SelectedValuePath).GetValue(item, null));
                }
                else
                {
                    foreach (var item in e.AddedItems)
                        if (!BindableSelectedItems.Contains((dynamic)item))
                            BindableSelectedItems.Add((dynamic)item);
                }
    
            if (e.RemovedItems.Count > 0)
                if (!string.IsNullOrWhiteSpace(SelectedValuePath))
                {
                    foreach (var item in e.RemovedItems)
                        if (BindableSelectedItems.Contains((dynamic)item.GetType().GetProperty(SelectedValuePath).GetValue(item, null)))
                            BindableSelectedItems.Remove((dynamic)item.GetType().GetProperty(SelectedValuePath).GetValue(item, null));
                }
                else
                {
                    foreach (var item in e.RemovedItems)
                        if (BindableSelectedItems.Contains((dynamic)item))
                            BindableSelectedItems.Remove((dynamic)item);
                }
        }
    
        private static void OnBindableSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is MultipleSelectionListBox listBox)
            {
                List<dynamic> newSelection = new List<dynamic>();
                if (!string.IsNullOrWhiteSpace(listBox.SelectedValuePath))
                    foreach (var item in listBox.BindableSelectedItems)
                    {
                        foreach (var lbItem in listBox.Items)
                        {
                            var lbItemValue = lbItem.GetType().GetProperty(listBox.SelectedValuePath).GetValue(lbItem, null);
                            if ((dynamic)lbItemValue == (dynamic)item)
                                newSelection.Add(lbItem);
                        }
                    }
                else
                    newSelection = listBox.BindableSelectedItems as List<dynamic>;
    
                listBox.SetSelectedItems(newSelection);
            }
        }
    }