在 MVVM Light 中为 RelayCommand 动态分配参数
Dynamically assigning a parameter to a RelayCommand in MVVM Light
我已经看到很多使用 MVVM Light 中的 RelayCommand class 通过命令传递参数的示例,但我想要的和我看到的略有不同。
我想创建几个按钮,所有按钮都关联了一个 ModuleType。当他们的动作被执行时,我想知道它是哪个 ModuleType。而且我想以代码高效的方式执行此操作,因此不必手动创建 RelayCommand,而是在 foreach 循环中执行所有操作,这也是因为我不知道在开始时必须创建多少个按钮。
这是代码。在我的 ViewModel
public ModuleSelectionViewModel(MachineStatusModel model, int order, List<ModuleType> modules) : base(model)
{
........
// create button for each of the modules
foreach (ModuleType mod in modules)
{
_navBarButtons.Add(new NavButton(mod.ToString(), new RelayCommand<ModuleType>(exec => ButtonExecute(mod)), mod));
}
RaisePropertyChanged("NavBarButtons");
}
// Binding to the Model
public ObservableCollection<NavButton> NavBarButtons
{
get { return _navBarButtons; }
}
// Execut Action
public void ButtonExecute(ModuleType mod)
{
WriteToLog("Selected " + mod.ToString());
}
// Support class
public class NavButton
{
public string ButtonContent { get; set; }
public ICommand ButtonCommand { get; set; }
public ModuleType ButtonModuleType;
public NavButton(string content, ICommand relay, ModuleType moduleType)
{
this.ButtonContent = content;
this.ButtonCommand = relay;
this.ButtonModuleType = moduleType;
}
}
我还在学习 lambda 表达式,所以我想我在 RelayCommand 的初始化上做错了什么。
如果您执行 foreach
循环并在 lambda 表达式中使用 循环变量 ,您将捕获它。不幸的是,该变量的作用域不正确(至少在旧版本的 C# 中,这个 changes with C# 5(感谢 Mafii))。
因此您需要执行以下操作:
foreach (ModuleType mod in modules)
{
// New variable that is locally scoped.
var type = mod;
_navBarButtons.Add(new NavButton(mod.ToString(),
new RelayCommand<ModuleType>(param => ButtonExecute(type)), type));
}
关于解决方案H.B。发布:不确定为什么它不能使用 lambda 函数,为什么在按下按钮时 ButtonExecute 没有被执行。而且我也不明白当 foo 为空并传递给函数时定义像 'foo => ButtonExecute(foo)' 这样的 lambda 函数的逻辑。但无论如何...
这就是我所做的并且正在工作:
型号
<ItemsControl Name="NavButtonsItemsControl" ItemsSource="{Binding NavBarButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding ButtonContent}" CommandParameter="{Binding ButtonModuleType}" Command="{Binding ButtonCommand}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ViewModel(检查我最初问题中的其余代码)
.........
_navBarButtons.Add(new NavButton(mod.ToString(), new RelayCommand<ModuleType>(ButtonExecute), type));
.........
private void ButtonExecute(ModuleType state)
{
WriteToLog("Command with parameter " + state.ToString());
}
您可以使用 Object 而不是 ModuleType 来使其更通用。解决方案不是使用 lambda 函数,而是在按钮中定义一个 CommandParameter 绑定,并将该值作为执行函数中的参数。我不认为绑定是明确定义的,这就是为什么我很难理解该值是如何达到 'ButtonExecute',但是按照 Dominik Schmidt tutorial 中的步骤它起作用了。
我已经看到很多使用 MVVM Light 中的 RelayCommand class 通过命令传递参数的示例,但我想要的和我看到的略有不同。
我想创建几个按钮,所有按钮都关联了一个 ModuleType。当他们的动作被执行时,我想知道它是哪个 ModuleType。而且我想以代码高效的方式执行此操作,因此不必手动创建 RelayCommand,而是在 foreach 循环中执行所有操作,这也是因为我不知道在开始时必须创建多少个按钮。
这是代码。在我的 ViewModel
public ModuleSelectionViewModel(MachineStatusModel model, int order, List<ModuleType> modules) : base(model)
{
........
// create button for each of the modules
foreach (ModuleType mod in modules)
{
_navBarButtons.Add(new NavButton(mod.ToString(), new RelayCommand<ModuleType>(exec => ButtonExecute(mod)), mod));
}
RaisePropertyChanged("NavBarButtons");
}
// Binding to the Model
public ObservableCollection<NavButton> NavBarButtons
{
get { return _navBarButtons; }
}
// Execut Action
public void ButtonExecute(ModuleType mod)
{
WriteToLog("Selected " + mod.ToString());
}
// Support class
public class NavButton
{
public string ButtonContent { get; set; }
public ICommand ButtonCommand { get; set; }
public ModuleType ButtonModuleType;
public NavButton(string content, ICommand relay, ModuleType moduleType)
{
this.ButtonContent = content;
this.ButtonCommand = relay;
this.ButtonModuleType = moduleType;
}
}
我还在学习 lambda 表达式,所以我想我在 RelayCommand 的初始化上做错了什么。
如果您执行 foreach
循环并在 lambda 表达式中使用 循环变量 ,您将捕获它。不幸的是,该变量的作用域不正确(至少在旧版本的 C# 中,这个 changes with C# 5(感谢 Mafii))。
因此您需要执行以下操作:
foreach (ModuleType mod in modules)
{
// New variable that is locally scoped.
var type = mod;
_navBarButtons.Add(new NavButton(mod.ToString(),
new RelayCommand<ModuleType>(param => ButtonExecute(type)), type));
}
关于解决方案H.B。发布:不确定为什么它不能使用 lambda 函数,为什么在按下按钮时 ButtonExecute 没有被执行。而且我也不明白当 foo 为空并传递给函数时定义像 'foo => ButtonExecute(foo)' 这样的 lambda 函数的逻辑。但无论如何...
这就是我所做的并且正在工作:
型号
<ItemsControl Name="NavButtonsItemsControl" ItemsSource="{Binding NavBarButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding ButtonContent}" CommandParameter="{Binding ButtonModuleType}" Command="{Binding ButtonCommand}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ViewModel(检查我最初问题中的其余代码)
.........
_navBarButtons.Add(new NavButton(mod.ToString(), new RelayCommand<ModuleType>(ButtonExecute), type));
.........
private void ButtonExecute(ModuleType state)
{
WriteToLog("Command with parameter " + state.ToString());
}
您可以使用 Object 而不是 ModuleType 来使其更通用。解决方案不是使用 lambda 函数,而是在按钮中定义一个 CommandParameter 绑定,并将该值作为执行函数中的参数。我不认为绑定是明确定义的,这就是为什么我很难理解该值是如何达到 'ButtonExecute',但是按照 Dominik Schmidt tutorial 中的步骤它起作用了。