2016-07-22 53 views
3

我的应用程序具有定期数据库同步功能。每当发生同步时,所有输入控件的值都会重置为数据库中的当前值。如何禁用焦点输入控件上的NotifyPropertyChanged事件?

但是,当在TextBox中输入长文本时发生同步事件时,这不仅不方便。

期望的行为是,当控件当前具有焦点时,输入控件的值未设置为绑定属性的值。只要键盘焦点丢失,当前值应该同步回绑定的属性,从而到数据库(这将是默认行为)。

我的第一个想法是修改我的控件,使绑定模式自动设置为OneWayToSource而输入具有键盘焦点。 目前我没有看到另一个选项,而是得到我在我的应用程序中的各种输入控件,这将是很多工作。

您是否看到在中央位置实现这种行为的方法,以便它可以被所有UI控件使用,最好不需要继承子类TextBox,ComboBox等等?

+0

目前还不清楚,期望的行为是什么。你想跳过集中控制还是想跳过绑定属性的更新?如果它的值被改变了?下一个同步阶段应该发生什么情况,使用滑动的值? – Dennis

+0

我添加了一段来指定所需的行为=) – Andre

+0

这是一个有趣的问题。当用户正在编辑并且您需要更新该字段时,您会做什么?用户总是赢吗?这听起来像你需要一个自定义的绑定实现,监听用户输入并阻止来自源的更新... – Will

回答

2

您可以创建一个帮助程序,该帮助程序将跟踪IsKeyboardFocusWithin属性,并仅在目标值不包含键盘焦点时更新目标值。 在例子中,你必须改变每秒具体来源:

public partial class MainWindow { 
    public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
     "Data", typeof(string), typeof(MainWindow), new PropertyMetadata(default(string), OnDataChanged)); 

    static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { 
     Debug.WriteLine((string)e.NewValue); 
    } 

    public string Data { 
     get { return (string)GetValue(DataProperty); } 
     set { SetValue(DataProperty, value); } 
    } 
    public MainWindow() { 
     InitializeComponent(); 
     DispatcherTimer dt = new DispatcherTimer(); 
     dt.Interval = TimeSpan.FromMilliseconds(1000); 
     dt.Tick += Dt_Tick; 
     dt.Start(); 
    } 

    void Dt_Tick(object sender, EventArgs e) { 
     Data = new Random().Next(0, 100).ToString(); 
    } 
} 

在这种情况下,这是数据属性。现在,帮手。我决定将其创建为一个的MarkupExtension简化XAML:

public class SynchronizationHelperExtension : MarkupExtension { 
    public Binding Binding { get; set; } 
    class Helper {    
     static int index = 0; 
     bool locked = false; 
     public static readonly DependencyProperty HelperProperty = DependencyProperty.RegisterAttached(
      "Helper", typeof(Helper), typeof(Helper), new PropertyMetadata(default(Helper))); 

     public static void SetHelper(DependencyObject element, Helper value) { 
      element.SetValue(HelperProperty, value); 
     } 

     public static Helper GetHelper(DependencyObject element) { 
      return (Helper)element.GetValue(HelperProperty); 
     } 
     public static readonly DependencyProperty FocusProperty = DependencyProperty.RegisterAttached("FocusProperty", typeof(bool), typeof(Helper), new PropertyMetadata(false, (o, args) => GetHelper(o)?.OnFocusPropertyChanged(o, (bool)args.OldValue, (bool)args.NewValue)));    
     public static readonly DependencyProperty SourceProperty = DependencyProperty.RegisterAttached("SourceProperty", typeof(object), typeof(Helper), new PropertyMetadata(false, (o, args) => GetHelper(o)?.OnSourcePropertyChanged(o, args.OldValue, args.NewValue))); 
     public static readonly DependencyProperty TargetProperty = DependencyProperty.RegisterAttached("TargetProperty", typeof(object), typeof(Helper), new PropertyMetadata(false, (o, args) => GetHelper(o)?.OnTargetPropertyChanged(o, args.OldValue, args.NewValue))); 

     void OnTargetPropertyChanged(DependencyObject o, object oldValue, object newValue) { 
      o.SetValue(SourceProperty, newValue); 
     } 
     void OnSourcePropertyChanged(DependencyObject o, object oldValue, object newValue) { 
      if (locked) 
       return; 
      o.SetValue(TargetProperty, newValue); 
     } 
     void OnFocusPropertyChanged(DependencyObject o, bool oldValue, bool newValue) { 
      locked = newValue; 
     } 
    } 

    public override object ProvideValue(IServiceProvider serviceProvider) { 
     var helper = new Helper(); 
     var ipwt = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; 
     var dObj = ipwt.TargetObject as DependencyObject; 
     BindingOperations.SetBinding(dObj, Helper.FocusProperty, new Binding() {Path = new PropertyPath(FrameworkElement.IsKeyboardFocusWithinProperty), RelativeSource = RelativeSource.Self}); 
     Binding.Mode = BindingMode.TwoWay; 
     BindingOperations.SetBinding(dObj, Helper.SourceProperty, Binding); 
     Helper.SetHelper(dObj, helper); 
     return new Binding() {Path = new PropertyPath(Helper.TargetProperty), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.LostFocus, RelativeSource = RelativeSource.Self }.ProvideValue(serviceProvider); 
    } 
} 

我们有3个属性: 一个存储从源实际值(Helper.SourceProperty) 第二 - 存储目标值(Helper.TargetProperty) 和用来将第三锁定这些特性(Helper.FocusProperty) 另外,我们有Binding属性之间的同步 - 这是绑定你用你的目标属性与来源(例如TextBox.TextMainWindow.Data) 在ProvideValue方法结合我们:

  1. 结合FrameworkElement.IsKeyboardFocusWithinPropertyHelper.FocusProperty锁定updets
  2. 更新原始的结合,使其双向(更新变更后的Data属性)
  3. 返回BindingExpression到Helper.TargetProperty

的XAML将是这样的:

<StackPanel> 
    <TextBox Text="{local:SynchronizationHelper Binding={Binding Data, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}}"/> 
    <TextBox Text="{local:SynchronizationHelper Binding={Binding Data, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}}"/> 
    <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=Data}"></TextBlock> 
</StackPanel> 

And the short video demonstrating the result