【问题标题】:Dynamically assigning a parameter to a RelayCommand in MVVM Light在 MVVM Light 中为 RelayCommand 动态分配参数
【发布时间】:2016-11-11 01:22:17
【问题描述】:

我已经看到了很多使用 MVVM Light 中的 RelayCommand 类通过命令传递参数的示例,但是我想要的和我看到的之间存在细微差别。

我想创建一些按钮,它们都具有关联的 ModuleType。当他们的动作被执行时,我想知道它是哪个 ModuleType。我想以一种代码高效的方式来做这件事,所以不必手动创建 RelayCommands,而是在 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 的初始化上做错了。

【问题讨论】:

  • 到底是什么问题?
  • @DevNewb:总而言之,我想按下一个按钮并使用参数触发执行功能。每个按钮对该参数都有不同的值
  • 从您的问题中不清楚您提供的代码中究竟有什么不起作用
  • @DevNewb 看看关于答案 H.B. 的讨论。给了。此时这部分不起作用'param => ButtonExecute(type))',ButtonExecute 不会执行。我希望它使用创建时传递的参数执行该函数'... new RelayCommand(param => ButtonExecute(type)) ...'

标签: c# wpf mvvm mvvm-light relaycommand


【解决方案1】:

如果您执行 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));
}

【讨论】:

  • 这是由于闭包原理。它仍然是相关的 AFAIK。
  • 我认为这已在 .net 4.0 中得到修复,但我不确定。它肯定在 .net 4.5 中得到修复
  • @WicherVisser 好的,它已在 c#5 中修复:blogs.msdn.microsoft.com/ericlippert/2009/11/12/…
  • 我猜还有一个细节缺失。如果我使用 'foo => ButtonExecute(foo)' 进行初始化(两边相同的变量,我的 ButtonExecute 函数会被触发,但默认值为 0。但是,如果我确实像你所说的 'exec => ButtonExecute(type)' 那么函数没有被触发,这是为什么呢?
  • @LuisFerreira:Lambda 表达式是函数,左边的任何值都是在函数执行时分配的,例如x =&gt; x * x 将用作输入的任何值平方。您在此处传递的函数将 CommandParameter 作为使用该命令的控件的输入传递给它。因此将相应地分配该参数。顺便说一句,这是CommandParameter 的主要用例,只需将其设置为您的类型,然后您就可以在函数中使用它。
【解决方案2】:

关于解决方案 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 中的步骤它可以工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多