【问题标题】:Custom event in custom view model binding自定义视图模型绑定中的自定义事件
【发布时间】:2020-08-15 11:05:02
【问题描述】:

我已经搜索过很多地方。我的问题是,我无法找到解决我这个非常简单的问题的方法。 我有一个页面,它承载一个数据网格。目标是让数据网格中的名称在双击时可编辑。从数据库中检索数据。我只想在编辑过程完成而不是在“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 中使用它?我对任何 cmets 都很满意。

非常感谢

更新:这是 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>

【问题讨论】:

    标签: wpf events mvvm icommand


    【解决方案1】:

    首先,通常不需要在ControlDependencyObject 上实现INotifyPropertyChanged。建议将所有用作绑定目标的属性实现为依赖属性(就像您所做的那样)。依赖属性提供了更好的性能。

    第二个假设的目标“让数据网格中的名称在双击时可编辑”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",
          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>
    

    【讨论】:

    • 我明白你的意思,我明白你来自哪里。我会将其标记为已回答,您的代码会在每次属性更改时引发事件,即使在输入期间也是如此。仅在完成编辑后,我将如何提高它?谢谢
    • 与代码无关,与实际的编辑控件有关。那么你如何实际编辑文本呢?你没有表现出来。通常你会提供一个TextBox 作为文本编辑控件。您应该将EditText 绑定到TextBox。您将看到OnEditTextChanged 方法被调用了两次:在编辑开始和命令完成时。我添加了一个检查:if (e.OldValue == null) 忽略编辑开始,仅在编辑完成时调用命令。
    猜你喜欢
    • 1970-01-01
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-25
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    相关资源
    最近更新 更多