将命令绑定到动态创建的按钮

时间:2018-05-07 12:20:40

标签: c# wpf mvvm data-binding icommand

我试图创建一个程序,允许我根据不同的要求选择一个程序来启动不同的程序。基本上我有一个JSON文档,指定Name,Path,Icon等,并为每个条目创建一个按钮。

我有一个ButtonDef类看起来像这样:

public class ButtonDef
{
    public int Id { get; set; }
    public string Caption { get; set; }
    public string Cmd { get; set; }
    public string Icon { get; set; }
}

我创建了一个名为ObservableCollection<ButtonDef>的{​​{1}},这是我Buttons中的公共媒体资源,填充的是ViewModel s。

我有一个ButtonDef属性和相应的方法来启动程序。

如果我为每个按钮明确创建一个RelayCommand并在XAML的Command指令中调用它,那么一切都有效,但由于我不知道会有多少个按钮,所以这不是一个好的解决方案。

我的XAML看起来像这样:

RelayCommand

按钮创建正常,标题正确,但命令不会触发。

我怎样才能实现这一目标?

编辑:

<ItemsControl ItemsSource="{Binding Buttons}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
                <Button Content="{Binding Caption}" Height="30" Width="50" Margin="10" Command="{Binding DoSomething}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>

</ItemsControl>

2 个答案:

答案 0 :(得分:1)

虽然@dymanoid提供的链接给了我一些som的见解,但需要做一些调整才能使它有效。

首先在ViewModel中定义RelayCommand,如下所示:

public RelayCommand<object> DoSomething {get; set;}

初始化RelayCommand属性:

DoSomething = new RelayCommand<object>(parameter => ExecuteSomething(parameter));

void ExecuteSomething(object parameter)
{
  // Do your work here
}

<强> XAML

按钮按以下方式声明:

   <ItemsControl ItemsSource="{Binding Buttons}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
                <Button Content="{Binding Caption}" Height="30" Width="50" Margin="10" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}},Path=DataContext.DoSomething}" CommandParameter="{Binding Cmd}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>

</ItemsControl>

其中RelativeSource和&#34; DataContext&#34; Path的一部分允许访问窗口的DataContext。

导致解决方案的两个链接:

WPF Command Parameter for button inside ItemsControl

Pass different commandparameters to same command using RelayCommand WPF

答案 1 :(得分:0)

我不同意这种方法,但我会回答这个问题。

您需要制作一个Binding对象,然后使用BindingOperations来应用绑定。我在下面的代码中提供了一个带注释的示例。

通常Commands CommandParameters通常不需要通知的约束Command。因此,我将提供一个快速示例,我希望能够为您提供足够的信息来添加您需要的任何绑定,包括Command(如果您愿意)。如果Binding永远不会改变,我强烈建议只设置它;这与使用Mode = OneTime设置StackPanel相同。

此示例仅用于说明如何在代码中进行绑定;没有其他的。如果您的MainWindow名为“root”中有PersonViewModel(或任何面板),那么您可以复制并粘贴此代码以进行播放。 (也添加必要的using语句)

在这里,我提供了一个简单的CommandParameterBinding,列出了这些人(人物),然后绑定到列表,仅为此示例添加public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Initialize(); } public void Initialize() { var ids = 0; var people = new List<PersonViewModel>() { new PersonViewModel() { Id = ids++, Name = "Mathew"}, new PersonViewModel() { Id = ids++, Name = "Mark"}, new PersonViewModel() { Id = ids++, Name = "Luke"}, new PersonViewModel() { Id = ids++, Name = "John"} }; foreach (var person in people) { var button = new Button() { Content = person.Name, Command = person.UpdatePersonCommand }; SetCommandParameterBinding(button, person); button.Click += (s, e) => MessageBox.Show(button.CommandParameter.ToString()); root.Children.Add(button); } } //This is the method that answers your question private static BindingExpressionBase SetCommandParameterBinding(ButtonBase button, PersonViewModel person) { //This sets a binding that binds the 'Name' property in PersonViewModel //Leave constructor parameter emtpy to bind to the object itself i.e. new Binding() { Source = Person }; will bind to person var binding = new Binding(nameof(PersonViewModel.Name)) { Source = person }; //This sets the binding to the button and button CommandParameterProperty var bindingExpression = BindingOperations.SetBinding(button, ButtonBase.CommandParameterProperty, binding); return bindingExpression; } } //This isn't a fully written ViewModel obviously. It's just here to make this example work. INotifyPropertyChanged is not completely implemented. It also definitely doesn't belong in this namespace. public class PersonViewModel : INotifyPropertyChanged { public string Name { get; set; } public int Id { get; set; } public ICommand UpdatePersonCommand { get; } public event PropertyChangedEventHandler PropertyChanged; }

Num = Num % 87