WPF DataGrid 多选绑定

发布于 2024-08-28 13:49:18 字数 94 浏览 5 评论 0原文

我有一个启用多选的数据网格。我需要更改视图模型中的选择。但是,SelectedItems 属性是只读的,不能直接绑定到视图模型中的属性。那么我如何向视图发出选择已更改的信号?

I have a datagrid that is multi-select enabled. I need to change the selection in the viewmodel. However, the SelectedItems property is read only and can't be directly bound to a property in the viewmodel. So how do I signal to the view that the selection has changed?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

内心激荡 2024-09-04 13:49:18

安迪是对的。 DataGridRow.IsSelected 是一个依赖属性,可以通过数据绑定来控制 ViewModel 中的选择。以下示例代码演示了这一点:

<Window x:Class="DataGridMultiSelectSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:tk="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <tk:DataGrid AutoGenerateColumns="False" ItemsSource="{Binding}" EnableRowVirtualization="False">
            <tk:DataGrid.Columns>
                <tk:DataGridTextColumn Header="Value" Binding="{Binding Value}" />
            </tk:DataGrid.Columns>
            <tk:DataGrid.RowStyle>
                <Style TargetType="tk:DataGridRow">
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </tk:DataGrid.RowStyle>
        </tk:DataGrid>
        <Button Content="Select Even" Click="Even_Click" />
        <Button Content="Select Odd" Click="Odd_Click" />
    </StackPanel>
</Window>

using System.ComponentModel;
using System.Windows;

namespace DataGridMultiSelectSample
{
    public partial class Window1
    {
        public Window1()
        {
            InitializeComponent();
            DataContext = new[]
                              {
                                  new MyViewModel {Value = "Able"},
                                  new MyViewModel {Value = "Baker"},
                                  new MyViewModel {Value = "Charlie"},
                                  new MyViewModel {Value = "Dog"},
                                  new MyViewModel {Value = "Fox"},
                              };
        }

        private void Even_Click(object sender, RoutedEventArgs e)
        {
            var array = (MyViewModel[]) DataContext;
            for (int i = 0; i < array.Length; ++i)
                array[i].IsSelected = i%2 == 0;
        }

        private void Odd_Click(object sender, RoutedEventArgs e)
        {
            var array = (MyViewModel[])DataContext;
            for (int i = 0; i < array.Length; ++i)
                array[i].IsSelected = i % 2 == 1;
        }
    }

    public class MyViewModel : INotifyPropertyChanged
    {
        public string Value { get; set; }

        private bool mIsSelected;
        public bool IsSelected
        {
            get { return mIsSelected; }
            set
            {
                if (mIsSelected == value) return;
                mIsSelected = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

请务必设置 DataGrid 元素上的 EnableRowVirtualization="False",否则存在 IsSelected 绑定不正常的风险。

Andy is correct. DataGridRow.IsSelected is a Dependency Property that can be databound to control selection from the ViewModel. The following sample code demonstrates this:

<Window x:Class="DataGridMultiSelectSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:tk="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <tk:DataGrid AutoGenerateColumns="False" ItemsSource="{Binding}" EnableRowVirtualization="False">
            <tk:DataGrid.Columns>
                <tk:DataGridTextColumn Header="Value" Binding="{Binding Value}" />
            </tk:DataGrid.Columns>
            <tk:DataGrid.RowStyle>
                <Style TargetType="tk:DataGridRow">
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </tk:DataGrid.RowStyle>
        </tk:DataGrid>
        <Button Content="Select Even" Click="Even_Click" />
        <Button Content="Select Odd" Click="Odd_Click" />
    </StackPanel>
</Window>

using System.ComponentModel;
using System.Windows;

namespace DataGridMultiSelectSample
{
    public partial class Window1
    {
        public Window1()
        {
            InitializeComponent();
            DataContext = new[]
                              {
                                  new MyViewModel {Value = "Able"},
                                  new MyViewModel {Value = "Baker"},
                                  new MyViewModel {Value = "Charlie"},
                                  new MyViewModel {Value = "Dog"},
                                  new MyViewModel {Value = "Fox"},
                              };
        }

        private void Even_Click(object sender, RoutedEventArgs e)
        {
            var array = (MyViewModel[]) DataContext;
            for (int i = 0; i < array.Length; ++i)
                array[i].IsSelected = i%2 == 0;
        }

        private void Odd_Click(object sender, RoutedEventArgs e)
        {
            var array = (MyViewModel[])DataContext;
            for (int i = 0; i < array.Length; ++i)
                array[i].IsSelected = i % 2 == 1;
        }
    }

    public class MyViewModel : INotifyPropertyChanged
    {
        public string Value { get; set; }

        private bool mIsSelected;
        public bool IsSelected
        {
            get { return mIsSelected; }
            set
            {
                if (mIsSelected == value) return;
                mIsSelected = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Be sure to set EnableRowVirtualisation="False" on the DataGrid element, else there's a risk that the IsSelected bindings fall out of kilter.

紧拥背影 2024-09-04 13:49:18

我没有太多地使用 DataGrid,但适用于 ListView 的一种技术是绑定到单个 IsSelected 属性。代码>ListViewItem。只需将列表中的每个对象设置为 true,然后它就会被选中。

也许表示 DataGrid 中的行的对象也有一个 IsSelected 属性,并且也可以以这种方式使用?

I haven't worked with the DataGrid much, but one technique that works for the ListView is to bind to the IsSelected property of the individual ListViewItem. Just set this to true for each object in your list, and then it will get selected.

Maybe the object that represents a row in the DataGrid also has an IsSelected property, and can be used in this way as well?

薄暮涼年 2024-09-04 13:49:18

伙计们,感谢您的帮助。我的问题解决了。我认为这个问题对于新的 WPF 开发人员来说很常见,因此我将在这里重申我的问题以及更详细的解决方案,以防其他人遇到同样的问题。

问题:我有一个启用多选的音频文件数据网格。网格有多个列标题。用户可以多选几行。当他单击“播放”按钮时,音频文件将按照列标题之一(例如 A 列)的顺序播放。播放开始时,多选将被清除,仅突出显示当前播放的文件。当所有文件播放完毕后,将重新显示多选。播放是在视图模型中完成的。正如您所看到的,这里有两个问题:1)如何从视图模型中选择当前正在播放的文件,2)如何从视图模型中向视图发出播放已完成的信号并重新显示多选。

解决方案:为了解决第一个问题,我在视图模型中创建了一个属性,该属性绑定到视图的 SelectedIndex 属性来选择当前播放的文件。为了解决第二个问题,我在视图模型中创建了一个布尔属性来指示播放已完成。在视图的代码后面,我订阅了布尔属性的 PropertyChanged 事件。在事件处理程序中,视图的 SelectedItems 属性是从保存的多重选择中重新创建的(SelectedItems 的内容已保存到列表中,并且在播放开始时已清除 SelectedItems)。起初,我在重新创建 SelectedItems 时遇到了麻烦。事实证明,问题是由于重新创建是通过第二个线程启动的。 WPF 不允许这样做。解决方案是使用 Dispatcher.Invoke() 让主线程完成工作。对于经验丰富的开发人员来说,这可能是一个非常简单的问题,但对于新手来说,这是一个小挑战。不管怎样,得到了不同人的很多帮助。

Guys, thanks for the help. My problem was solved. I think the problem is pretty common for new WPF developers, so I will restate my problem and as well as the solution in more details here just in case someone else runs into the same kind of problems.

The problem: I have a multi-select enabled datagrid of audio files. The grid has multiple column headers. The user can multi-select several row. When he clicks the Play button, the audio files will be played in the order of one the columns headers (say column A). When playback starts, the multi-select is cleared and only the currently playing file is highlighted. When playback is finished for all files, the multi-selection will be re-displayed. The playback is done in the viewmodel. As you can see, there are two problems here: 1) how to select the currently playing file from the viewmodel, and 2) how to signal to the view from the viewmodel that playback is finished and re-display the multi-selection.

The solution: To solve the first problem, I created a property in the viewmodel that is bound to the view's SelectedIndex property to select the currently playing file. To solve the second problem, I created a boolean property in the view model to indicate playback is finished. In the view's code behind, I subscribed the the boolean property's PropertyChanged event. In the event handler, the view's SelectedItems property is re-created from the saved multi-selection (the contents of SelectedItems was saved into a list and SelectedItems was cleared when playback started). At first, I had trouble re-creating SelectedItems. It turned out the problem was due to the fact that re-creation was initiated through a second thread. WPF does not allow that. The solution to this is to use the Dispatcher.Invoke() to let the main thread do the work. This may be a very simple problem for experienced developers, but for newbies, it's a small challenge. Anyway, a lot of help from different people.

江挽川 2024-09-04 13:49:18

只需在任何 MultiSelector 派生类上使用 SelectedItems,并在它返回的 IList 上使用方法 Add、Remove、Clear 即可。

Just use SelectedItems on any MultiSelector derived class , and use methods Add, Remove, Clear on IList it returns .

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文