Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将命令绑定到动态创建的按钮_C#_Wpf_Mvvm_Data Binding_Icommand - Fatal编程技术网

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

C# 将命令绑定到动态创建的按钮,c#,wpf,mvvm,data-binding,icommand,C#,Wpf,Mvvm,Data Binding,Icommand,我正在尝试创建一个程序,它允许我根据不同的需求选择一个程序来启动不同的程序。基本上,我有一个指定名称、路径、图标等的JSON文档,并为每个条目创建一个按钮 我有一个ButtonDef类,看起来像这样: public class ButtonDef { public int Id { get; set; } public string Caption { get; set; } public string Cmd { get; set; } public strin

我正在尝试创建一个程序,它允许我根据不同的需求选择一个程序来启动不同的程序。基本上,我有一个指定名称、路径、图标等的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; }
}
<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 RelayCommand<object> DoSomething {get; set;}
我创建了一个名为
Buttons
ObservableCollection
,它是我的
ViewModel
中的公共属性,并用
ButtonDef
s填充is

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

如果我为每个按钮显式地创建一个
RelayCommand
,并在XAML的命令指令中调用它,那么一切都会正常工作,但因为我不知道有多少按钮,所以这不是一个好的解决方案

我的XAML如下所示:

public class ButtonDef
{
    public int Id { get; set; }
    public string Caption { get; set; }
    public string Cmd { get; set; }
    public string Icon { get; set; }
}
<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 RelayCommand<object> DoSomething {get; set;}

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

我怎样才能做到这一点

编辑:

public类MainViewModel:ViewModelBase
{
公共可观察收集按钮{get;set;}
公共列表项{get;set;}
公共中继命令DoSomething{get;set;}
/// 
///初始化MainViewModel类的新实例。
/// 
公共主视图模型(IDataService数据服务)
{
项目=新列表{新数据项目(“项目1”)、新数据项目(“项目2”)};
//添加(新数据项(“第1项”);
如果(Buttons==null)Buttons=newobserveCollection();
foreach(项目中的var项目)
{
按钮。添加(新按钮)
{
标题=项目。标题,
Cmd=“exe的路径”
});
}
}

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

您需要创建一个
Binding
对象,然后使用
BindingOperations
应用绑定

通常,
命令
不需要通知绑定,但是,
命令参数
通常需要。因此,我将提供一个简单的示例,希望能为您提供足够的信息来添加所需的任何绑定,包括
命令
(如果您愿意)。如果
命令
从未更改,我强烈建议您建议只设置它;这与使用
Mode=OneTime
设置
Binding
相同

此示例仅演示如何在代码中进行绑定,而不演示其他操作。如果在
主窗口中有一个名为“root”的
StackPanel
(或任何面板),则可以复制并粘贴此代码以使用它。(还可以添加必要的using语句)

在这里,我提供了一个简单的
PersonViewModel
,列出这些人(People),然后绑定到列表,在本例中只添加一个
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;
}
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
初始化();
}
公共无效初始化()
{
var-id=0;
var people=新列表()
{
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按钮=新按钮()
{
Content=person.Name,
Command=person.UpdatePersonCommand
};
SetCommandParameterBinding(按钮、人员);
单击+=(s,e)=>MessageBox.Show(button.CommandParameter.ToString());
root.Children.Add(按钮);
}
}
//这就是回答你问题的方法
私有静态BindingExpressionBase SetCommandParameterBinding(ButtonBase按钮,PersonViewModel person)
{
//这将设置绑定PersonViewModel中“Name”属性的绑定
//让构造函数参数emtpy绑定到对象本身,即新绑定(){Source=Person};将绑定到Person
var binding=新绑定(nameof(PersonViewModel.Name)){Source=person};
//这将设置与按钮和按钮命令ParameterProperty的绑定
var bindingExpression=BindingOperations.SetBinding(button,ButtonBase.CommandParameterProperty,binding);
返回绑定表达式;
}
}
//显然,这不是一个完全编写的ViewModel。它只是为了让这个示例工作。INotifyPropertyChanged没有完全实现。它也肯定不属于这个命名空间。
公共类PersonViewModel:INotifyPropertyChanged
{
公共字符串名称{get;set;}
公共int Id{get;set;}
public ICommand UpdatePersonCommand{get;}
公共事件属性更改事件处理程序属性更改;
}

虽然@dymanoid提供的链接让我对som有了深刻的了解,但需要进行更多的调整才能使其正常工作

首先在
视图模型中定义RelayCommand,如下所示:

public class ButtonDef
{
    public int Id { get; set; }
    public string Caption { get; set; }
    public string Cmd { get; set; }
    public string Icon { get; set; }
}
<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 RelayCommand<object> DoSomething {get; set;}
public RelayCommand DoSomething{get;set;}
初始化RelayCommand属性:

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

void ExecuteSomething(object parameter)
{
  // Do your work here
}
DoSomething=newrelaycommand(参数=>ExecuteSomething(参数));
void executeMething(对象参数)
{
//你在这里工作吗
}
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和路径的“DataContext”部分允许访问窗口的DataContext

导致解决方案的两个环节:

这可能会有所帮助:@dymanoid如果我愿意的话