I've been trying to see if there is an easy/clever way to implement binding to ListBox.SelectedItems. If you have tried yourself, you will know, that markup binding using BindingExtension will not work - the property doesn't support it. So you are left with wiring up a handler for SelectionChanged and trying that route. The closest I've gotten is this post:
我一直试图看看是否有一种简单/聪明的方法来实现与ListBox.SelectedItems的绑定。如果您已经尝试过,您将会知道,使用BindingExtension的标记绑定将不起作用 - 该属性不支持它。因此,您需要连接SelectionChanged的处理程序并尝试该路由。我得到的最接近的是这篇文章:
http://alexshed.spaces.live.com/blog/cns!71C72270309CE838!149.entry
http://alexshed.spaces.live.com/blog/cns!71C72270309CE838!149.entry
Update: the above mentioned blog is no longer available, that author's current blog is here and the closest I could find to the referenced blog post is this * answer.
更新:上面提到的博客不再可用,作者的当前博客在这里,我能找到的最接近参考博客文章的是这个*答案。
Which implements all the necessary C# in a handy attached property. But it implements the "binding" as a One-Way, Target to Source. I'd like Two-Way binding.
它在一个方便的附加属性中实现了所有必要的C#。但它实现了“绑定”作为单向,目标来源。我想要双向绑定。
Any ideas?
有任何想法吗?
2 个解决方案
#1
41
I've found an elegant solution, and I've just found time to write a blog post about it.
我找到了一个优雅的解决方案,我刚刚抽出时间写一篇关于它的博客文章。
What I did was to create an attached property, SynchronizedSelectedItems that you can set on the ListBox (or DataGrid in fact). You databind this to a collection, and then, with a bit of magic, the SelectedItems property on the ListBox and your collection are kept in sync. You can download all the code from my blog post.
我所做的是创建一个附加属性,您可以在ListBox(或实际上是DataGrid)上设置的SynchronizedSelectedItems。您将此数据绑定到一个集合,然后,通过一些魔术,ListBox上的SelectedItems属性和您的集合保持同步。您可以从我的博客文章下载所有代码。
The "magic" is a class which listens out for CollectionChanged events in either collection, and propagates the changes to the other.
“magic”是一个类,它侦听任一集合中的CollectionChanged事件,并将更改传播到另一个集合。
#2
0
I was looking for a solution for this, and the proposed seemed overly complex. So, here is a new two way binding solution that is limited to just an attached property, and uses weak event handling for watching changes in the defined dependency property. I didn't spend any time making this bulletproof, but it does work.
我正在寻找一个解决方案,这个提议似乎过于复杂。因此,这是一个新的双向绑定解决方案,仅限于附加属性,并使用弱事件处理来监视已定义的依赖项属性中的更改。我没有花时间制作这种防弹,但确实有效。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication2
{
public class ListBoxHelper
{
private static Dictionary<int, bool> SynchToDPInProcessDictionary = new Dictionary<int, bool>();
private static Dictionary<int, bool> SynchToLBInProcessDictionary = new Dictionary<int, bool>();
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(ListBoxHelper),
new FrameworkPropertyMetadata((IList)null,
new PropertyChangedCallback(OnSelectedItemsChanged)));
public static IList GetSelectedItems(DependencyObject d)
{
return (IList)d.GetValue(SelectedItemsProperty);
}
public static void SetSelectedItems(DependencyObject d, IList value)
{
d.SetValue(SelectedItemsProperty, value);
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var listBox = d as ListBox;
if (listBox == null)
throw new InvalidOperationException("ListBoxHelper should only be used with ListBox or ListBox derived classes (like ListView).");
int hashcode = listBox.GetHashCode();
// Gets set on the initial binding.
if (!SynchToDPInProcessDictionary.ContainsKey(hashcode))
{
SynchToDPInProcessDictionary[hashcode] = false;
SynchToLBInProcessDictionary[hashcode] = false;
var observableCollection = GetSelectedItems(listBox) as INotifyCollectionChanged;
if (observableCollection != null)
{
// Create a weak CollectionChanged event handler on the SelectedItems property
// that synchronizes the collection back to the listbox.
CollectionChangedEventManager.AddHandler(observableCollection,
delegate(object sender, NotifyCollectionChangedEventArgs e2)
{
SyncToLBSelectedItems(GetSelectedItems(d), (ListBox)d);
});
}
}
SynchToDPSelectedItems(listBox);
listBox.SelectionChanged += delegate
{
SynchToDPSelectedItems(listBox);
};
}
private static void SynchToDPSelectedItems(ListBox listBox)
{
int hashcode = listBox.GetHashCode();
if (SynchToLBInProcessDictionary[hashcode]) return;
SynchToDPInProcessDictionary[hashcode] = true;
try
{
IList dpSelectedItems = GetSelectedItems(listBox);
dpSelectedItems.Clear();
if (listBox.SelectedItems != null)
{
foreach (var item in listBox.SelectedItems)
dpSelectedItems.Add(item);
}
}
finally
{
SynchToDPInProcessDictionary[hashcode] = false;
}
}
private static void SyncToLBSelectedItems(IList dpSelectedItems, ListBox listBox)
{
int hashcode = listBox.GetHashCode();
if (SynchToDPInProcessDictionary[hashcode]) return;
SynchToLBInProcessDictionary[hashcode] = true;
try
{
listBox.SelectedItems.Clear();
if (dpSelectedItems != null)
{
foreach (var item in dpSelectedItems)
listBox.SelectedItems.Add(item);
}
}
finally
{
SynchToLBInProcessDictionary[hashcode] = false;
}
}
}
}
#1
41
I've found an elegant solution, and I've just found time to write a blog post about it.
我找到了一个优雅的解决方案,我刚刚抽出时间写一篇关于它的博客文章。
What I did was to create an attached property, SynchronizedSelectedItems that you can set on the ListBox (or DataGrid in fact). You databind this to a collection, and then, with a bit of magic, the SelectedItems property on the ListBox and your collection are kept in sync. You can download all the code from my blog post.
我所做的是创建一个附加属性,您可以在ListBox(或实际上是DataGrid)上设置的SynchronizedSelectedItems。您将此数据绑定到一个集合,然后,通过一些魔术,ListBox上的SelectedItems属性和您的集合保持同步。您可以从我的博客文章下载所有代码。
The "magic" is a class which listens out for CollectionChanged events in either collection, and propagates the changes to the other.
“magic”是一个类,它侦听任一集合中的CollectionChanged事件,并将更改传播到另一个集合。
#2
0
I was looking for a solution for this, and the proposed seemed overly complex. So, here is a new two way binding solution that is limited to just an attached property, and uses weak event handling for watching changes in the defined dependency property. I didn't spend any time making this bulletproof, but it does work.
我正在寻找一个解决方案,这个提议似乎过于复杂。因此,这是一个新的双向绑定解决方案,仅限于附加属性,并使用弱事件处理来监视已定义的依赖项属性中的更改。我没有花时间制作这种防弹,但确实有效。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication2
{
public class ListBoxHelper
{
private static Dictionary<int, bool> SynchToDPInProcessDictionary = new Dictionary<int, bool>();
private static Dictionary<int, bool> SynchToLBInProcessDictionary = new Dictionary<int, bool>();
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(ListBoxHelper),
new FrameworkPropertyMetadata((IList)null,
new PropertyChangedCallback(OnSelectedItemsChanged)));
public static IList GetSelectedItems(DependencyObject d)
{
return (IList)d.GetValue(SelectedItemsProperty);
}
public static void SetSelectedItems(DependencyObject d, IList value)
{
d.SetValue(SelectedItemsProperty, value);
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var listBox = d as ListBox;
if (listBox == null)
throw new InvalidOperationException("ListBoxHelper should only be used with ListBox or ListBox derived classes (like ListView).");
int hashcode = listBox.GetHashCode();
// Gets set on the initial binding.
if (!SynchToDPInProcessDictionary.ContainsKey(hashcode))
{
SynchToDPInProcessDictionary[hashcode] = false;
SynchToLBInProcessDictionary[hashcode] = false;
var observableCollection = GetSelectedItems(listBox) as INotifyCollectionChanged;
if (observableCollection != null)
{
// Create a weak CollectionChanged event handler on the SelectedItems property
// that synchronizes the collection back to the listbox.
CollectionChangedEventManager.AddHandler(observableCollection,
delegate(object sender, NotifyCollectionChangedEventArgs e2)
{
SyncToLBSelectedItems(GetSelectedItems(d), (ListBox)d);
});
}
}
SynchToDPSelectedItems(listBox);
listBox.SelectionChanged += delegate
{
SynchToDPSelectedItems(listBox);
};
}
private static void SynchToDPSelectedItems(ListBox listBox)
{
int hashcode = listBox.GetHashCode();
if (SynchToLBInProcessDictionary[hashcode]) return;
SynchToDPInProcessDictionary[hashcode] = true;
try
{
IList dpSelectedItems = GetSelectedItems(listBox);
dpSelectedItems.Clear();
if (listBox.SelectedItems != null)
{
foreach (var item in listBox.SelectedItems)
dpSelectedItems.Add(item);
}
}
finally
{
SynchToDPInProcessDictionary[hashcode] = false;
}
}
private static void SyncToLBSelectedItems(IList dpSelectedItems, ListBox listBox)
{
int hashcode = listBox.GetHashCode();
if (SynchToDPInProcessDictionary[hashcode]) return;
SynchToLBInProcessDictionary[hashcode] = true;
try
{
listBox.SelectedItems.Clear();
if (dpSelectedItems != null)
{
foreach (var item in dpSelectedItems)
listBox.SelectedItems.Add(item);
}
}
finally
{
SynchToLBInProcessDictionary[hashcode] = false;
}
}
}
}