[英]How to tell which Button has been clicked, when it's generated dynamically? (MVVM)
[英]How to retrieve which Button has been Clicked when Its Generated Dynamically
好吧,我是一名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)
方法,其中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
這里它告訴按鈕被點擊了哪個,並采取的值的用戶channe
升即,如果點擊第二個按鈕,然后channel = 2
。
在這里,我需要實現用C ++編寫的代碼。 如何獲取頻道,即單擊了哪個按鈕。 看看cmd |= (channel & 0xFF);
,它使用了channel
值。 如何在我的應用程序中實現它?
您只需將一個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 Class實例。
在您的Command Execute方法中
public void DoSomethingExecute(object param)
{
VoltageBoardChannel result = param as VoltageBoardChannel;
string value = result.VoltageText;
int index = result.ID;
}
這是你對MVVM的基本問題:
ViewModel永遠不應該知道View; 它應該可以獨立於任何View元素進行測試
在您的SetCommandExecute方法中,期望從View發送一些基於工作的文本。 如果您要為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都是一個VoltageModel,因此您可以直接綁定到它的多個屬性。 示例Text="{Binding VoltageText ...}"
datagrid中的每個項都有一個每行對象實例的隱含DataContext。 由於每一行都綁定到一個VoltageModel實例,因此您可以直接在代碼中使用該事實。 View知道它正在使用哪些ViewModel屬性和實例,並且可以將用戶所采取行動的特定VoltageModel“向下”傳遞回ViewModel。
推理:
當您的命令事件運行時,它應傳入VoltageModel對象,這使您可以直接訪問所有實例屬性。 請記住,這是最佳實踐,因為您希望能夠對SetCommandExecute
方法進行單元測試,而無需在某些文本中傳遞視圖,解析它,在View上查找某些控件以及所有這些內容。
簡而言之:ViewModel應該是完全獨立的,並且能夠基於ViewModel可用的數據運行所有單元測試。
我自己只做了一點WPF。 當我將數據綁定到控件時,我可以通過某種方式將該數據項與其他數據項區分開來。
偽代碼:
private void Button_Clicked(object sender, EventArgs e) {
MyType obj = (MyType)listView1.SelectedItem[0];
if (obj.UniqueItem == whatINeed) {
DoStuffFunction();
}
}
我不知道這是否適合你的情況,但這就是我解決問題的方法。
您已經將電壓文本框綁定到屬性,因此不需要將該值作為命令參數傳遞。 相反,您可以將源指定為命令:
<Button CommandParameter="TheButton" />
並在您的命令處理程序的實現中:
public void SetCommandExecute(object source)
{
string source = source as string;
if (source == "TheButton")
{
int val = Convert.ToInt32(this.VoltageText);
}
}
嗨而不是將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
}
您可以嘗試使用以上兩種方法之一。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.