简体   繁体   English

您可以通过列表在DataGrid上设置SelectedItems

[英]Can you set SelectedItems on a DataGrid via a list

I have this code sitting in a button's attached TargetedTriggerAction<DataGrid> 我将此代码放在按钮附加的TargetedTriggerAction <DataGrid>中

What I'm trying to do is find the items that I'd like selected and place them into a list. 我要做的是找到我想要的项目并将它们放入列表中。 Once that is complete I'd like to set the DataGrid's SelectedItems with the list. 一旦完成,我想用列表设置DataGrid的SelectedItems。

I would like to do it this way, as I have many thousands of items to iterate through, doing a majority on a background thread and setting SelectedItems at the end, avoiding the UI thread with singular calls to SelectedItems.Add() will be more efficient. 我想这样做,因为我有成千上万的项目要迭代,在后台线程上做多数并在最后设置SelectedItems,避免使用对SelectedItems.Add()的单一调用的UI线程将更多高效。 That is my logic at this point anyway. 无论如何,这是我的逻辑。

I realise that SelecteedItems is readonly so my question is can I actually do this? 我意识到SelecteedItems是只读的所以我的问题是我能真正做到这一点吗? How can I set SelectedItems via a list? 如何通过列表设置SelectedItems? On another note if the SelectAll button can select everything in a short period of time then there must be a way achieve what I want also... shouldn't there? 另一方面,如果SelectAll按钮可以在短时间内选择所有内容,那么必须有一种方法可以实现我想要的......不应该在那里吗?

IList<Object> tempItems = new List<Object>();
var itemsSource = this.Target.Items as IEnumerable;
Task.Factory.StartNew(() =>
{
    FullTextSearch<UserViewModel>.FullTextSearchInit();

    if (itemsSource != null)
    {
        foreach (var item in itemsSource)
        {
            if (FullTextSearch<UserViewModel>.Match((UserViewModel)item, sv))
            {
                tempItems.Add(item);
            }
        }
        if (tempItems.Count > 0)
        {
            Application.Current.Dispatcher.Invoke(new Action(() =>
                /**** How to set? ****/
                this.Target.SelectedItems = tempList
                ));
        }
    }
});

[EDIT] Tried doing the following, however, IsUpdatingSelectedItems, BeginUpdateSelectedItems and EndUpdateSelectedItems all come back with similar errors: [编辑]尝试执行以下操作,但是,IsUpdatingSelectedItems,BeginUpdateSelectedItems和EndUpdateSelectedItems都返回了类似的错误:

System.Windows.Controls.Primitives.MultiSelector.BeginUpdateSelectedItems()' is inaccessible due to its protection level 由于其保护级别,System.Windows.Controls.Primitives.MultiSelector.BeginUpdateSelectedItems()'无法访问

if (!this.Target.IsUpdatingSelectedItems)
{
    this.Target.BeginUpdateSelectedItems();
    foreach (object item in this.Target.Items)
    {
        if (FullTextSearch<UserViewModel>.Match((UserViewModel)item, sv))
        {
            this.Target.SelectedItems.Add(item);
        }
    }
    this.Target.EndUpdateSelectedItems();
}

In the comments as well as in the edited question, it has been asked how to invoke the protected BeginUpdateSelectedItems and EndUpdateSelectedItems methods of a DataGrid. 在评论和编辑的问题中,已经询问如何调用DataGrid的受保护的BeginUpdateSelectedItemsEndUpdateSelectedItems方法。

The simplest approach would be subclassing DataGrid and implement a method which can invoke those protected methods: 最简单的方法是继承DataGrid并实现一个可以调用这些受保护方法的方法:

    public class MyDataGrid : DataGrid
    {
        public void SelectManyItems(IEnumerable itemsToBeSelected)
        {
            if (!IsUpdatingSelectedItems)
            {
                BeginUpdateSelectedItems();
                foreach (object item in itemsToBeSelected)
                    SelectedItems.Add(item);
                EndUpdateSelectedItems.Invoke();
            }
        }
    }

While this looks nice and easy, it has the disadvantage of requiring you to replace the DataGrid with MyDataGrid wherever you need this functionality. 虽然这看起来很简单,但它的缺点是需要您在需要此功能的任何地方使用MyDataGrid替换DataGrid When dealing with an existing or 3rd-party code base, this approach quickly becomes unfeasible. 在处理现有或第三方代码库时,这种方法很快变得不可行。

Another, more versatile approach is in leveraging reflection to call the protected methods from "outside" of the DataGrid object instances. 另一种更通用的方法是利用反射从DataGrid对象实例的“外部”调用受保护的方法。 This does not require sub-classing of DataGrid . 这不需要对DataGrid进行子类化。

public static class MultiSelectorHelper
{
    private static readonly PropertyInfo _piIsUpdatingSelectedItems;
    private static readonly MethodInfo _miBeginUpdateSelectedItems;
    private static readonly MethodInfo _miEndUpdateSelectedItems;

    static MultiSelectorHelper()
    {
        _piIsUpdatingSelectedItems = typeof(MultiSelector).GetProperty("IsUpdatingSelectedItems", BindingFlags.NonPublic | BindingFlags.Instance);
        _miBeginUpdateSelectedItems = typeof(MultiSelector).GetMethod("BeginUpdateSelectedItems", BindingFlags.NonPublic | BindingFlags.Instance);
        _miEndUpdateSelectedItems = typeof(MultiSelector).GetMethod("EndUpdateSelectedItems", BindingFlags.NonPublic | BindingFlags.Instance);
    }


    public static void SelectManyItems(this MultiSelector control, IEnumerable itemsToBeSelected)
    {
        control.Dispatcher.Invoke(
            (Action) (() =>
            {
                if (!(bool) _piIsUpdatingSelectedItems.GetValue(control, null))
                {
                    _miBeginUpdateSelectedItems.Invoke(control, null);
                    try
                    {
                        foreach (object item in itemsToBeSelected)
                            control.SelectedItems.Add(item);
                    }
                    finally
                    {
                        _miEndUpdateSelectedItems.Invoke(control, null);
                    }
                }
            })
        );
    }
}

Note that the SelectManyItems is implemented as an extension method, working on any control which is derived from MultiSelector (which includes DataGrid). 请注意, SelectManyItems是作为扩展方法实现的,处理从MultiSelector (包括DataGrid)派生的任何控件。

Also note the try - finally block, which ensures calling of the EndUpdateSelectedItems method even when an exception occurs during adding of the selected items. 还要注意try - finally块,它确保即使在添加所选项期间发生异常时也要调用EndUpdateSelectedItems方法。

Usage of this extension method is simple: 使用此扩展方法很简单:

IEnumerable collectionWithItemsToSelect = ...
dataGridInstance.SelectManyItems(collectionWithItemsToSelect);

You can get Selecteditems in SelectionChanged() 你可以在SelectionChanged()获得Selecteditems

XAML: XAML:

    <Window x:Class="ListViewSelectedItemsBinding.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:local="clr-namespace:ListViewSelectedItemsBinding"

xmlns:w="clr-namespace:System.Windows.Workarounds"

x:Name="root" Width="500" Height="700">

<Window.Resources>

<local:MyConverter x:Key="myConverter"/>

</Window.Resources>

<StackPanel>

<ListView x:Name="listView" ItemsSource="{Binding}" SelectionMode="Extended"

SelectionChanged="listView_SelectionChanged"

w:ListView.HasBindableSelectedItems="True">

<ListView.View>

<GridView>

<GridViewColumn Header="Artist" DisplayMemberBinding="{Binding Path=Artist}" />

<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Path=Title}" />

<GridViewColumn Header="Genre" DisplayMemberBinding="{Binding Path=Genre}" />

</GridView>

</ListView.View>

</ListView>

<StackPanel Background="LightGreen">

<DockPanel>

<Label DockPanel.Dock="Left" Content="SelectedItems.Count = " />

<TextBox Text="{Binding ElementName=listView, Path=SelectedItems.Count, Mode=OneWay}" IsReadOnly="True" />

</DockPanel>

<DockPanel>

<Label DockPanel.Dock="Left" Content="SelectedItems = " />

<TextBox Text="{Binding ElementName=listView, Path=SelectedItems, Mode=OneWay,

Converter={StaticResource myConverter}, ConverterParameter='Artist'}" IsReadOnly="True" />

</DockPanel>

</StackPanel>

<StackPanel Background="LightBlue">

<DockPanel>

<Label DockPanel.Dock="Left" Content="Selection.Count = " />

<TextBox Text="{Binding ElementName=root, Path=Selection.Count, Mode=OneWay}" IsReadOnly="True" />

</DockPanel>

<DockPanel>

<Label DockPanel.Dock="Left" Content="Selection = " />

<TextBox Text="{Binding ElementName=root, Path=Selection, Mode=OneWay,

Converter={StaticResource myConverter}, ConverterParameter='Artist'}" IsReadOnly="True" />

</DockPanel>

</StackPanel>

<local:PropertiesPanel x:Name="propertiesPanel1" Background="LightGreen"

Subjects="{Binding ElementName=listView, Path=SelectedItems, Mode=OneWay}" />

<local:PropertiesPanel x:Name="propertiesPanel2" Background="LightBlue"

Subjects="{Binding ElementName=root, Path=Selection, Mode=OneWay}" />

<local:PropertiesPanel x:Name="propertiesPanel3" Background="LightPink"

Subjects="{Binding ElementName=listView, Path=BindableSelectedItems}" />

</StackPanel>

</Window>

background: 背景:

    using System;

using System.Collections;

using System.Windows.Data;

using System.Windows.Controls;



namespace System.Windows.Workarounds

{

public static class ListView

{

    public static readonly DependencyProperty HasBindableSelectedItemsProperty;

    public static readonly DependencyProperty BindableSelectedItemsProperty;

    static DependencyProperty SelectionChangedHandlerProperty;



    static ListView()

    {

    BindableSelectedItemsProperty = DependencyProperty.Register("BindableSelectedItems", typeof(IList),typeof(System.Windows.Controls.ListView));

    HasBindableSelectedItemsProperty = DependencyProperty.RegisterAttached("HasBindableSelectedItems", typeof(bool),typeof(System.Windows.Controls.ListView), new PropertyMetadata(false));

    SelectionChangedHandlerProperty = DependencyProperty.RegisterAttached("SelectionChangedHandler", typeof(SelectionChangedHandler),typeof(System.Windows.Controls.ListView));

    }



    public static void SetHasBindableSelectedItems(System.Windows.Controls.ListView source, bool value)
    {
    SelectionChangedHandler Handler = (SelectionChangedHandler)source.GetValue(SelectionChangedHandlerProperty);

    if (value && Handler == null)
    {
        Handler = new SelectionChangedHandler(source);
        source.SetValue(SelectionChangedHandlerProperty, Handler);
    } 
    else if (!value && Handler != null)
    {
        source.ClearValue(SelectionChangedHandlerProperty);
    }
}

}



internal class SelectionChangedHandler
{

    Binding Binding;
    internal SelectionChangedHandler(System.Windows.Controls.ListView owner)
    {
        Binding = new Binding("SelectedItems");
        Binding.Source = owner;
        owner.SetBinding(ListView.BindableSelectedItemsProperty, Binding);
        owner.SelectionChanged +=new SelectionChangedEventHandler(Owner_SelectionChanged);
    }

    void Owner_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        System.Windows.Controls.ListView Owner =(System.Windows.Controls.ListView)sender;
        BindingOperations.ClearBinding(Owner, ListView.BindableSelectedItemsProperty);
        Owner.SetBinding(ListView.BindableSelectedItemsProperty, Binding);
    }
}
}

you can also make a dependencyproperty to replace 'selecteditems' 你也可以创建一个dependencyproperty来替换'selecteditems'

http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM