在 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 中的步骤它起作用了。