2009-01-05 10 views
17

He estado tratando de ver si hay una manera fácil/inteligente de implementar el enlace a ListBox.SelectedItems. Si lo ha probado usted mismo, sabrá que el enlace de marcado utilizando BindingExtension no funcionará; la propiedad no lo admite. Así que te queda la tarea de cablear un controlador para SelectionChanged y probar esa ruta. Lo más cerca que he conseguido es este post:TwoWay Implementación de enlace manual para ListBox.SelectedItems?

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

Actualización: el blog mencionado anteriormente ya no está disponible, blog actual de ese autor is here y el más cercano que pude encontrar a la entrada del blog que se hace referencia es this StackOverflow answer.

Que implementa todo el C# necesario en una práctica propiedad adjunta. Pero implementa el "enlace" como unidireccional, objetivo a la fuente. Me gustaría enlazar en ambos sentidos.

¿Alguna idea?

Respuesta

41

He encontrado una solución elegante, y acabo de encontrar el momento para escribir un blog post about it.

Lo que hice fue crear una propiedad adjunta, SynchronizedSelectedItems que puede establecer en el ListBox (o DataGrid de hecho). Usted enlazará esto con una colección, y luego, con un poco de magia, la propiedad SelectedItems en el ListBox y su colección se mantendrán sincronizados. Puedes descargar todo el código de mi publicación en el blog.

La "magia" es una clase que escucha los eventos CollectionChanged en cualquier colección y propaga los cambios a la otra.

+0

Gracias, que realmente ayudaron. – Echilon

+0

simplemente genial - realmente buenas cosas – headsling

+1

Intento adoptar tu código fuente en Silverlight pero no funciona. ¿Tienes algún ejemplo en Silverlight? – Anonymous

0

Estaba buscando una solución para esto, y la propuesta parecía demasiado compleja. Por lo tanto, aquí hay una nueva solución de enlace bidireccional que se limita a solo una propiedad adjunta, y utiliza un manejo de eventos débil para ver los cambios en la propiedad de dependencia definida. No pasé el tiempo haciendo esto a prueba de balas, pero funciona.


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