WPF 中的复选框网格

发布于 2024-10-08 19:03:29 字数 481 浏览 5 评论 0原文

我有一个 WPF UserControl 的数据上下文绑定到这样的类:

public class CheckBoxGridViewModel
{
  public List<List<bool>> Checkboxes {get; set;}
}

我希望它显示复选框网格。我假设我可以使用 Itemscontrol,但不确切知道如何使用每行的动态列集来实现它。

这个问题似乎回答了我的问题,除了答案没有给出这个例子,我不知道如何写出来。

所以问题是,我将如何编写 xaml 来显示 Checkboxes 属性的复选框,以便它们排列在一个漂亮的网格中?

外部列表将是每一行,内部列表将是该行的每一列。

I have a WPF UserControl's datacontext tied to a class like this:

public class CheckBoxGridViewModel
{
  public List<List<bool>> Checkboxes {get; set;}
}

I want it to display a grid of checkboxes. I'm assuming I can use an Itemscontrol, but don't know exactly how to do it with a dynamic set of columns for each row.

This question seems to answer mine, except the answer didn't give the example and I can't figure how to write it out.

So the question is, how would I write the xaml to display the checkboxes of the Checkboxes property so that they are lined up in a nice grid?

The outer list would be each row and the inner list would be each column of the row.

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

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

发布评论

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

评论(4

述情 2024-10-15 19:03:29

目前尚不清楚您是否期望每个内部列表具有相同的大小,但如果它们是相同的大小,您可以使用简单的设置。将嵌套 ItemsControls 与单行/列 UniformGrids 结合使用将为您提供均匀分布并自动处理任何大小的集合,而无需像网格一样设置行和列定义:

<ItemsControl ItemsSource="{Binding Checkboxes}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <ItemsControl ItemsSource="{Binding}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <CheckBox IsChecked="{Binding}"/>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsPanelTemplate>
          <UniformGrid Rows="1"/>
        </ItemsPanelTemplate>
      </ItemsControl>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <UniformGrid Columns="1"/>
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>

It's not clear if you're expecting each inner List to be the same size but if they are you can use a simple setup. Using nested ItemsControls with single row/column UniformGrids will give you an even distribution and automatically handle collections of any size without needing to setup Row and Column Definitions like with Grid:

<ItemsControl ItemsSource="{Binding Checkboxes}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <ItemsControl ItemsSource="{Binding}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <CheckBox IsChecked="{Binding}"/>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsPanelTemplate>
          <UniformGrid Rows="1"/>
        </ItemsPanelTemplate>
      </ItemsControl>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <UniformGrid Columns="1"/>
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>
ぇ气 2024-10-15 19:03:29

请参阅这个问题< /a>. Jobi Joy 的答案将让您呈现 2D 列表,但绑定不起作用,因此您无法编辑您的值。

为了能够绑定值,您可以使用像这样的帮助器类

public static class BindableListHelper
{
    public static List<List<Ref<T>>> GetBindable2DList<T>(List<List<T>> list)
    {
        List<List<Ref<T>>> refInts = new List<List<Ref<T>>>();

        for (int i = 0; i < list.Count; i++)
        {
            refInts.Add(new List<Ref<T>>());
            for (int j = 0; j < list[i].Count; j++)
            {
                int a = i;
                int b = j;
                refInts[i].Add(new Ref<T>(() => list[a][b], z => { list[a][b] = z; }));
            }
        }
        return refInts;
    }
}

此方法使用此 Ref 类

public class Ref<T> 
{
    private readonly Func<T> getter; 
    private readonly Action<T> setter;
    public Ref(Func<T> getter, Action<T> setter) 
    { 
        this.getter = getter; 
        this.setter = setter; 
    }
    public T Value { get { return getter(); } set { setter(value); } } 
}

然后您可以为 ItemsControl 设置 ItemsSource

itemsControl.ItemsSource = BindableListHelper.GetBindable2DList<bool>(Checkboxes);

,并且编辑应该可以

使用与 Jobi Joy 相同的代码我链接的问题,您可以将 DataTemplate_Level2 中的 Button 更改为 CheckBox 并将其 IsChecked 绑定到 Value (因为否则它将指向 Ref 类)

<Window.Resources>
    <DataTemplate x:Key="DataTemplate_Level2">
        <CheckBox IsChecked="{Binding Path=Value}" Height="15" Width="15" Margin="2"/>
    </DataTemplate>
    <DataTemplate x:Key="DataTemplate_Level1">
        <ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </DataTemplate>
</Window.Resources>
<StackPanel>
    <Border HorizontalAlignment="Left" BorderBrush="Black" BorderThickness="2">
        <ItemsControl x:Name="itemsControl" ItemTemplate="{DynamicResource DataTemplate_Level1}"/>
    </Border>
</StackPanel>

如果不设置 CheckBox 的 Content 属性,它将看起来像这样

< img src="https://i.sstatic.net/AZdyl.png" alt="替代文本">

See this question. The answer by Jobi Joy will let you present the 2D list but Binding won't work so you can't edit your values.

To be able to bind the values you can use a helper class like this

public static class BindableListHelper
{
    public static List<List<Ref<T>>> GetBindable2DList<T>(List<List<T>> list)
    {
        List<List<Ref<T>>> refInts = new List<List<Ref<T>>>();

        for (int i = 0; i < list.Count; i++)
        {
            refInts.Add(new List<Ref<T>>());
            for (int j = 0; j < list[i].Count; j++)
            {
                int a = i;
                int b = j;
                refInts[i].Add(new Ref<T>(() => list[a][b], z => { list[a][b] = z; }));
            }
        }
        return refInts;
    }
}

This method uses this Ref class

public class Ref<T> 
{
    private readonly Func<T> getter; 
    private readonly Action<T> setter;
    public Ref(Func<T> getter, Action<T> setter) 
    { 
        this.getter = getter; 
        this.setter = setter; 
    }
    public T Value { get { return getter(); } set { setter(value); } } 
}

Then you can set ItemsSource for the ItemsControl with

itemsControl.ItemsSource = BindableListHelper.GetBindable2DList<bool>(Checkboxes);

and the editing should work

Using the same code as Jobi Joy from the question I linked, you can change the Button in DataTemplate_Level2 to a CheckBox and bind IsChecked for it to Value instead (since it will point to the Ref class otherwise)

<Window.Resources>
    <DataTemplate x:Key="DataTemplate_Level2">
        <CheckBox IsChecked="{Binding Path=Value}" Height="15" Width="15" Margin="2"/>
    </DataTemplate>
    <DataTemplate x:Key="DataTemplate_Level1">
        <ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </DataTemplate>
</Window.Resources>
<StackPanel>
    <Border HorizontalAlignment="Left" BorderBrush="Black" BorderThickness="2">
        <ItemsControl x:Name="itemsControl" ItemTemplate="{DynamicResource DataTemplate_Level1}"/>
    </Border>
</StackPanel>

Without setting the Content property for the CheckBox it'll look something like this

alt text

橘味果▽酱 2024-10-15 19:03:29

您可以考虑创建一个公开 RowColumnValue 属性的类,并绑定到这些属性的集合。这使您可以使用您选择的方法分配行和列位置,并且网格中的布局非常简单(一旦您了解 ItemsControl 如何使用其 ItemsPanel当然是 ItemContainerStyle 属性):

      <ItemsControl ItemsSource="{Binding Checkboxes}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <CheckBox IsChecked="{Binding Value, Mode=TwoWay}"/>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
          <ItemsPanelTemplate>
          <Grid DockPanel.Dock="Top">
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="20"/>
              <ColumnDefinition Width="20"/>
              <ColumnDefinition Width="20"/>
              <ColumnDefinition Width="20"/>
              <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
            </Grid.RowDefinitions> 
            </Grid>
          </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
          <Style TargetType="ContentPresenter">
            <Setter Property="Grid.Row" Value="{Binding Row}"/>
            <Setter Property="Grid.Column" Value="{Binding Column}"/>
          </Style>
        </ItemsControl.ItemContainerStyle>
      </ItemsControl>

You might consider creating a class that exposes Row, Column, and Value properties, and binding to a collection of these. This lets you assign row and column positions using the method of your choice, and layout in a grid is very straightforward (once you understand how ItemsControl uses its ItemsPanel and ItemContainerStyle properties, of course):

      <ItemsControl ItemsSource="{Binding Checkboxes}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <CheckBox IsChecked="{Binding Value, Mode=TwoWay}"/>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
          <ItemsPanelTemplate>
          <Grid DockPanel.Dock="Top">
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="20"/>
              <ColumnDefinition Width="20"/>
              <ColumnDefinition Width="20"/>
              <ColumnDefinition Width="20"/>
              <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
              <RowDefinition Height="20"/>
            </Grid.RowDefinitions> 
            </Grid>
          </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
          <Style TargetType="ContentPresenter">
            <Setter Property="Grid.Row" Value="{Binding Row}"/>
            <Setter Property="Grid.Column" Value="{Binding Column}"/>
          </Style>
        </ItemsControl.ItemContainerStyle>
      </ItemsControl>
呆° 2024-10-15 19:03:29

您到底想要如何排列这个网格?这将影响使用哪个 ItemsControl.ItemsPanel。一些想法...

使用 UniformGrid 或 WPF 工具包 WrapPanel。

How exactly do you want this grid arranged? This will influence which ItemsControl.ItemsPanel to use. A couple of ideas...

Use a UniformGrid, or the WPF Toolkit WrapPanel.

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