手动提升/触发列表框SelectionChangedEvent

时间:2014-05-28 09:44:29

标签: c# wpf mvvm listbox selectionchanged

我想在我的 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);
        }

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

提前致谢。

修改

我找到了另一个解决方案(实际上更多的是黑客攻击)而不是@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
    }

2 个答案:

答案 0 :(得分:1)

请参阅this

如果将ItemsSource设置为集合,并且集合实现INPC中的对象,则可以设置Style上的ListViewItem以使用绑定对象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"

并记得添加对Interactivity assembly的引用。

答案 1 :(得分:0)

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

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

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