6

我正在尝试使用 MVVM 在 WPF 4.0 中创建一个 DataGrid ...

所需功能 -

  1. Muti - 使用复选框选择行(单击)
  2. 选中所有复选框以选中数据网格中的所有复选框

像这样的东西-

在此处输入图像描述

已经2天了,我无法弄清楚如何有效地解决问题..

一个可行的例子是我现在需要的尽快..

如果有人有一个可行的解决方案与我分享,我将不胜感激......

N请不要告诉我用谷歌搜索这个东西,因为没有任何东西适合我......

更新 -

  1. 我正在使用列的自动生成
  2. 我不想在我的模型中添加“IsSelected”或任何此类属性。
  3. 我只是面临2个问题-

首先,“全选”功能,即选中所有复选框,单击列标题中存在的复选框...(我可以选择和取消选择数据网格,但无法勾选/取消勾选复选框)

其次,在不按住 Ctrl 键的情况下单击鼠标进行多选。

4

3 回答 3

20

当你使用 MVVM 时,你必须知道什么是数据,什么是严格的 UI。

SelectedItems将成为数据的一部分,还是只是您的 UI?

如果它是您数据的一部分,那么您确实应该IsSelected在数据模型上拥有一个属性,即使这意味着扩展数据类以包含一个属性,或者创建一个仅包含andIsSelected的包装类。第一个选项可能是首选,因为您可以保留,它使列绑定更简单。bool IsSelectedobject MyDataItemAutoGenerateColumns="True"

然后,您只需将您的绑定DataGridRow.SelectedItemIsSelected数据项的属性:

<Style TargetType="{x:Type DataGridRow}">
    <Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>

但是,如果您SelectedItems仅用于 UI,或者您在这种情况下由于某种原因破坏了 MVVM 模式,那么您可以创建未绑定CheckBox并使用一些代码来确保CheckBox正确同步到SelectedItem.

我做了一个快速示例应用程序,我的代码如下所示:

首先,我只是CheckBox使用DataGridTemplateColumn. 这将在AutoGenerateColumns列列表之前添加。

<DataGrid x:Name="TestDataGrid" ItemsSource="{Binding Test}" 
          SelectionMode="Extended" CanUserAddRows="False"
          PreviewMouseLeftButtonDown="TestDataGrid_PreviewMouseLeftButtonDown_1">
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox x:Name="TestCheckBox"
                              PreviewMouseLeftButtonDown="CheckBox_PreviewMouseLeftButtonDown" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

其次,我向 中添加了一个PreviewMouseDown事件以CheckBox使其设置IsSelected行的属性。

private void CheckBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    var chk = (CheckBox)sender;
    var row = VisualTreeHelpers.FindAncestor<DataGridRow>(chk);
    var newValue = !chk.IsChecked.GetValueOrDefault();

    row.IsSelected = newValue;
    chk.IsChecked = newValue;

    // Mark event as handled so that the default 
    // DataGridPreviewMouseDown doesn't handle the event
    e.Handled = true;
}

它需要导航VisualTree以找到DataGridRow与单击的关联CheckBox以选择它,并且为了让生活更轻松,我正在使用我博客上的一些自定义 VisualTreeHelpers 来查找DataGridRow. 您可以使用相同的代码,也可以创建自己的方法来搜索VisualTree.

最后,如果用户点击 以外的任何地方CheckBox,我们希望禁用默认DataGrid选择事件。这确保了该IsSelected值仅在您单击 时才会更改CheckBox

有多种方法可以禁用不同级别的选择,但为了简单起见,DataGrid.PreviewMouseLeftButtonDown如果用户没有点击CheckBox.

private void TestDataGrid_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
{
    var chk = VisualTreeHelpers.FindAncestor<CheckBox>((DependencyObject)e.OriginalSource, "TestCheckBox");

    if (chk == null)
        e.Handled = true;
}

我再次使用我的自定义VisualTreeHelpers来导航可视化树并确定是否单击了 CheckBox,如果用户单击了除CheckBox.

至于您添加CheckBoxSelectAllUnselectAll项目的第二个请求,这将再次取决于您的选择是 UI 还是数据的一部分。

如果它是 UI 的一部分,只需在 中添加一个CheckBoxDataGridTemplateColumn.HeaderTemplate然后单击它时,循环浏览,在第一列中DataGrid.Rows找到,然后选中或取消选中它。CheckBox

如果它是数据的一部分,您仍然可以做同样的事情(仅在 中设置绑定值DataGrid.Items而不是CheckBox.IsCheckedfrom DataGrid.Rows),或者您可以按照Adolfo Perez 的建议进行操作,并将其绑定到ViewModel.

于 2013-06-16T15:36:30.947 回答
3

对于 MVVM 解决方案,您可以尝试以下操作:

    <StackPanel>
    <DataGrid ItemsSource="{Binding Path=TestItems}" AutoGenerateColumns="False" Name="MyDataGrid"
              CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridCheckBoxColumn Binding="{Binding IsSelected}" Width="50" >
                <DataGridCheckBoxColumn.HeaderTemplate>
                    <DataTemplate x:Name="dtAllChkBx">
                        <CheckBox Name="cbxAll" Content="All" IsChecked="{Binding Path=DataContext.AllSelected,RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                    </DataTemplate>
                </DataGridCheckBoxColumn.HeaderTemplate>
            </DataGridCheckBoxColumn>
            <DataGridTemplateColumn Header="Name" Width="SizeToCells" IsReadOnly="True">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</StackPanel>

在你的ViewModel

private void PopulateTestItems()
        {
            TestItems = new ObservableCollection<TestItem>();
            for (int i = 0; i < 5; i++)
            {
                TestItem ti = new TestItem();
                ti.Name = "TestItem" + i;
                ti.IsSelected = true;
                TestItems.Add(ti);
            }
        }

        private bool _AllSelected;
        public bool AllSelected
        {
            get { return _AllSelected; }
            set
            {
                _AllSelected = value;
                TestItems.ToList().ForEach(x => x.IsSelected = value);
                NotifyPropertyChanged(m => m.AllSelected);
            }
        }

    private ObservableCollection<TestItem> _TestItems;
    public ObservableCollection<TestItem> TestItems
    {
        get { return _TestItems; }
        set
        {
            _TestItems = value;
            NotifyPropertyChanged(m => m.TestItems);
        }
    }

最后是示例模型类:

public class TestItem : ModelBase<TestItem>
{
    private string _Name;
    public string Name
    {
        get { return _Name; }
        set
        {
            _Name = value;
            NotifyPropertyChanged(m => m.Name);
        }
    }
    private bool _IsSelected;
    public bool IsSelected
    {
        get { return _IsSelected; }
        set
        {
            _IsSelected = value;
            NotifyPropertyChanged(m => m.IsSelected);
        }
    }
}

上面的大部分代码应该是不言自明的,但如果您有任何问题,请告诉我

于 2013-06-14T19:08:17.767 回答
2

您的视图可能类似于

    <DataGrid Name="SomeDataGrid" Grid.Row="0" ItemsSource="{Binding Path=SomeCollection}">
            <DataGrid.Columns>
            <DataGridTemplateColumn>
            <DataGridTemplateColumn.HeaderTemplate>
    <DataTemplate>
        <CheckBox IsChecked="{Binding
            RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
            Path=DataContext.AllItemsAreChecked}" />
    </DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate DataType="{x:Type local:SomeType}">
                            <CheckBox Focusable="False" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  
                                      HorizontalAlignment="Left" VerticalAlignment="Center"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="RandomNumber" Width="160">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate DataType="{x:Type local:SomeType}">
                            <TextBlock Text="{Binding Path=RandomNumber}" TextWrapping="Wrap"  HorizontalAlignment="Left" VerticalAlignment="Center"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Date" Width="160">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate DataType="{x:Type local:SomeType}">
                            <TextBlock Text="{Binding Path=Date}" TextWrapping="Wrap"  HorizontalAlignment="Left" VerticalAlignment="Center"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Time" Width="50">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate DataType="{x:Type local:SomeType}">


                                    <TextBlock Text="{Binding Time}" HorizontalAlignment="Left" VerticalAlignment="Center"/>


                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>                    

            </DataGrid.Columns>
        </DataGrid>

在 viewmodel SomeCollection 绑定属性是一个 observablecollection sometype 包含诸如 IsSelected 、 RandomNumber 、Date 、 Time 之类的属性

例如:

    class ViewModel
    {
      public ObservableCollection<SomeType> SomeCollection{get;set;}
    }

    class SomeType
    {
       public string Date {get;set;}
       public string Time {get;set;}
       public string RandomNumber {get;set;}
       public bool IsSelected {get;set;}
    }
于 2013-06-14T19:08:30.190 回答