C# 将命令绑定到动态创建的按钮
我正在尝试创建一个程序,它允许我根据不同的需求选择一个程序来启动不同的程序。基本上,我有一个指定名称、路径、图标等的JSON文档,并为每个条目创建一个按钮 我有一个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
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如果我愿意的话