自定义视图模型绑定中的自定义事件
Custom event in custom view model binding
我到处搜索。我的问题是,我找不到解决我这个非常简单的问题的方法。
我有一个页面,其中包含一个数据网格。目标是使数据网格中的名称在双击时可编辑。数据是从数据库中检索的。我只想在编辑过程完成时而不是在“PropertyChanged”上更新“名称”属性。
我的自定义控件:
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Ey.Ventuz.SessionManager.Ui
{
/// <summary>
/// Interaktionslogik für TextEditInPlace.xaml
/// </summary>
public partial class TextEditInPlace : UserControl, INotifyPropertyChanged
{
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<string> NameChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private bool isEditing;
/// <summary>
/// Is true while editing
/// </summary>
public bool IsEditing
{
get { return isEditing; }
set
{
isEditing = value;
OnPropertyChanged(nameof(IsEditing));
if(isEditing == false)
{
NameChanged?.Invoke(this, EditableText);
}
}
}
public string EditableText
{
get { return (string)GetValue(EditableTextProperty); }
set { SetValue(EditableTextProperty, value); }
}
// Using a DependencyProperty as the backing store for EditableText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EditableTextProperty =
DependencyProperty.Register("EditableText", typeof(string), typeof(TextEditInPlace), new PropertyMetadata("test"));
public TextEditInPlace()
{
InitializeComponent();
}
private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
IsEditing = true;
}
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
IsEditing = false;
}
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
IsEditing = false;
}
}
}
我的数据网格:
<DataGrid Name="myDataGrid"
ItemsSource="{Binding ItemList, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedStyle}"
RowHeaderWidth="100"
AutoGenerateColumns="False"
HorizontalAlignment="Stretch"
HeadersVisibility="Column"
SelectionMode="Single"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Thumbnail" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Path=Logo}"
Width="100"
Stretch="Uniform"
HorizontalAlignment="Left"
Margin="3"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Name" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:TextEditInPlace EditableText="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="NameChanged">
<i:InvokeCommandAction Command="{Binding UpdateListItemNameCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local:TextEditInPlace>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding Path=Created, StringFormat=\{0:dd.MM.yyyy \}}" Header="Date" Width="*"/>
<DataGridTextColumn Binding="{Binding Path=CreatedBy}" Header="Author" Width="*"/>
</DataGrid.Columns>
</DataGrid>
我的视图模型:
using Ey.Ventuz.SessionManager.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;
namespace Ey.Ventuz.SessionManager.Ui
{
public class StyleConfigurationViewModel : BaseViewModel
{
public List<VentuzStyle> ventuzStyles;
public ICollectionView ItemList { get; set; }
public DefaultStyleData StyleData { get; set; }
private VentuzStyle selectedStyle;
public VentuzStyle SelectedStyle
{
get { return selectedStyle; }
set
{
if (value != null)
{
selectedStyle = value;
}
}
}
public ICommand UpdateListItemNameCommand { get; set; }
public ICommand GoBackCommand { get; set; }
public ICommand DuplicateStyleCommand { get; set; }
public ICommand RemoveStyleCommand { get; set; }
public StyleConfigurationViewModel()
{
InitializeProperties();
FillList();
GoBackCommand = new RelayCommand(() => GoBack());
DuplicateStyleCommand = new RelayCommand(() => DuplicateStyle());
RemoveStyleCommand = new RelayCommand(() => RemoveStyle());
UpdateListItemNameCommand = new RelayCommand(() => UpdateListItemName());
}
private void UpdateListItemName()
{
}
private void InitializeProperties()
{
ventuzStyles = new List<VentuzStyle>(SessionSelectionData.GetVentuzStyles());
if (ventuzStyles != null && ventuzStyles.Count > 0)
{
try
{
foreach (VentuzStyle ventuzStyle in ventuzStyles)
{
ventuzStyle.PropertyChanged += VentuzStyle_PropertyChanged;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
private void VentuzStyle_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
private void FillList()
{
ItemList = new CollectionViewSource() { Source = ventuzStyles }.View;
}
private void GoBack()
{
AggregatorLight.GoBack("go back");
}
private void DuplicateStyle()
{
VentuzStyle _ventuzStyle = new VentuzStyle();
_ventuzStyle = ObjectCopier.DeepCopy(SelectedStyle);
ventuzStyles.Add(SessionSelectionData.CreateStyle(_ventuzStyle));
ItemList.Refresh();
}
private void RemoveStyle()
{
if(ventuzStyles.Count() > 0)
{
SessionSelectionData.RemoveStyle(SelectedStyle);
ventuzStyles.Remove(SelectedStyle);
}
ItemList.Refresh();
}
}
}
如何在自定义用户控件中创建自定义事件?我如何在 XAML 中使用它?我很高兴收到任何评论。
非常感谢
更新:这是 TextEditInPlace 的 Xaml:
<Grid Height="Auto" Width="Auto">
<TextBlock Text="{Binding EditableText, ElementName=userControl}"
Visibility="{Binding IsEditing, ElementName=userControl, Converter={local:BooleanToInvisibilityConverter}}"
MouseLeftButtonDown="TextBlock_MouseLeftButtonDown" />
<TextBox Text="{Binding EditableText, ElementName=userControl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding IsEditing, ElementName=userControl, Converter={local:BooleanToVisibilityConverter}}"
Style="{StaticResource TextEditBox}" Margin="-4"
KeyDown="TextBox_KeyDown"
LostFocus="TextBox_LostFocus" />
</Grid>
首先,一般来说没有必要在Control
或DependencyObject
上实现INotifyPropertyChanged
。建议将所有用作绑定目标的属性实现为依赖属性(就像您所做的那样)。依赖属性提供更好的性能。
第二个假设目标 " 使数据网格中的名称在双击时可编辑" 是 DataGrid
文本单元格的默认行为。 TextEditInPlace
自定义控件绝对是多余的。它只会增加复杂性,而不会带来额外的好处。
一般来说,一个栏目由两个模板组成:一个CellTemplate
和一个CellEditingTemplate
。当单元格转换到编辑模式时,例如通过双击该单元格,然后加载 CellEditingTemplate
。编辑过程中的数据只有在用户退出编辑模式时才会提交。这是将数据发送回绑定源的时刻。因此,编辑的控件没有必要跟踪单元格的状态或创建自定义状态。由于 CellTemplate
的目的是显示值,因此一个简单的 TextBlock
作为内容宿主就足够了。
此外,Bindiiung.UpdateSourceTrigger
默认设置为 UpdateSourceTrigger.PropertyChanged
,除非 Binding.Target
明确指定不同的触发器,例如 TextBox.Text
或 Selector.SelectedItem
,默认为 TwoWay
。
因此,您可以保留默认触发器的分配并缩短绑定表达式以提高可读性。
如果您仍想使用自定义文本编辑控件,您的模板应如下所示。请注意,为了让您的控件执行命令,只需实现 ICommandSource
:
TextEditInPlace.xaml.cs
public partial class TextEditInPlace : UserControl, ICommandSource
{
public static readonly DependencyProperty EditableTextProperty = DependencyProperty.Register(
"EditableText",
typeof(ICommand),
typeof(TextEditInPlace),
new FrameworkPropertyMetadata(
"test",
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnEditTextChanged));
new PropertyMetadata("test", OnEditableTextChanged));
public string EditableText
{
get { return (string)GetValue(EditableTextProperty); }
set { SetValue(EditableTextProperty, value); }
}
#region Implementation of ICommandSource
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(TextEditInPlace),
new PropertyMetadata(default(ICommand)));
public ICommand Command
{
get => (ICommand)GetValue(SectionNavigator.CommandProperty);
set => SetValue(SectionNavigator.CommandProperty, value);
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(TextEditInPlace),
new PropertyMetadata(default(object)));
public object CommandParameter
{
get => (object)GetValue(SectionNavigator.CommandParameterProperty);
set => SetValue(SectionNavigator.CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
"CommandTarget",
typeof(IInputElement),
typeof(TextEditInPlace),
new PropertyMetadata(default(IInputElement)));
public IInputElement CommandTarget
{
get => (IInputElement)GetValue(SectionNavigator.CommandTargetProperty);
set => SetValue(SectionNavigator.CommandTargetProperty, value);
}
#endregion
public TextEditInPlace()
{
InitializeComponent();
}
private static void OnEditTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var _this = d as TextEditInPlace;
if (e.OldValue != null
&& e.NewValue != e.OldValue
&& _this.Command?.CanExecute(_this.CommandParameter) ?? false)
{
_this.Command?.Execute(_this.CommandParameter);
}
}
}
DataGrid
列模板
<DataGridTemplateColumn Header="Name" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditTemplate>
<DataTemplate>
<local:TextEditInPlace EditableText="{Binding Name}"
Command="{Binding UpdateListItemNameCommand}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditTemplate>
</DataGridTemplateColumn>
我到处搜索。我的问题是,我找不到解决我这个非常简单的问题的方法。 我有一个页面,其中包含一个数据网格。目标是使数据网格中的名称在双击时可编辑。数据是从数据库中检索的。我只想在编辑过程完成时而不是在“PropertyChanged”上更新“名称”属性。
我的自定义控件:
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Ey.Ventuz.SessionManager.Ui
{
/// <summary>
/// Interaktionslogik für TextEditInPlace.xaml
/// </summary>
public partial class TextEditInPlace : UserControl, INotifyPropertyChanged
{
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<string> NameChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private bool isEditing;
/// <summary>
/// Is true while editing
/// </summary>
public bool IsEditing
{
get { return isEditing; }
set
{
isEditing = value;
OnPropertyChanged(nameof(IsEditing));
if(isEditing == false)
{
NameChanged?.Invoke(this, EditableText);
}
}
}
public string EditableText
{
get { return (string)GetValue(EditableTextProperty); }
set { SetValue(EditableTextProperty, value); }
}
// Using a DependencyProperty as the backing store for EditableText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EditableTextProperty =
DependencyProperty.Register("EditableText", typeof(string), typeof(TextEditInPlace), new PropertyMetadata("test"));
public TextEditInPlace()
{
InitializeComponent();
}
private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
IsEditing = true;
}
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
IsEditing = false;
}
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
IsEditing = false;
}
}
}
我的数据网格:
<DataGrid Name="myDataGrid"
ItemsSource="{Binding ItemList, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedStyle}"
RowHeaderWidth="100"
AutoGenerateColumns="False"
HorizontalAlignment="Stretch"
HeadersVisibility="Column"
SelectionMode="Single"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Thumbnail" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Path=Logo}"
Width="100"
Stretch="Uniform"
HorizontalAlignment="Left"
Margin="3"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Name" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:TextEditInPlace EditableText="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="NameChanged">
<i:InvokeCommandAction Command="{Binding UpdateListItemNameCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local:TextEditInPlace>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding Path=Created, StringFormat=\{0:dd.MM.yyyy \}}" Header="Date" Width="*"/>
<DataGridTextColumn Binding="{Binding Path=CreatedBy}" Header="Author" Width="*"/>
</DataGrid.Columns>
</DataGrid>
我的视图模型:
using Ey.Ventuz.SessionManager.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;
namespace Ey.Ventuz.SessionManager.Ui
{
public class StyleConfigurationViewModel : BaseViewModel
{
public List<VentuzStyle> ventuzStyles;
public ICollectionView ItemList { get; set; }
public DefaultStyleData StyleData { get; set; }
private VentuzStyle selectedStyle;
public VentuzStyle SelectedStyle
{
get { return selectedStyle; }
set
{
if (value != null)
{
selectedStyle = value;
}
}
}
public ICommand UpdateListItemNameCommand { get; set; }
public ICommand GoBackCommand { get; set; }
public ICommand DuplicateStyleCommand { get; set; }
public ICommand RemoveStyleCommand { get; set; }
public StyleConfigurationViewModel()
{
InitializeProperties();
FillList();
GoBackCommand = new RelayCommand(() => GoBack());
DuplicateStyleCommand = new RelayCommand(() => DuplicateStyle());
RemoveStyleCommand = new RelayCommand(() => RemoveStyle());
UpdateListItemNameCommand = new RelayCommand(() => UpdateListItemName());
}
private void UpdateListItemName()
{
}
private void InitializeProperties()
{
ventuzStyles = new List<VentuzStyle>(SessionSelectionData.GetVentuzStyles());
if (ventuzStyles != null && ventuzStyles.Count > 0)
{
try
{
foreach (VentuzStyle ventuzStyle in ventuzStyles)
{
ventuzStyle.PropertyChanged += VentuzStyle_PropertyChanged;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
private void VentuzStyle_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
private void FillList()
{
ItemList = new CollectionViewSource() { Source = ventuzStyles }.View;
}
private void GoBack()
{
AggregatorLight.GoBack("go back");
}
private void DuplicateStyle()
{
VentuzStyle _ventuzStyle = new VentuzStyle();
_ventuzStyle = ObjectCopier.DeepCopy(SelectedStyle);
ventuzStyles.Add(SessionSelectionData.CreateStyle(_ventuzStyle));
ItemList.Refresh();
}
private void RemoveStyle()
{
if(ventuzStyles.Count() > 0)
{
SessionSelectionData.RemoveStyle(SelectedStyle);
ventuzStyles.Remove(SelectedStyle);
}
ItemList.Refresh();
}
}
}
如何在自定义用户控件中创建自定义事件?我如何在 XAML 中使用它?我很高兴收到任何评论。
非常感谢
更新:这是 TextEditInPlace 的 Xaml:
<Grid Height="Auto" Width="Auto">
<TextBlock Text="{Binding EditableText, ElementName=userControl}"
Visibility="{Binding IsEditing, ElementName=userControl, Converter={local:BooleanToInvisibilityConverter}}"
MouseLeftButtonDown="TextBlock_MouseLeftButtonDown" />
<TextBox Text="{Binding EditableText, ElementName=userControl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding IsEditing, ElementName=userControl, Converter={local:BooleanToVisibilityConverter}}"
Style="{StaticResource TextEditBox}" Margin="-4"
KeyDown="TextBox_KeyDown"
LostFocus="TextBox_LostFocus" />
</Grid>
首先,一般来说没有必要在Control
或DependencyObject
上实现INotifyPropertyChanged
。建议将所有用作绑定目标的属性实现为依赖属性(就像您所做的那样)。依赖属性提供更好的性能。
第二个假设目标 " 使数据网格中的名称在双击时可编辑" 是 DataGrid
文本单元格的默认行为。 TextEditInPlace
自定义控件绝对是多余的。它只会增加复杂性,而不会带来额外的好处。
一般来说,一个栏目由两个模板组成:一个CellTemplate
和一个CellEditingTemplate
。当单元格转换到编辑模式时,例如通过双击该单元格,然后加载 CellEditingTemplate
。编辑过程中的数据只有在用户退出编辑模式时才会提交。这是将数据发送回绑定源的时刻。因此,编辑的控件没有必要跟踪单元格的状态或创建自定义状态。由于 CellTemplate
的目的是显示值,因此一个简单的 TextBlock
作为内容宿主就足够了。
此外,Bindiiung.UpdateSourceTrigger
默认设置为 UpdateSourceTrigger.PropertyChanged
,除非 Binding.Target
明确指定不同的触发器,例如 TextBox.Text
或 Selector.SelectedItem
,默认为 TwoWay
。
因此,您可以保留默认触发器的分配并缩短绑定表达式以提高可读性。
如果您仍想使用自定义文本编辑控件,您的模板应如下所示。请注意,为了让您的控件执行命令,只需实现 ICommandSource
:
TextEditInPlace.xaml.cs
public partial class TextEditInPlace : UserControl, ICommandSource
{
public static readonly DependencyProperty EditableTextProperty = DependencyProperty.Register(
"EditableText",
typeof(ICommand),
typeof(TextEditInPlace),
new FrameworkPropertyMetadata(
"test",
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnEditTextChanged));
new PropertyMetadata("test", OnEditableTextChanged));
public string EditableText
{
get { return (string)GetValue(EditableTextProperty); }
set { SetValue(EditableTextProperty, value); }
}
#region Implementation of ICommandSource
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(TextEditInPlace),
new PropertyMetadata(default(ICommand)));
public ICommand Command
{
get => (ICommand)GetValue(SectionNavigator.CommandProperty);
set => SetValue(SectionNavigator.CommandProperty, value);
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(TextEditInPlace),
new PropertyMetadata(default(object)));
public object CommandParameter
{
get => (object)GetValue(SectionNavigator.CommandParameterProperty);
set => SetValue(SectionNavigator.CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
"CommandTarget",
typeof(IInputElement),
typeof(TextEditInPlace),
new PropertyMetadata(default(IInputElement)));
public IInputElement CommandTarget
{
get => (IInputElement)GetValue(SectionNavigator.CommandTargetProperty);
set => SetValue(SectionNavigator.CommandTargetProperty, value);
}
#endregion
public TextEditInPlace()
{
InitializeComponent();
}
private static void OnEditTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var _this = d as TextEditInPlace;
if (e.OldValue != null
&& e.NewValue != e.OldValue
&& _this.Command?.CanExecute(_this.CommandParameter) ?? false)
{
_this.Command?.Execute(_this.CommandParameter);
}
}
}
DataGrid
列模板
<DataGridTemplateColumn Header="Name" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditTemplate>
<DataTemplate>
<local:TextEditInPlace EditableText="{Binding Name}"
Command="{Binding UpdateListItemNameCommand}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditTemplate>
</DataGridTemplateColumn>