如何知道在使用c#编程创建的wpf按钮中单击了哪个按钮

本文关键字:按钮 wpf 单击 创建 何知道 编程 | 更新日期: 2023-09-27 18:13:35

我在这种情况下,我必须点击在c#编程动态创建的按钮(因为用户在运行时决定要创建多少按钮)。我这样写代码:

int runTimeIntegerValue = 6; //Lets say its 6
Button[] childGridSavebutn = new Button[runTimeIntegerValue -1];
for (int j = 0; j < runTimeIntegerValue; j++)
{   
    childGridSavebutn[j] = GenerateButton("Save Table");
    childSmallgrid[j] = generateRowColumnGrid(1, 1);
    Grid.SetColumn(childGridSavebutn[j], 1);
    Grid.SetRow(childGridSavebutn[j], i);
    childSmallgrid[j].Children.Add(childGridSavebutn[j]);
    childGridSavebutn[j].Click += (source, e) =>
    {
        txtBoxOrder[j].Text = "I am from " + j;
        MessageBox.Show("I am from " + j);  //This message always show "I am From 5"
    };
}
private Button GenerateButton(string p)
{
    Button btn = new Button();
    //btn.Name = p;
    btn.Content = p;
    btn.Width = 80;
    btn.HorizontalAlignment = HorizontalAlignment.Center;
    btn.Height = 20;           
    return btn;
}

我弹出一个消息后,MessageBox.Show("I am from " + i);知道这个消息框是从哪个按钮弹出的。它总是弹出5(因为i的最后一次计数是5)。

那么如何知道哪个按钮被点击了,通过点击那个按钮?因为有5个按钮,所以我应该知道点击按钮,按钮编号1/2/3/4/5是由弹出的消息与尊重i的位置计时。(我的意思是按钮在位置被点击,消息必须显示这"我来自2"当第二个按钮被点击)

SUMMARY:我有childgridsavebutton[1到5]数组(它甚至可以由用户决定运行时间)。它显示了5个按钮,怎么知道哪个按钮被点击了?(因为它总是弹出第5个),因为进一步我必须产生按钮点击事件接收。我将如何做到这一点,如果我不知道哪个按钮被点击。最后UI必须像这样重复5次

如何知道在使用c#编程创建的wpf按钮中单击了哪个按钮

您确定需要以编程方式创建这些按钮吗?考虑一种基于视图模型的纯XAML生成按钮的方法。

假设你有一个单一按钮的视图模型:

public class SingleButtonViewModel {
    public string Text { get; set; }
    public ICommand Command { get; set; }
}

父视图模型:

public class ParentViewModel {
    public IList<SingleButtonViewModel> Buttons { get; set; }
}

在XAML中:

<ItemsControl ItemsSource="{Binding Buttons}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Content="{Binding Text}" Command="{Binding Command}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

然后你以编程方式创建你的按钮视图模型,给它任何你想要的命令。

考虑到我没有实现INotifyPropertyChanged,因此为了可读性,

尝试将代码更改为。

for (int jj = 0; jj < runTimeIntegerValue; jj++)
{   
    int j = jj;
    some code as you had before    
}

你得到的问题是由于c#如何捕获闭包中的变量。

Eric Lippert对这种行为有深入的解释:

  • 关闭被认为有害的循环变量
  • 关闭循环变量,第二部分

基本上,被捕获的是循环变量,而不是它的值。


每个人都至少遇到过一次捕获变量的问题,如果你不知道它是什么,你就不能用谷歌搜索解决方案。

使用Tag作为其他答案也是一个有效的解决方案。同样,使用数据绑定模板可能是最好的解决方案。因此,我不会投票关闭这个问题,因为它是关于闭包中捕获变量的101个问题之一的副本。

编辑2:你在评论中所要求的

<Grid Name="ButtonsContainer">
    <ListBox Name="lb_GlobalList">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <StackPanel>
                        <Button Tag="{Binding SomeNumberValue}" Content="Click me" Click="Button_Click" />
                        <TextBlock Text="{Binding Path=SomeNumberValue, StringFormat={}I am from {0}}" />
                    </StackPanel>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        List<SomeBusinessObject> businessObjects = new List<SomeBusinessObject>();
        for (int i = 0; i < 5; i++)
        {
            businessObjects.Add(new SomeBusinessObject() { SomeNumberValue = i, SomeTextValue = "sample text" });
        }
        lb_GlobalList.ItemsSource = businessObjects;
    }
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("I am from " + (sender as Button).Tag.ToString());
    }
}
public class SomeBusinessObject
{
    public int SomeNumberValue { get; set; }
    public string SomeTextValue { get; set; }
}

编辑:我不是故意使用你的代码,所以你会理解:

  1. 捕获封闭
  2. 中的变量
  3. 如何正确使用没有Name属性的控件(事件中的发送者对象)
  4. 如何使用Tag属性存储任何对象-甚至其他控件
  5. 你所做的在很多层面上都是错误的——如在其他回答
  6. 中评论的那样,DataBindig, DataTemplate等。

在我看来,最好的解决方案是添加一些元数据,你想与你的Button类使用Tag关联,然后添加点击事件通常的方式和使用事件处理程序中的(sender as Button)来访问按钮

参见我的例子

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    for (int i = 0; i < 10; i++)
    {
        Button b = new Button();
        b.Tag = i;
        b.Click += B_Click;
        sp_Container.Children.Add(b);
    }
}
private void B_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show((sender as Button).Tag.ToString());
}

在我看来,你所做的是错误的。在特殊情况下,您应该更需要这个示例。WPF中有数据绑定功能,使用它吧!

Replace

childGridSavebutn[i].Click += (source, e) =>
{    
    txtBoxOrder[i].Text = "I am from " + i;
    MessageBox.Show("I am from " + i);  //This message always show "I am From 5"
};

childGridSavebutn[i].Tag = i;
childGridSavebutn[i].Click += (source, e) =>
{
    var button = source as Button;
    var index = (int)button.Tag;
    txtBoxOrder[index].Text = "I am from " + index;
    MessageBox.Show("I am from " + index);
};

订阅事件时必须设置i的值,可以使用Tag属性。否则,您将获得每条消息上i的最后更新值。

当你当前的代码得到一个按钮点击j的值已经完成了循环,所以对于所有按钮点击你得到j的最后一个值

但是很容易修复。只要像这样改变你的循环:

for (int j = 0; j < runTimeIntegerValue; j++)
{
    childGridSavebutn[j] = GenerateButton("Save Table");
    childSmallgrid[j] = generateRowColumnGrid(1, 1);
    Grid.SetColumn(childGridSavebutn[j], 1);
    Grid.SetRow(childGridSavebutn[j], i);
    childSmallgrid[j].Children.Add(childGridSavebutn[j]);
    childGridSavebutn[j].Click += (source, e) =>
    {
        int j2 = j;
        txtBoxOrder[j2].Text = "I am from " + j2;
        MessageBox.Show("I am from " + j2);
    };
}

int j2 = j;行在循环运行时捕获j的值,以便在单击发生时j2是正确的。