自定义视图模型绑定中的自定义事件

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>

首先,一般来说没有必要在ControlDependencyObject上实现INotifyPropertyChanged。建议将所有用作绑定目标的属性实现为依赖属性(就像您所做的那样)。依赖属性提供更好的性能。

第二个假设目标 " 使数据网格中的名称在双击时可编辑"DataGrid 文本单元格的默认行为。 TextEditInPlace 自定义控件绝对是多余的。它只会增加复杂性,而不会带来额外的好处。

一般来说,一个栏目由两个模板组成:一个CellTemplate和一个CellEditingTemplate。当单元格转换到编辑模式时,例如通过双击该单元格,然后加载 CellEditingTemplate。编辑过程中的数据只有在用户退出编辑模式时才会提交。这是将数据发送回绑定源的时刻。因此,编辑的控件没有必要跟踪单元格的状态或创建自定义状态。由于 CellTemplate 的目的是显示值,因此一个简单的 TextBlock 作为内容宿主就足够了。

此外,Bindiiung.UpdateSourceTrigger 默认设置为 UpdateSourceTrigger.PropertyChanged,除非 Binding.Target 明确指定不同的触发器,例如 TextBox.TextSelector.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>