【问题标题】:Raise/Fire Listbox SelectionChangedEvent manually手动提高/触发列表框 SelectionChangedEvent
【发布时间】:2014-07-17 11:16:34
【问题描述】:

我想在我的 ListBox 中以编程方式选择多个项目。因此,为了尽可能对 mvvm 友好,我创建了一个继承自 ListBox 的自定义控件。在这个自定义控件中,我创建了一个依赖属性,允许更改项目选择。这是 OnPropertyChanged 部分的代码:

private static void OnSetSelectionToPropertyChanged
            (DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            InitializableListBox list = d as InitializableListBox;
            Dictionary<int, string> toSelect = e.NewValue as Dictionary<int, string>;
            if (toSelect == null)
                return;

            list.SetSelectedItems(toSelect);
        }

选择效果很好,但此解决方案不会引发 OnSelectionChanged 事件

所以我也试试:

private static void OnSetSelectionToPropertyChanged
            (DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            InitializableListBox list = d as InitializableListBox;
            Dictionary<int, string> toSelect = e.NewValue as Dictionary<int, string>;
            if (toSelect == null)
                return;

            SelectionChangedEventArgs e_selChanged;

            List<Object> removed = new List<object>();
            List<Object> added = new List<object>();

            //Clear the SelectedItems list
            while(list.SelectedItems.Count > 0)
            {
                removed.Add(list.SelectedItems[0]);
                list.SelectedItems.RemoveAt(0);
            }

            //Add each selected items
            foreach (var item in toSelect)
            {
                list.SelectedItems.Add(item);
                added.Add(list.SelectedItems[list.SelectedItems.Count - 1]);
            }

            //Raise the SelectionChanged event
            e_selChanged = new SelectionChangedEventArgs(SelectionChangedEvent,removed,added);
            list.OnSelectionChanged(e_selChanged);
        }

但这并没有更好。我认为我处理事件的方式不正确,所以如果你能帮助我,那就太好了。

提前致谢。

编辑

我找到了另一个解决方案(实际上更像是一个 hack)而不是 @NETscape。我不认为它更好,但它似乎工作得很好,而且可能更容易。

诀窍是创建一个依赖属性,允许您访问SelectedItems 属性(在普通ListBox 上是只读且不可绑定的)。这是我的自定义 ListBox 的代码:

public class InitializableListBox : ListBox
    {

        public InitializableListBox()
        {
            SelectionChanged += CustomSelectionChanged;
        }

        private void CustomSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            InitializableListBox s = sender as InitializableListBox;
            if (s == null)
                return;

            s.CustomSelectedItems = s.SelectedItems;
        }

        #region CustomSelectedItems DependyProperty

        public static DependencyProperty CustomSelectedItemsProperty = 
            DependencyProperty.RegisterAttached("CustomSelectedItems",
            typeof(System.Collections.IList),typeof(InitializableListBox),
            new PropertyMetadata(null, OnCustomSelectedItemsPropertyChanged));

        public  System.Collections.IList  CustomSelectedItems 
        {
            get 
            {
                return (System.Collections.IList)GetValue(CustomSelectedItemsProperty); 
            }
            set 
            { 
                SetValue(CustomSelectedItemsProperty, value);                
            }
        }

        private static void OnCustomSelectedItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            InitializableListBox list = d as InitializableListBox;
            System.Collections.IList toSelect = e.NewValue as System.Collections.IList;
            if (toSelect == null)
                return;

            list.SetSelectedItems(toSelect);
        }

        #endregion
    }

【问题讨论】:

  • 您选择的项目是否已绑定到某个属性?如果是这样,请提高 propertychanged。

标签: c# wpf mvvm listbox selectionchanged


【解决方案1】:

this

如果将 ItemsSource 设置为集合,并且集合中的对象实现 INPC,则可以在 ListViewItem 上设置 Style 以使用绑定对象 IsSelected 属性。

请参阅this 回答以了解我的意思。

假设你有ItemsSource="{Binding Items}",那么你可以这样做:

Items.Where(item => item.IsSelected == true);  

返回您选择的项目列表。
您也可以通过Items[0].IsSelected = true; 以编程方式选择一个项目。

简而言之,您不必使用自定义控件在 ListView 上实现多选。

编辑

我在实施 Telerik 的 RadGridView 时遇到了虚拟化问题。我使用了一种行为来解决这个问题,它似乎奏效了:

public class RadGridViewExt : Behavior<RadGridView>
{
    private RadGridView Grid
    {
        get
        {
            return AssociatedObject as RadGridView;
        }
    }

    public INotifyCollectionChanged SelectedItems
    {
        get { return (INotifyCollectionChanged)GetValue(SelectedItemsProperty); }
        set { SetValue(SelectedItemsProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.Register("SelectedItems", typeof(INotifyCollectionChanged), typeof(RadGridViewExt), new PropertyMetadata(OnSelectedItemsPropertyChanged));


    private static void OnSelectedItemsPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
    {
        var collection = args.NewValue as INotifyCollectionChanged;
        if (collection != null)
        {
            collection.CollectionChanged += ((RadGridViewExt)target).ContextSelectedItemsCollectionChanged;
        }
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        Grid.SelectedItems.CollectionChanged += GridSelectedItemsCollectionChanged;
    }

    void ContextSelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        UnsubscribeFromEvents();

        Transfer(SelectedItems as IList, AssociatedObject.SelectedItems);

        SubscribeToEvents();
    }

    void GridSelectedItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        UnsubscribeFromEvents();

        Transfer(AssociatedObject.SelectedItems, SelectedItems as IList);

        SubscribeToEvents();
    }

    private void SubscribeToEvents()
    {
        AssociatedObject.SelectedItems.CollectionChanged += GridSelectedItemsCollectionChanged;

        if (SelectedItems != null)
        {
            SelectedItems.CollectionChanged += ContextSelectedItemsCollectionChanged;
        }
    }

    private void UnsubscribeFromEvents()
    {
        AssociatedObject.SelectedItems.CollectionChanged -= GridSelectedItemsCollectionChanged;

        if (SelectedItems != null)
        {
            SelectedItems.CollectionChanged -= ContextSelectedItemsCollectionChanged;
        }
    }

    public static void Transfer(IList source, IList target)
    {
        if (source == null || target == null)
            return;

        target.Clear();

        foreach (var o in source)
        {
            target.Add(o);
        }
    }
}

RadGridView 控件内部:

<i:Interaction.Behaviors>
    <local:RadGridViewExt SelectedItems="{Binding SelectedItems}" />
</i:Interaction.Behaviors>

在哪里

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

记得添加对交互程序集的引用。

【讨论】:

  • 是的,我知道 IsSelected 属性,但它和 WPF 的虚拟化 ListBox 存在一些问题。这就是我尝试使用自定义控件的原因。
  • 是的,你说得对,我可以这样做,但由于我的 List 可以有很多项目,所以会出现流动性问题,这就是为什么我对这个自定义控件发疯的原因。 -编辑 抱歉,没有看到您的编辑
  • 我找到了另一个解决方案(请参阅我的编辑)。即使有很多项目,它似乎也能很好地工作。我会尝试大量使用它们,看看它是否好,但我看不出为什么它不会。无论如何,非常感谢您的帮助。
【解决方案2】:

ListBox 有一个名为SelectionMode 的属性。您可以将其设置为Multiple 以启用多选。

<ListBox x:Name="MyListBox" SelectionMode="Multiple"/>

之后可以使用SelectedItems属性获取所有选中的项目。

【讨论】:

  • 谢谢,但我已经在SelectionMode="Multiple"。当我尝试以编程方式选择项目时出现问题。
猜你喜欢
  • 1970-01-01
  • 2014-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多