简体   繁体   English

如何在动态生成按钮时检索哪个按钮被单击

[英]How to retrieve which Button has been Clicked when Its Generated Dynamically

Alright I am a C++ developer and currently I am working on WPF app and looks like this is a tricky situation. 好吧,我是一名C ++开发人员,目前我正在研究WPF应用程序,看起来这是一个棘手的情况。 I have generated set of buttons, labels etc dynamically where both the textbox and button are bound to each other. 我动态地生成了一组按钮,标签等文本框和按钮彼此绑定。 I had done this in C++ code earlier and now I need to do it in WPF app. 我之前用C ++代码完成了这个,现在我需要在WPF应用程序中完成它。

XAML: 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>

ViewModel: 视图模型:

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);
}

Thus it generates Button + Textbox + Label 3 times as shown in Initialize() method. 因此,它生成Button + Textbox + Label 3次,如Initialize()方法所示。 Now VoltageCommand = m_voltageCommand gives me the text entered in textbox and it calls SetCommandExecute(object voltageText) method where voltageText gives me the value entered. 现在, VoltageCommand = m_voltageCommand为我提供了在文本框中输入的文本,它调用了SetCommandExecute(object voltageText)方法,其中voltageText给出了输入的值。

Model: 模型:

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

Here it tells the user which button has been clicked and takes the value of channe l ie if second button is clicked then channel = 2 . 这里它告诉按钮被点击了哪个,并采取的值的用户channe升即,如果点击第二个按钮,然后channel = 2

Here I need to implement the code as written in C++ one. 在这里,我需要实现用C ++编写的代码。 How can I get the channel ie which button has been clicked. 如何获取频道,即单击了哪个按钮。 Have a look at cmd |= (channel & 0xFF); 看看cmd |= (channel & 0xFF); , it used the channel value. ,它使用了channel值。 How can I achieve it in my app? 如何在我的应用程序中实现它?

You can add simply an ID Property to your VoltageBoardChannel Class. 您只需将一个ID属性添加到您的VoltageBoardChannel类。

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

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

Then change your CommandParameter Binding to CommandParameter="{Binding}" instead of CommandParameter="{Binding VoltageText}" you will now receive not only the Text but also the instance of VoltageBoardChannel Class which now holds an ID. 然后将CommandParameter Binding更改为CommandParameter="{Binding}"而不是CommandParameter="{Binding VoltageText}"您现在不仅会收到Text,还会收到现在拥有ID的VoltageBoardChannel Class实例。

In your Command Executemethod 在您的Command Execute方法中

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

Here is the fundamental problem with your implimentation of MVVM: 这是你对MVVM的基本问题:

The ViewModel should never know about the View; ViewModel永远不应该知道View; It should be testable independant of any View elements 它应该可以独立于任何View元素进行测试

In your SetCommandExecute method is expecting to do some work based text sent from the View. 在您的SetCommandExecute方法中,期望从View发送一些基于工作的文本。 If you were to write a unit test for the SetCommandExecute method, using only information from other parts of the ViewModel, what would you pass in? 如果您要为SetCommandExecute方法编写单元测试,仅使用ViewModel其他部分的信息,您会传递什么?

Instead, your SecCommandExecute should read: 相反,您的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
    }
}

Your XML should read: 你的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>

How does this work? 这是如何运作的? Well it comes down to the DataContext. 那么它归结为DataContext。 Since your DataContext for each line in the grid is a VoltageModel, you are able to bind directly to several of its properties. 由于网格中每一行的DataContext都是一个VoltageModel,因此您可以直接绑定到它的多个属性。 Example Text="{Binding VoltageText ...}" 示例Text="{Binding VoltageText ...}"

Each item in the datagrid has an implied DataContext of the object instance for each row. datagrid中的每个项都有一个每行对象实例的隐含DataContext。 Since each row is binding to an instance of VoltageModel, then you can use that fact directly in your code. 由于每一行都绑定到一个VoltageModel实例,因此您可以直接在代码中使用该事实。 The View knows what ViewModel properties and instances it is working with, and can pass back "down" to the ViewModel the particular VoltageModel the user acted on. View知道它正在使用哪些ViewModel属性和实例,并且可以将用户所采取行动的特定VoltageModel“向下”传递回ViewModel。

Reasoning: 推理:

When your command event runs it should pass in the VoltageModel object, which gives you direct access to all of the instance properties. 当您的命令事件运行时,它应传入VoltageModel对象,这使您可以直接访问所有实例属性。 Remember that this is best practice because you want to be able to unit test the SetCommandExecute method without requiring that a view pass in some text, parsing it, finding some control on a View, and all that stuff. 请记住,这是最佳实践,因为您希望能够对SetCommandExecute方法进行单元测试,而无需在某些文本中传递视图,解析它,在View上查找某些控件以及所有这些内容。

In short: The ViewModel should be completely self-contained and able to run all unit test based on data available to just the ViewModel. 简而言之:ViewModel应该是完全独立的,并且能够基于ViewModel可用的数据运行所有单元测试。

I've only done a little WPF myself. 我自己只做了一点WPF。 When I bound my data to the control, there was some way for me to uniquely distinguish that data item from other data items. 当我将数据绑定到控件时,我可以通过某种方式将该数据项与其他数据项区分开来。

pseudo code: 伪代码:

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

I don't know if that will work for your case, but that was how I tackled my problem. 我不知道这是否适合你的情况,但这就是我解决问题的方法。

You're already binding the voltage text box to a property, so there shouldn't be a need to pass that value in as the command parameter. 您已经将电压文本框绑定到属性,因此不需要将该值作为命令参数传递。 Instead, you can specify the source as the Command: 相反,您可以将源指定为命令:

<Button CommandParameter="TheButton" />

And in the implementation of your command handler: 并在您的命令处理程序的实现中:

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

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

Hi Instead of Binding VoltageText to CommandParameter bind the Button to it and you can take VoltageText from ButtonDataContext or By Binding SelectedItem of ListBox to ViewModel Property.I hope this will help. 嗨而不是将VoltageText绑定到CommandParameter将Button绑定到它,你可以从ButtonDataContext中获取VoltageText或者通过将ListBox的SelectedItem绑定到ViewModel Property.I希望这会有所帮助。

<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
}

You can try with one of the above two ways . 您可以尝试使用以上两种方法之一。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM