ListBox.SelectedItems的双向手动绑定实现方法?

17

我一直在尝试找到一种简单/聪明的方法来实现对ListBox.SelectedItems的绑定。如果你自己尝试过,你会知道,使用BindingExtension的标记绑定不起作用--该属性不支持它。因此,你只能通过为SelectionChanged连接一个处理程序并尝试该路线。我最接近的是这篇文章:

http://alexshed.spaces.live.com/blog/cns!71C72270309CE838!149.entry

更新:上述提到的博客已经不再可用,该作者的当前博客在这里,我能找到的跟所引用博客帖子最接近的是这个StackOverflow回答

它通过一个方便的附加属性实现了所有必要的C#代码。但它将“绑定”实现为单向的,从目标到源。我想要双向绑定。

有什么想法吗?

2个回答

41

我找到了一个优雅的解决方案,现在我有时间写一篇关于它的博客文章

我的做法是创建一个附加属性"SynchronizedSelectedItems",你可以将其设置在ListBox(或者事实上DataGrid)上。然后将其与一个集合进行数据绑定,稍微施一点魔法,ListBox的SelectedItems属性和你的集合就会保持同步。你可以从我的博客文章中下载代码。

这个"魔法"是一个类,它监听两个集合中任意一个的CollectionChanged事件,并将更改传播到另一个集合中。


1
我尝试将您的源代码应用到Silverlight中,但它无法工作。您有任何在Silverlight中的示例吗? - Anonymous
无法下载示例。您能否在博客文章中分享扩展行为代码或在其他地方分享代码? - Simsons

0
我一直在寻找解决方案,但提出的方案似乎过于复杂。因此,这里提供了一种新的双向绑定解决方案,仅限于附加属性,并使用弱事件处理来监视定义的依赖属性中的更改。我没有花费太多时间来使其完美无缺,但它确实有效。
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;
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接