2009-01-05 43 views
17

我一直在試圖看看是否有一個簡單/巧妙的方式來實現綁定到ListBox.SelectedItems。如果你已經嘗試過自己,你會知道,使用BindingExtension的標記綁定將不起作用 - 該屬性不支持它。因此,您需要爲SelectionChanged配置一個處理程序並嘗試該路線。我已經得到的最接近的是這篇文章:ListBox.SelectedItems的雙向手動綁定實現?

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

更新:上面提到的博客不再可用,筆者目前的博客is here,我能找到所引用的博客文章最接近的是this StackOverflow answer

它實現了所有必要的C#在一個方便的附加屬性。但它實現了「綁定」作爲單向,目標到源。我想要雙向綁定。

任何想法?

回答

41

我找到了一個優雅的解決方案,我剛剛找到時間寫一個blog post about it

我所做的是創建一個附加屬性SynchronizedSelectedItems,您可以在ListBox(或DataGrid上)上設置它。將這個數據綁定到一個集合,然後,用一些魔法,ListBox上的SelectedItems屬性和你的集合保持同步。你可以從我的博客文章下載所有的代碼。

「magic」是一個類,它偵聽任一集合中的CollectionChanged事件,並將更改傳播給另一個集合。

+0

謝謝,真正幫助。 – Echilon 2009-06-01 20:02:45

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