【问题标题】:How to retrieve which Button has been Clicked when Its Generated Dynamically动态生成时如何检索已单击的按钮
【发布时间】:2012-10-15 17:26:48
【问题描述】:

好吧,我是一名 C++ 开发人员,目前我正在开发 WPF 应用程序,看起来这是一个棘手的情况。我已经动态生成了一组按钮、标签等,其中文本框和按钮都相互绑定。我之前在 C++ 代码中完成了此操作,现在我需要在 WPF 应用程序中执行此操作。

XAML:

<ListBox x:Name="myViewChannelList" HorizontalAlignment="Stretch" Height="Auto" ItemsSource="{Binding VoltageCollection}" Margin="0" VerticalAlignment="Stretch" Width="Auto" >
            <ListBox.Resources>
                <convert:BooleanToVisibilityConverter x:Key="booltovisibility"/>
            </ListBox.Resources>

            <ListBox.ItemTemplate>
                <DataTemplate >
                    <Grid Visibility="{Binding IsAvailable, Converter={StaticResource booltovisibility}}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="170"  />
                            <ColumnDefinition />
                            <ColumnDefinition  />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>

                        <Label Grid.Column="0" Content="{Binding ChannelName}" Margin="50,20,0,0"></Label>

                        <Grid Grid.Column="1">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
                            <TextBox Grid.Column="0" Text="{Binding VoltageText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="25" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="170,20,0,0" />
                            <Button Grid.Column="1" Content="Set" Height="25" CommandParameter="{Binding VoltageText}" Command="{Binding VoltageCommand}" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20,20,0,0" ></Button>
                        </Grid>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

视图模型:

private ICommand m_voltageCommand;

    public ChannelList()
    {
         m_voltageCommand = new DelegateVoltageCommand(x => SetCommandExecute(x));
    }

public void Initialize()
{
    VoltageCollection = new ObservableCollection<VoltageModel> { new VoltageModel() { ChannelName = "", IsAvailable = false, VoltageText = String.Empty, VoltageCommand = m_voltageCommand },
                                                                 new VoltageModel() { ChannelName = "VDD__Main", IsAvailable = true, VoltageText = String.Empty, VoltageCommand = m_voltageCommand }, 
                                                                 new VoltageModel() { ChannelName = "VDD__IO__AUD", IsAvailable = true, VoltageText = String.Empty, VoltageCommand = m_voltageCommand }, 
                                                                 new VoltageModel() { ChannelName = "VDD__CODEC__AUD", IsAvailable = true, VoltageText = String.Empty, VoltageCommand = m_voltageCommand } 
                                                               }; 
}

ObservableCollection<VoltageModel> _voltages;
public ObservableCollection<VoltageModel> VoltageCollection
{
    get
    {
        return _voltages;
    }
    set
    {
        _voltages = value;
        OnPropertyChanged("VoltageCollection");
    }
} 

// Event when SET Button is clicked
public void SetCommandExecute(object voltageText)
{       
    string value = voltageText.ToString();
    int val = Convert.ToInt32(value);
}

因此它生成 Button + Textbox + Label 3 次,如Initialize() 方法所示。现在VoltageCommand = m_voltageCommand 给了我在文本框中输入的文本,它调用了SetCommandExecute(object voltageText) 方法,其中电压文本给了我输入的值。

型号:

string voltageText = string.Empty;
    public string VoltageText
    {
        get
        {
            return voltageText;
        }

        set
        {
            voltageText = value;
            OnPropertyChanged("VoltageText");
        }
    }

**C++ Code:**

// Since we have 3 channels, channel maintains count
if(button == m_setButton[channel])
{
    unsigned cmd = 0x0300;
    int numBytes = 0;

    cmd |= (channel & 0xFF);
            // Some code

这里它告诉用户哪个按钮被点击并取channel的值,即如果第二个按钮被点击然后channel = 2

这里我需要实现用 C++ 编写的代码。如何获取频道,即点击了哪个按钮。看看cmd |= (channel &amp; 0xFF);,它使用了channel 值。如何在我的应用程序中实现它?

【问题讨论】:

  • 我不确定我是否理解您想要做什么,但为什么不将整个 VoltageModel 传递给命令而不是只传递文本?只需将CommandParameter="{Binding VoltageText}" 更改为CommandParameter="{Binding }"
  • 为什么不只为按钮使用 Tag 属性? button1.Tag = 1
  • 动态与否,它是一个框架元素属性。 :) 我对一些代码的 Tag 属性使用自定义类。 Tag property 是许多非常方便的框架元素属性之一。
  • @StonedJesus 如果您使用的是 MVVM 设计模式,那么您根本不应该引用 UI 对象。我不明白您为什么要实际引用 Button 对象。听起来你只是想知道项目的项目索引,在这种情况下,我会将VoltageModel 传递为CommandParameter,并使用VoltageCollection.IndexOf(ItemPassedInCommandParameter) 查找当前项目的索引。
  • @StonedJesus 我正要回答你的问题,但看到有人已经在下面发布了答案:)

标签: c# .net wpf button mvvm


【解决方案1】:

您可以简单地将 ID 属性添加到您的 VoltageBoardChannel 类。

int index ; 
public int ID 
{ 
    get 
    { 
        return index; 
    } 

    set 
    { 
        index = value; 
        OnPropertyChanged("ID"); 
    }
}

然后将您的 CommandParameter Binding 更改为 CommandParameter="{Binding}" 而不是 CommandParameter="{Binding VoltageText}",您现在不仅会收到 Text,还会收到现在拥有 ID 的 VoltageBoardChannel 类的实例。

在您的命令执行方法中

public void DoSomethingExecute(object param) 
{ 
    VoltageBoardChannel result = param as VoltageBoardChannel; 
    string value = result.VoltageText;
    int index = result.ID;
}

【讨论】:

  • 谢谢马克。我希望我能为你演奏鼓独奏:D
【解决方案2】:

这是您实现 MVVM 的根本问题:

ViewModel 永远不应该知道 View;它应该可以独立于任何 View 元素进行测试

在您的 SetCommandExecute 方法中,期望执行一些基于从视图发送的文本的工作。如果你要为 SetCommandExecute 方法编写一个单元测试,只使用来自 ViewModel 其他部分的信息,你会传入什么?

相反,您的 SecCommandExecute 应该是:

SetCommandExecute(object voltageModel)
{
    VoltageModel myModel = voltageModel as VoltageModel; // Cast the model to the correct object type
    if (myModel != null)
    { // myModel will be null of voltageModel is not a VoltageModel instance
        // TODO: Whatever work you need to do based on the values of the myModel
    }
}

您的 XML 应为:

<Button Grid.Column="1" Content="Set" Height="25" CommandParameter="{Binding }" Command="{Binding VoltageCommand}" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20,20,0,0" ></Button>

这是如何工作的?那么它归结为DataContext。由于网格中每条线的 DataContext 都是一个电压模型,因此您可以直接绑定到它的几个属性。示例Text="{Binding VoltageText ...}"

datagrid 中的每一项都有每行的对象实例的隐含 DataContext。由于每一行都绑定到 VoltageModel 的一个实例,因此您可以直接在代码中使用该事实。 View 知道它正在使用哪些 ViewModel 属性和实例,并且可以将用户操作的特定电压模型“向下”传递回 ViewModel。

推理:

当您的命令事件运行时,它应该传入 VoltageModel 对象,这使您可以直接访问所有实例属性。请记住,这是最佳实践,因为您希望能够对 SetCommandExecute 方法进行单元测试,而不需要视图传递一些文本、解析它、在视图上找到一些控件等等。

简而言之:ViewModel 应该是完全独立的,并且能够根据仅 ViewModel 可用的数据运行所有单元测试。

【讨论】:

  • 是的,我知道。 Rachel 早先帮了我很多 :) 好吧,让我告诉你我想在这里实现什么。当我在文本框中输入值并单击按钮时,输入的值被检索并调用 SetCommandExecute 其中对象电压文本给我输入的值。因此,在这里我将执行一些操作,我需要知道哪个channel 已被触发,即如果我在ChannelName="VDD_IO_AUD" 文本框中输入值并单击SET 按钮,它应该给索引2,因为VDD_IO_AUD 是列表中的第二项.
  • 使用这个值 2. 我必须调用 updatemethod 来做一些操作:)
  • 只是一个澄清问题,每一行都有自己的设置按钮,对吧?
  • 是的,伙计,你是对的 :) 供参考检查初始化方法。你会发现VoltageText = String.Empty, VoltageCommand = m_voltageCommand 因此它给了我我在 VoltageText 和 On Button Click 中输入的值采用了各自的值:)
【解决方案3】:

我自己只做了一点 WPF。当我将数据绑定到控件时,我可以通过某种方式将该数据项与其他数据项唯一区分开来。

伪代码:

private void Button_Clicked(object sender, EventArgs e) {
  MyType obj = (MyType)listView1.SelectedItem[0];
  if (obj.UniqueItem == whatINeed) {
    DoStuffFunction();
  }
}

我不知道这是否适用于您的情况,但这就是我解决问题的方式。

【讨论】:

  • 感谢您的回复队友 :) 看起来这对我没有帮助:(
【解决方案4】:

您已经将电压文本框绑定到一个属性,因此不需要将该值作为命令参数传入。相反,您可以将源指定为命令:

<Button CommandParameter="TheButton" />

在你的命令处理程序的实现中:

public void SetCommandExecute(object source)
{       
    string source = source as string;

    if (source == "TheButton")
    {
       int val = Convert.ToInt32(this.VoltageText);
    }
}

【讨论】:

  • 是的,我已经这样做了。检查更新的代码:) 但仍然无法弄清楚如何完成它
  • 嘿兄弟,我仍然无法弄清楚你到底想在这里做什么:)
【解决方案5】:

您好,不要将 VoltageText 绑定到 CommandParameter 而是将 Button 绑定到它,您可以从 ButtonDataContext 获取 VoltageText 或通过将 ListBox 的 SelectedItem 绑定到 ViewModel 属性。我希望这会有所帮助。

<ListBox x:Name="lb">
        <ListBoxItem>
            <Button x:Name="btn" CommandParameter="btn" Command="{Binding MyCommand}" VerticalAlignment="Bottom" Height="30"/>
        </ListBoxItem>
        <ListBoxItem>
            <Button  CommandParameter="{Binding SelectedIndex, ElementName=lb}" Command="{Binding MyCommand}" VerticalAlignment="Bottom" Height="30"/> 
        </ListBoxItem>
    </ListBox>

 public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }
    private ICommand _myCommand;
    public ICommand MyCommand { get { return _myCommand ?? (new CommandHandler((o) => FireCommand(o),()=> true)); } }
    public void FireCommand(object obj)
    {
        var a = lb.SelectedIndex; 
    }

public class CommandHandler:ICommand
{
        private Action<object> del;
        public CommandHandler(Action<object> action, Func<bool> b=null)
        {
            del = action;
        }

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            del(parameter);
        }

        #endregion
}

您可以尝试以上两种方法之一。

【讨论】:

  • 这些按钮有什么属性可以用来区分它们吗?
  • 并非如此。我只想知道所选按钮的索引。
  • 我更新了答案。这只是给出想法的一种原型。
  • 你为什么要在 ListBox 中使用两个按钮?
  • 它只是一种演示,每个列表项都有按钮,因为每个 ListItem 都有按钮。
猜你喜欢
  • 1970-01-01
  • 2020-08-09
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
  • 2014-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多