问题描述
我进行了广泛搜索。我的问题是,我找不到这个非常简单的问题的解决方案。 我有一个页面,其中托管一个数据网格。目的是使数据网格中的名称双击可编辑。从数据库中检索数据。我只想在编辑过程完成时更新“名称”属性,而不要在“ 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>
我的viewmodel:
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,UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding IsEditing,Converter={local:BooleanToVisibilityConverter}}"
Style="{StaticResource TextEditBox}" Margin="-4"
KeyDown="TextBox_KeyDown"
LostFocus="TextBox_LostFocus" />
</Grid>
解决方法
首先,通常不需要在INotifyPropertyChanged
或Control
上实现DependencyObject
。建议将所有用作绑定目标的属性作为依赖项属性来实现(如您所做的那样)。依赖项属性可提供更好的性能。
第二个假定目标“ ”是要双击可编辑的数据网格中的名称”。是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",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),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),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>