代码之家  ›  专栏  ›  技术社区  ›  Exxoff

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

  •  1
  • Exxoff  · 技术社区  · 8 年前

    我正在尝试创建一个程序,它允许我根据不同的要求选择一个程序来启动不同的程序。基本上,我有一个指定名称、路径、图标等的JSON文档,并为每个条目创建了一个按钮。

    我有一个 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> 打电话 Buttons 在我的 ViewModel 并填充为 按钮DEF s

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

    如果我显式创建 继电器命令 对于每个按钮,并在XAML的命令指令中调用它,但由于我不知道将有多少个按钮,因此这不是一个好的解决方案。

    我的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 DoSomething}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    
    </ItemsControl>
    

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

    我怎样才能做到这一点?

    编辑:

     public class MainViewModel : ViewModelBase
    {
    
        public ObservableCollection<ButtonDef> Buttons { get; set; }
    
        public List<DataItem> Items { get; set; }
    
        public RelayCommand DoSomething { get; set; }
    
    
    
    
    
    
        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel(IDataService dataService)
        {
    
    
            Items = new List<DataItem> { new DataItem("Item 1"), new DataItem("Item 2") };
    
            //Items.Add(new DataItem("Item 1"));
    
            if (Buttons == null) Buttons = new ObservableCollection<ButtonDef>();
            foreach (var item in Items)
            {
                Buttons.Add(new ButtonDef
                {
                    Caption = item.Title,
                    Cmd = "Path to exe"
            });
        }
    
    
    }
    
    2 回复  |  直到 8 年前
        1
  •  1
  •   Michael Puckett II    8 年前

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

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

    典型的 Commands 还不需要法定装订 CommandParameters 经常这样做。因此,我将提供一个快速的示例,希望它能为您提供足够的信息来添加所需的任何绑定,包括 Command 如果你想的话。如果 命令 永远不要改变,我强烈建议设置它;这与设置a相同 结合 具有 Mode = OneTime

    这个例子只是展示如何在代码中进行绑定;没有别的了。如果你有 StackPanel (或任何面板) MainWindow 命名为“root”,然后您可以复制并粘贴此代码以使用它。(同时添加必要的using语句)

    这里我提供一个简单的 PersonViewModel ,列出这些人的列表,然后绑定到列表,只添加一个 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;
    }
    
        2
  •  1
  •   Exxoff    8 年前

    虽然@dymanoid提供的链接给了我一些som见解,但它需要更多的调整才能发挥作用。

    首先在 ViewModel 像这样:

    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和 Path 允许访问窗口的DataContext。

    解决方案的两个环节:

    WPF Command Parameter for button inside ItemsControl

    Pass different commandparameters to same command using RelayCommand WPF