【问题标题】:Checkbox with DataGrid WPF带有 DataGrid WPF 的复选框
【发布时间】:2013-06-11 11:08:03
【问题描述】:

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

所需功能 -

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

类似的东西-

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

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

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

请不要告诉我用谷歌搜索这件事,因为我没有任何事情可以解决......

更新 -

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

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

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

【问题讨论】:

  • 哪一部分给您带来的问题最多?将CheckBox 放在标题中?让它勾选/取消勾选所有项目框?
  • 第一个谷歌点击:here。哥们儿,认真的。我倾向于在 Stackoverflow 中提供完整的工作示例,但你的兴趣太明显了,我不打算回答这个问题。
  • HighCore - 这我可以在 5 分钟内完成,而无需谷歌搜索! 1.我正在使用列的自动生成 2.我不想在我的模型中添加“IsSelected”或任何此类属性.. 3.我只是面临两个问题首先,“全选”功能,即选中所有复选框列标题中存在的复选框单击...其次,在不按住 Ctrl 键的情况下单击鼠标进行多项选择..

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


【解决方案1】:

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

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

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

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

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

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

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

首先,我刚刚使用DataGridTemplateColumn 将未绑定的CheckBox 列添加到列列表中。这将被添加到 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>

其次,我在CheckBox 中添加了一个PreviewMouseDown 事件,以使其设置行的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 以找到与单击的CheckBox 关联的DataGridRow 以选择它,并且为了让生活更轻松,我正在使用一些自定义VisualTreeHelpers that I have on my blog 来查找DataGridRow。您可以使用相同的代码,也可以创建自己的方法来搜索VisualTree

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

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

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

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

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

如果它是数据的一部分,你仍然可以做同样的事情(只在DataGrid.Items 中设置绑定值,而不是DataGrid.Rows 中的CheckBox.IsChecked),或者你可以像Adolfo Perez suggested 那样做,并且将其绑定到ViewModel 上的属性。

【讨论】:

  • awesome rachel.. 真的很棒.. 稍微调整了 d 代码以优化 d 工作...否则它真的很整洁...
  • 我已经为此苦苦挣扎了很长一段时间,因为我们以编程方式创建了 DataGridTemplateColumns。最后,您的 PreviewMouseLeftButtonDown 处理程序的实现似乎可以正常工作,像这样附加到 CheckBox:checkBoxFactory.AddHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(ChkBox_PreviewMouseLeftButtonDown));
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    您的视图可能类似于

        <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>
    

    在视图模型中 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;}
        }
    

    【讨论】:

      猜你喜欢
      • 2014-10-14
      • 2017-04-24
      • 2016-01-13
      • 2018-08-03
      • 2018-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多