c# - 具有可绑定选定项的多选列表框 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”道具通信。
解决方案
我打碎了这个鸡蛋。
对我来说最重要的是避免使用行为或代码背后,我相信我已经完成了这一点(一些可能需要测试但到目前为止工作)
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);
}
}
}
我最初是在交换保存所选项目的属性值,这破坏了我的更高级别的绑定,但是这个控件直接修改了集合,使所有引用保持不变。我尚未完全测试两种方式绑定,但它会在初始加载时加载正确的值,并在列表框中进行编辑时引发所有正确的集合更改事件。
推荐阅读
- python - 任何人都知道如何从 tcp 协议更改为 http 协议
- android - Android : 在 JitsiMeetView 之后 CameraX 不打开
- java - 如何在 Angular 侧边栏中添加外部工具提示?
- python - TensorBoard 中的 hp_metric 是什么以及如何摆脱它?
- android - (android) 如何从 DialogFragment 实现多选?
- node.js - 如何将 socket.io 连接到非网站应用程序?
- python - 熊猫在行中获取最大 ID 并添加到空行
- flutter - 如何在从另一个小部件继承的小部件中设置数据?
- javascript - VueJS- 在 v-for 循环中渲染表单,该循环引用最初为空的对象数组
- php - 如何在 laravel 刀片的 foreach 循环中获得第 n 行?