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

问题描述

我进行了广泛搜索。我的问题是,我找不到这个非常简单的问题的解决方案。 我有一个页面,其中托管一个数据网格。目的是使数据网格中的名称双击可编辑。从数据库中检索数据。我只想在编辑过程完成时更新“名称属性,而不要在“ 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>

解决方法

首先,通常不需要在INotifyPropertyChangedControl上实现DependencyObject。建议将所有用作绑定目标的属性作为依赖项属性来实现(如您所做的那样)。依赖项属性可提供更好的性能。

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

通常,一列包含两个模板:CellTemplateCellEditingTemplate。当单元格转换到编辑模式时,例如通过双击单元格,将加载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",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>