【问题标题】:Identify which button was clicked in WPF gird based on 2D array根据二维数组确定在 WPF 网格中单击了哪个按钮
【发布时间】:2020-01-21 09:21:04
【问题描述】:

我正在构建一个应用程序,它将在 2D 网格中显示按钮列表,类似于 How to populate a WPF grid based on a 2-dimensional array

挑战在于判断在buttonClick 事件处理程序中单击了哪个按钮(以 i、j 表示)。

我最初的想法是让按钮名称包含 i,j 的字符串表示形式(例如 'button_2_3'),但无法使用 '{binding ...}' 指定名称。

不能使用按钮标签(button.content),因为应用程序要求在按钮上显示非唯一标签(按钮标签代表单元格上的一块,类似于国际象棋,其中“Q”标签表示皇后的位置)。

最好的选择似乎是在按钮的 CommandParameter 属性中存储行/列的表示。

我的问题:如何填充每个按钮的 CommandParameter 以便于提取单击按钮的 (i, j) ?将用户定义的数据附加到按钮是否有更好的选择?

发件人:How to populate a WPF grid based on a 2-dimensional array

<Window.Resources>
<DataTemplate x:Key="DataTemplate_Level2">
        <Button Content="{Binding}" Height="40" Width="50" Margin="4,4,4,4" click="buttonClick"/>
</DataTemplate>

<DataTemplate x:Key="DataTemplate_Level1">
    <ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</DataTemplate>

public Window1()
{
    List<List<int>> lsts = new List<List<int>>();

    for (int i = 0; i < 5; i++)
    {
        lsts.Add(new List<int>());

        for (int j = 0; j < 5; j++)
        {
            lsts[i].Add(i * 10 + j);
        }
    }

    InitializeComponent();

    lst.ItemsSource = lsts;
}

private void buttonClick(object sender, EventArgs e)
{
  //do something or...
  Button clicked = (Button) sender;
  MessageBox.Show("Button's name is: " + clicked.Name);
}

【问题讨论】:

  • 有没有更好的替代方法将用户定义的数据附加到按钮上?使用标签。
  • UniformGrid 将是 ItemsPanel(具有一维 ItemsSource)的更好选择。查看示例:stackoverflow.com/q/43097851/1506454stackoverflow.com/q/37145391/1506454
  • 最终合并了两个提议的解决方案 - 使用“标签”来存储具有“行”和“列”以及“值”属性的对象。

标签: wpf button datatemplate commandparameter


【解决方案1】:

您可以绑定 Button 的 Tag 属性

<Button Tag="{Binding}" .../>

并在 Clicked 处理程序中使用它,如下所示:

private void Button_Click(object sender, RoutedEventArgs e)
{
    int value = (int)((Button)sender).Tag;
    int row = value / 10;
    int column = value % 10;

    Debug.WriteLine("Clicked row {0}, column {1}", row, column);
}

使用带有 UniformGrid 作为 ItemsPanel 的单个 ItemsControl 也可能更简单:

<ItemsControl ItemsSource="{Binding}"
              HorizontalAlignment="Left" VerticalAlignment="Top">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="5"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Content="{Binding}" Tag="{Binding}"
                    Height="40" Width="50" Margin="4" Click="Button_Click"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

并将其 ItemsSource 绑定到 List&lt;int&gt;:

public MainWindow()
{
    InitializeComponent();

    var list = new List<int>();

    for (int i = 0; i < 5; i++)
    {
        list.AddRange(Enumerable.Range(i * 10, 5));
    }

    DataContext = list;
}

【讨论】:

  • 感谢您对uniformGrid 的意见。我无法使用“内容”作为按钮的提示-正如我在问题中指出的那样-标签(按钮的content)显示单元格中的棋子(类似于国际象棋,其中“Q”将指示女王位置)。我在帖子中强调了这一要求。
  • 请查看编辑后的答案。您也可以绑定 Tag 属性。
【解决方案2】:

您可以为按钮视图模型使用类型而不是 int 列表。

然后在内容中绑定“名称”,按钮将在 DataContext 中包含您的对象。

class ButtonViewModel
{
  public string Name {get;set;} 
  public int Value {get;set;} 
}  

public Window1()
{
    List<List<ButtonViewModel>> lsts = new List<List<ButtonViewModel>>();

    for (int i = 0; i < 5; i++)
    {
        lsts.Add(new List<ButtonViewModel>());

        for (int j = 0; j < 5; j++)
        {
            var model = new ButtonViewModel();
            model.Value = i * 10 + j;
            model.Name = "Q: "+ model.Value;
            lsts[i].Add(model);
        }
    }

    InitializeComponent();

    lst.ItemsSource = lsts;
}

private void buttonClick(object sender, EventArgs e)
{
  //do something or...
  Button clicked = (Button) sender;
  MessageBox.Show("Button's name is: " + (clicked.DataContext as ButtonViewModel).Name);
}

WPF:

<Window.Resources>
<DataTemplate x:Key="DataTemplate_Level2">
        <Button Content="{Binding Name}" DataContext={Binding} Height="40" Width="50" Margin="4,4,4,4" click="buttonClick"/>
</DataTemplate>

附:代替按钮“сlick”事件,可以使用“command”属性。应该将命令属性添加到 ButtonViewModel。这是一种正确的 MVVM 方式。 ButtonViewModel 也应该实现 IPropertyChanged 接口来通知接口值的变化。

【讨论】:

  • 对于 WPF,DataTemplate 'DataTemplate_Level2' 是否需要以任何方式更新?它没有在答案中列出,我不确定这是否意味着不需要更改。
  • @dash-o 是的,您应该将“内容”绑定到“名称”属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
相关资源
最近更新 更多