使用DataGrid WPF的复选框

时间:2013-06-14 18:01:33

标签: wpf mvvm wpf-controls wpfdatagrid wpf-4.0

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

所需功能 -

  1. Muti - 使用复选框(单击)
  2. 选择行
  3. 选中全部复选框以检查datagrid中的所有复选框
  4. 像这样 -

    enter image description here

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

    我现在需要一个工作范例。

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

    N请不要告诉我google这件事,因为没有任何事情能帮助我......

    更新 -

    1. 我正在使用列的自动生成
    2. 我不想在我的模型中添加“IsSelected”或任何此类属性..
    3. 我只是面临两个问题 -
    4. 首先,“选择所有”功能,即选中复选框上的所有复选框,单击列标题中存在的复选框...(我可以选择和取消选择数据网格但不能勾选/取消勾选复选框)< / p>

      其次,鼠标点击多次选择而不按住Ctrl键..

3 个答案:

答案 0 :(得分:19)

当您使用MVVM时,您必须了解什么是数据以及什么是严格的UI。

您的SelectedItems是您的数据的一部分,还是只是您的用户界面?

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

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

<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 that I have on my blog来找到DataGridRow。您可以使用相同的代码,也可以创建自己的方法来搜索VisualTree

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

有多种方法可以在不同级别禁用选择,但为了简化生活我只是禁用DataGrid.PreviewMouseLeftButtonDown事件,如果用户没有点击CheckBox。< / p>

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以外的任何位置时取消该事件。

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

如果它是用户界面的一部分,只需向CheckBox添加DataGridTemplateColumn.HeaderTemplate,点击它后,循环浏览DataGrid.Rows,找到第一个CheckBox列,并选中或取消选中它。

如果它是数据的一部分,您仍然可以执行相同的操作(仅在DataGrid.Items中设置绑定值而不是CheckBox.IsChecked中的DataGrid.Rows,或者您可以执行此操作作为Adolfo Perez suggested,并将其绑定到ViewModel上的属性。

答案 1 :(得分: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);
        }
    }

最后是样本Model类:

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);
        }
    }
}

上面的大多数代码都应该是不言自明的,但如果您有任何疑问,请告诉我

答案 2 :(得分: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;}
    }