【问题标题】:WPF GridView with Multiple Checkbox Columns and Select All Column Header具有多个复选框列并选择所有列标题的 WPF GridView
【发布时间】:2011-08-23 14:00:42
【问题描述】:

我有一个包含多个 GridViewColumns 的 ListView。有几列是复选框。每个列标题也包含一个复选框,目的是选中/取消选中该列中的所有复选框。每行的复选框都绑定到我的视图模型中的属性。我看过几个帖子,其中场景是一列复选框,但这些解决方案都不适合我,因为我有 4 列复选框。我还需要将选择的状态从一次访问持续到下一次访问(可以检查列中的所有、部分或没有复选框)。

这是我的 ListView 的 XAML 的缩写版本:

<ListView ItemsSource="{Binding AveragingParameters}">
  <ListView.View>
    <GridView AllowsColumnReorder="False">

      <GridViewColumn Header="Parameter">
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <DockPanel>
              <TextBlock Text="{Binding Name}" />
            </DockPanel>
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>

      <GridViewColumn>
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <Grid HorizontalAlignment="Stretch">
              <CheckBox  x:Name="chkAvg" IsChecked="{Binding CalcAverage}" />
            </Grid>
          </DataTemplate>
        </GridViewColumn.CellTemplate>
        <Grid>
          <CheckBox x:Name="chkAvgSelectAll" Content="Avg" ToolTip="Select All" />
        </Grid>
      </GridViewColumn>

      <GridViewColumn>
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <Grid HorizontalAlignment="Stretch">
              <CheckBox  x:Name="chkMin" IsChecked="{Binding CalMin}" />
            </Grid>
          </DataTemplate>
        </GridViewColumn.CellTemplate>
        <Grid>
          <CheckBox x:Name="chkMinSelectAll" Content="Min" ToolTip="Select All" />
        </Grid>
      </GridViewColumn>

    </GridView>
  </ListView.View>
</ListView>

我已经尝试在 Checked 属性上使用命令和命令参数、PropertyChanged 事件,并在后面的代码中处理 Checked 事件,但没有任何信息可以让我知道要在绑定的集合中更改什么。

我想我可以为每个单独的事件处理程序创建一个单独的事件处理程序,但如果可能的话,我希望能够在单个事件处理程序中处理它,因为最终我将创建一个在显示的列的术语。

提前致谢

【问题讨论】:

  • 当用户选中或取消选中chkAvgSelectAllchkMinSelectAll 时,您想知道您需要在AveragingParameters 中进行哪些更改吗?
  • 我试图弄清楚如何链接到我的视图模型并确定我需要对我的 AveragingParameters 集合中的每个项目产生影响的布尔属性。 H.B. 下面的帖子可能就是门票。
  • 我不确定我是否理解问题所在。如果您将每一列绑定到 ModelView 中的单个属性/字段,那么当该复选框状态更改时,您的底层绑定对象也会更改。不需要确定哪个布尔属性,它通过绑定是显式的。也许我不明白这个问题。
  • @SRM,也许我遗漏了一些东西,但是我的模型的状态正在保持,我需要在后续访问时保留网格中每个项目的布尔值(抱歉没有提及在我的原始帖子中。)似乎您的解决方案会打破这一点。这就是为什么我不能使用触发器。仅供参考,我确实使用多重绑定解决了我的问题,解决方案将很快发布。

标签: c# wpf listview checkbox


【解决方案1】:

我已经解决了我的问题,它使用了一个命令以及一个包含我要设置的模型属性名称的标签,以及一个设置为绑定到标签和复选框的 IsSelected 属性的多重绑定的 CommandParameter。代码如下:

My View 的 Xaml 资源:

<UserControl.Resources>
   <converters:NameValueMultiBindingConverter x:Key="SelectAllConverter" />
</UserControl.Resources>

我的视图的 Xaml:

<ListView ItemsSource="{Binding AveragingParameters}">
  <ListView.View>
    <GridView AllowsColumnReorder="False">

      <GridViewColumn Header="Parameter">
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <DockPanel>
              <TextBlock Text="{Binding Name}" />
            </DockPanel>
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>

      <GridViewColumn>
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <Grid HorizontalAlignment="Stretch">
              <CheckBox  x:Name="chkAvg" IsChecked="{Binding CalcAverage}" />
            </Grid>
          </DataTemplate>
        </GridViewColumn.CellTemplate>

        <CheckBox x:Name="chkAvgSelectAll" Content="Avg" 
                  Tag="CalcAvg" Command="SelectAllCheckedCommand" 
                  ToolTip="Select All">
            <MultiBinding Converter="{StaticResource SelectAllConverter}">
              <Binding Path="Tag" RelativeSource="{RelativeSource self}" />
              <Binding Path="IsChecked" RelativeSource="{RelativeSource self}" />
            </MultiBinding>
        </CheckBox>
      </GridViewColumn>

      <GridViewColumn>
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <Grid HorizontalAlignment="Stretch">
              <CheckBox  x:Name="chkMin" IsChecked="{Binding CalMin}" />
            </Grid>
          </DataTemplate>
        </GridViewColumn.CellTemplate>

        <CheckBox x:Name="chkMinSelectAll" Content="Avg" 
                  Tag="CalcMin" Command="SelectAllCheckedCommand" 
                  ToolTip="Select All">
            <MultiBinding Converter="{StaticResource SelectAllConverter}">
              <Binding Path="Tag" RelativeSource="{RelativeSource self}" />
              <Binding Path="IsChecked" RelativeSource="{RelativeSource self}" />
            </MultiBinding>
        </CheckBox>
      </GridViewColumn>

    </GridView>
  </ListView.View>
</ListView>

我的多值转换器:

public class NameValueMultiBindingConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter,
                          System.Globalization.CultureInfo culture)
    {

        var parameters = (object[])values;
        return new NameValueConverterResult 
                        { 
                           Name = (string)parameters[0], 
                           Value = parameters[1] 
                        };
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, 
                                System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

我在转换器中使用的结果对象:

public class NameValueConverterResult
{
    //The name of the model property to set
    public string Name { get; set; }

    //The value we're setting it to
    public object Value { get; set; }
}

我的视图模型中的 DelegateCommand(我正在使用 PRISM)和处理程序

public ICommand SelectAllCheckedCommand { get; private set; }
private void OnSelectAllCheckedCommand(object arg )
{
   if (arg == null || !(arg is NameValueConverterResult)) return;

   NameValueConverterResult prop = arg as NameValueConverterResult;

   //Reflect on the model to find the property we want to update.
   PropertyInfo propInfo = Averagers.FirstOrDefault().GetType().GetProperty(prop.Name);
   if (propInfo == null) return;

   //Set the property on the Model
   foreach (var item in Averagers)
      propInfo.SetValue(item, prop.Value, null);
} 

我希望我没有滥用 StackOverflow 来提供我想出的解决方案。我不确定这里的礼仪。

【讨论】:

    【解决方案2】:

    只需根据需要交出尽可能多的信息,然后通过反射完成其余的工作,例如

    <ListView ItemsSource="{Binding DpData}">
        <ListView.View>
            <GridView>
                <GridViewColumn>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsChecked="{Binding IsActive}"/>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                    <!-- Pass collection to Tag property, relevant member info is in the Content,
                         could also create an array in the Tag if the Content should be a nicer
                         looking string. -->
                    <CheckBox Content="IsActive" Click="CheckBox_Click" Tag="{Binding DpData}"/>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>
    
    // This method could be used for all columns,
    // as long as they contain the necessary info
    // which should be provided in the XAML:
    //     - Item Collection
    //     - Property Name
    private void CheckBox_Click(object sender, RoutedEventArgs e)
    {
        var cb = sender as CheckBox;
        var items = cb.Tag as IEnumerable;
        PropertyInfo prop = null;
        foreach (var item in items)
        {
            if (prop == null)
            {
                var type = item.GetType();
                var propname = cb.Content as string;
                prop = type.GetProperty(propname);
            }
            prop.SetValue(item, cb.IsChecked, null);
        }
    }
    

    【讨论】:

    • 感谢您的帖子。我会试一试。我假设 DPData 是我在原始帖子中调用的 AveragedParameters 的同义词。
    • 任何种类的 ItemsSource,是的。
    • 你的帖子给了我一些想法。由于我使用的是 MVVM,我的最终解决方案使用了 MultiBinding。我会尽快发布我的解决方案。
    【解决方案3】:

    如果您想坚持使用 MVVM 方法,我建议您使用绑定方法而不是事件处理方法,因为 MVVM 避免在代码隐藏文件中编写代码。

    一种方法是简单地将视图模型中的两个 bool 属性与两个复选框的 IsChecked 属性绑定。根据使用 INotifyPropetyChanged 的​​更改通知,您可以遍历您的 itemssource,即 AveragingParameters 并更改 CalMinCalcAverage

    【讨论】:

    • 这是对 MVVM 的常见误解。 MVVM 并没有规定后面没有代码。它所规定的是您的代码中没有业务逻辑。我听说它是​​这样说的:“CodeBehind 应该只是设计师的领域”。换句话说,如果您有代码,请确保它是特定于视图的并且没有业务逻辑。否则,您最终会在 ViewModel 中看到令人讨厌的视图逻辑组合 - 就像将业务逻辑放入您的视图中一样糟糕。
    • 感谢您的帖子。我实际上使用多重绑定解决了这个问题。我会尽快将其作为解决方案包含在内。
    • 我相信@SRM 关于 MVVM 和代码隐藏是正确的。如果您需要对 WPF 和 Silverlight 的支持,遵循这些准则将使您可以轻松地支持其他视图。至于您提出的解决方案,如果可以的话,我想避免绑定到多个属性。原因是,此代码的下一次迭代可能是用户控件,它允许使列数动态化。今天我有 4 列复选框。未来的消费者可能需要不同的视图模型并且需要更多或更少的列。
    猜你喜欢
    • 1970-01-01
    • 2019-11-24
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 2016-01-17
    • 2014-06-20
    • 1970-01-01
    相关资源
    最近更新 更多