首页 > 解决方案 > 具有可绑定选定项的多选列表框 DP 未绑定回源

问题描述

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

我为 ListBox 改编的代码如下:

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);
        }
    }
}

其余的你可能会假设,但无论如何我都会阻止基础知识。

我在 LB 中的物品:

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}">

绑定似乎从未与我的“MyCollectionOfSelectedIDs”道具通信。

标签: c#wpfdata-binding

解决方案


我打碎了这个鸡蛋。

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

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);
        }
    }
}

我最初是在交换保存所选项目的属性值,这破坏了我的更高级别的绑定,但是这个控件直接修改了集合,使所有引用保持不变。我尚未完全测试两种方式绑定,但它会在初始加载时加载正确的值,并在列表框中进行编辑时引发所有正确的集合更改事件。


推荐阅读