Реализация двунаправленного ручного ввода для ListBox.SelectedItems?

17

Я пытался выяснить, есть ли простой / умный способ реализовать привязку к ListBox.SelectedItems. Если вы сами пробовали, вы узнаете, что привязка разметки с использованием BindingExtension не будет работать - свойство не поддерживает ее. Таким образом, вам остается подключить обработчик для SelectionChanged и попробовать этот маршрут. Самое близкое, что я получил, это сообщение:

Ссылка

Обновление: вышеупомянутый блог больше не доступен, текущий блог автора здесь и самый близкий, который я мог найти в упомянутом сообщении в блоге, этот ответ в StackOverflow .

, который реализует все необходимые C # в удобном прикрепленном свойстве. Но он реализует «привязку» как одностороннюю, целевую к источнику. Я бы хотел, чтобы двусторонняя привязка.

Любые идеи?

    
задан robcecil 05.01.2009 в 19:41
источник

2 ответа

41

Я нашел элегантное решение, и я нашел время, чтобы написать в блоге об этом .

Я сделал, чтобы создать прикрепленное свойство SynchronizedSelectedItems, которое вы можете установить в ListBox (или DataGrid на самом деле). Вы привязываете это к коллекции, а затем, с небольшим количеством магии, свойство SelectedItems в ListBox и вашей коллекции синхронизируется. Вы можете скачать весь код из моего сообщения в блоге.

«magic» - это класс, который прослушивает события CollectionChanged в любой коллекции и передает изменения другим.

    
ответ дан Samuel Jack 12.02.2009 в 13:47
источник
  • Спасибо, это действительно помогло. –  Echilon 01.06.2009 в 22:02
  • просто отлично - действительно хороший материал –  headsling 27.04.2010 в 14:18
  • Я пытаюсь принять ваш код souce для Silverlight, но он не работает. Есть ли у вас пример в Silverlight? –  Anonymous 23.11.2010 в 05:21
  • +1 Спасибо за добавление полезного ресурса в сообщество. –  JDB 08.10.2012 в 22:03
  • Не удалось загрузить образец. Не могли бы вы поделиться расширенным кодом поведения в блоге или поделиться кодом где-нибудь еще. –  Simsons 18.03.2013 в 12:19
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;
            }
        }
    }
}
    
ответ дан user4013241 05.09.2014 в 23:11
источник