C# 维护属性时在WPF ListView中显示按钮的DataTemplate
在WPF中,我有一个包含两列的ListView,第一列需要是一个按钮。如果我错了,请纠正我,但我发现在ListView中实现按钮的唯一方法是使用DataTemplate。我发现的问题是,当使用DataTemplate映射原始按钮属性时,我无法维护它们,因此我不得不使用绑定来重新映射每个单独的属性(包括自定义属性,因为我实际上使用的是从按钮继承的自定义用户控件)。这似乎与手动映射所有属性无关,因此可能有更好的方法自动持久化这些属性 以下是我的测试代码:C# 维护属性时在WPF ListView中显示按钮的DataTemplate,c#,wpf,mvvm,binding,datatemplate,C#,Wpf,Mvvm,Binding,Datatemplate,在WPF中,我有一个包含两列的ListView,第一列需要是一个按钮。如果我错了,请纠正我,但我发现在ListView中实现按钮的唯一方法是使用DataTemplate。我发现的问题是,当使用DataTemplate映射原始按钮属性时,我无法维护它们,因此我不得不使用绑定来重新映射每个单独的属性(包括自定义属性,因为我实际上使用的是从按钮继承的自定义用户控件)。这似乎与手动映射所有属性无关,因此可能有更好的方法自动持久化这些属性 以下是我的测试代码: public MainWindow() {
public MainWindow() {
InitializeComponent();
ObservableCollection<ScreenRequest> screenRequests = new ObservableCollection<ScreenRequest>() {
new ScreenRequest("A", "1"),
new ScreenRequest("B", "2")
};
myListView.ItemsSource = screenRequests;
}
public class ScreenRequest {
public CustomButton ScreenButton { set; get; }
public string Details { set; get; }
public ScreenRequest(string buttonText, string customProperty) {
this.ScreenButton = new CustomButton();
this.ScreenButton.Content = buttonText;
this.ScreenButton.CustomProperty = customProperty;
this.ScreenButton.Click += new RoutedEventHandler(InitiateScreenRequest);
}
private void InitiateScreenRequest(object sender, RoutedEventArgs e) {
CustomButton screenBtn = (CustomButton)sender;
screenBtn.Content = "BUTTON TEXT CHANGED";
}
}
public class CustomButton : Button {
public string CustomProperty { get; set; }
}
public主窗口(){
初始化组件();
ObservableCollection screenRequests=新的ObservableCollection(){
新屏幕请求(“A”、“1”),
新屏幕请求(“B”、“2”)
};
myListView.ItemsSource=屏幕请求;
}
公共类屏蔽请求{
公共自定义按钮屏幕按钮{set;get;}
公共字符串详细信息{set;get;}
公共屏幕请求(字符串buttonText、字符串customProperty){
this.ScreenButton=新建CustomButton();
this.ScreenButton.Content=buttonText;
this.ScreenButton.CustomProperty=CustomProperty;
this.ScreenButton.Click+=新路由EventHandler(InitiateScreenRequest);
}
private void InitiateScreenRequest(对象发送方,RoutedEventArgs e){
CustomButton屏幕BTN=(CustomButton)发送方;
screenBtn.Content=“按钮文本已更改”;
}
}
公共类自定义按钮:按钮{
公共字符串CustomProperty{get;set;}
}
以及XAML:
<Window...
...
<Window.Resources>
<DataTemplate x:Key="ButtonTemplate">
<local:CustomButton Content="{Binding ScreenButton.Content}"/>
</DataTemplate>
</Window.Resources>
<Grid x:Name="grdMain">
...
<ListView...
<ListView.View>
<GridView x:Name="gridView">
<GridViewColumn CellTemplate="{StaticResource ButtonTemplate}" Width="Auto" Header="Screen" HeaderStringFormat="Screen"/>
<GridViewColumn Header="Details" HeaderStringFormat="Details" DisplayMemberBinding="{Binding Details}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
关键部分:命令
类:
//Dead-simple implementation of ICommand
//Serves as an abstraction of Actions performed by the user via interaction with the UI (for instance, Button Click)
public class Command : ICommand
{
public Action Action { get; set; }
public void Execute(object parameter)
{
if (Action != null)
Action();
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action action)
{
Action = action;
}
}
结果:
注意事项:
看看UI与数据和功能的区别。这是WPF的方式。切勿将UI与数据/业务代码混用
ViewModel中的命令
用作按钮的抽象。ViewModel不知道按钮是什么,也不应该知道。如果您需要更多详细信息,请告诉我。有点困惑。您在谈论按钮中的哪些属性,以及在DataTemplate中定义模板和关联样式“一次”之后,该模板和关联样式将被重新用于集合中的每个元素,因此会有什么过激行为。在本例中,我只想维护CustomButton的内容、CustomProperty和单击properties。现在这还不算过分,但我想如果我有10+个属性,并且必须映射它们中的每一个,而不是仅仅有一种“自动绑定”的方法。如果我们被迫手动绑定所有内容,我想我知道如何使用DependencyProperty执行CustomProperty,但不知道如何通过DataTemplate持久化单击事件。谢谢你的评论。伙计,我不知道什么是屏幕
,但是你的数据项
不应该包含UI元素的实例
。也就是说,您不能在ViewModel中放置类似于ScreenButton
的内容。@HighCore+1,如果OP正在这样做的话。我假设他在代码隐藏文件中有三个类,在看到mix@TonyRush我是说你不应该把UI
和数据混为一谈。您的ScreenRequest
类不是从FrameworkElement
派生的,因此我必须假设它是数据
类,而不是UI
类。它不应该包含对从FrameworkElement
派生的任何内容的引用。这太棒了。谁不是通过相关的例子学习得最好?我已经运行了你的代码,但我想在接下来的几个小时里真正了解它是如何工作的。我稍后会再作评论。非常感谢!:)好的,我了解ViewModel类,但还不了解Command类的内部工作原理,希望这会随着时间的推移而到来。对于我正在处理的一个项目,我将您的示例进一步介绍,并在每行中显示一个项目列表,我的列将动态生成以绑定到正确的索引。例如,列1的绑定为列表[0]。Text,列2的绑定为列表[1]。Text。这非常有效,我现在唯一的问题是,当我单击按钮时,它会将我带到包含当前行列表的对象实例中,因此我实际上不知道……单击了哪个列(按钮)。我可以与您分享一个Visual Studio解决方案吗?我想我正在寻找关于我是否走上了正确的道路或者是否需要重新评估我的战略的信息。我希望这是有道理的。@TonyRush这是另一个问题的主题。。。如果你想的话,可以分开贴。。我可以帮你。。
public partial class TonyRush : Window
{
public TonyRush()
{
InitializeComponent();
DataContext = new List<ScreenRequest>
{
new ScreenRequest() {ActionDescription = "Click Me!"},
new ScreenRequest() {ActionDescription = "Click Me Too!"},
new ScreenRequest() {ActionDescription = "Click Me Again!!"},
};
}
}
public class ScreenRequest: INotifyPropertyChanged
{
public Command SomeAction { get; set; }
private string _actionDescription;
public string ActionDescription
{
get { return _actionDescription; }
set
{
_actionDescription = value;
NotifyPropertyChanged("ActionDescription");
}
}
private string _details;
public string Details
{
get { return _details; }
set
{
_details = value;
NotifyPropertyChanged("Details");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ScreenRequest()
{
SomeAction = new Command(ExecuteSomeAction) {IsEnabled = true};
}
//public SomeProperty YourProperty { get; set; }
private void ExecuteSomeAction()
{
//Place your custom logic here based on YourProperty
ActionDescription = "Clicked!!";
Details = "Some Details";
}
}
//Dead-simple implementation of ICommand
//Serves as an abstraction of Actions performed by the user via interaction with the UI (for instance, Button Click)
public class Command : ICommand
{
public Action Action { get; set; }
public void Execute(object parameter)
{
if (Action != null)
Action();
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action action)
{
Action = action;
}
}