【问题标题】:Two way binding with PasswordBox using ReactiveUI使用 ReactiveUI 与 PasswordBox 进行两种方式绑定
【发布时间】:2020-06-10 16:59:39
【问题描述】:

我遇到了如何在PasswordBox 中正确绑定密码的问题。我目前的解决方案几乎可以工作。问题是打字过程中的光标停留在开头,而不是结尾的文本。输入 12345,我的视图模型收到值 54321

我正在使用 WFP 和 XAML。视图后面的代码如下所示:

Observable.FromEventPattern(SecretKey, nameof(SecretKey.PasswordChanged))
    .Subscribe(evt => ViewModel.SecretKey = SecretKey.Password)
    .DisposeWith(disposableRegistration);

this.OneWayBind(ViewModel, viewModel => viewModel.SecretKey, view => view.SecretKey.Password)
    .DisposeWith(disposableRegistration);

当其中一个结构被注释时,光标的行为是正确的,所以问题在于两种方式的绑定,我不知道如何解决它。正如你现在PasswordBox.Password 无法从视图绑定到视图模型,我需要使用事件。

【问题讨论】:

    标签: .net wpf xaml reactiveui


    【解决方案1】:

    我认为没有“官方”的方式可以做到这一点,我不确定你是否应该这样做。
    但是你可以在设置值后手动设置光标位置来解决这个问题(source)。

    private void SetCursor(PasswordBox passwordBox, int index)
    {
        passwordBox.GetType().GetMethod("Select", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(passwordBox, new object[] { index, 0 });
    }
    

    “黑客”绑定:

    this.WhenAnyValue(x => x.ViewModel.SecretKey)
        .Where(pw => pw != null)
        .Do(pw =>
        {
            secretKey.Password = pw;
            SetCursor(secretKey, pw.Length);
        })
        .Subscribe()
        .DisposeWith(disposableRegistration);
    

    【讨论】:

      【解决方案2】:

      PasswordBox 通常由附加属性处理。我进行了研究,ReactiveUi 绑定无法处理带有附加属性的两种方式绑定。另一方面,当您使用 ReactiveUi 时,XAML 中的绑定没有危险信号 - 不鼓励,但不禁止。请记住,纯ReactiveUi 无法实现附加属性,我决定在绑定PasswordBox 时例外,我认为这是合理且正确的。

      我使用了PasswordHelper,它实现了wpftutorial.net上教程中的附加属性

      public static class PasswordHelper
      {
          public static readonly DependencyProperty PasswordProperty =
              DependencyProperty.RegisterAttached("Password",
              typeof(string), typeof(PasswordHelper),
              new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));
       
          public static readonly DependencyProperty AttachProperty =
              DependencyProperty.RegisterAttached("Attach",
              typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false, Attach));
       
          private static readonly DependencyProperty IsUpdatingProperty =
             DependencyProperty.RegisterAttached("IsUpdating", typeof(bool), 
             typeof(PasswordHelper));
       
       
          public static void SetAttach(DependencyObject dp, bool value)
          {
              dp.SetValue(AttachProperty, value);
          }
       
          public static bool GetAttach(DependencyObject dp)
          {
              return (bool)dp.GetValue(AttachProperty);
          }
       
          public static string GetPassword(DependencyObject dp)
          {
              return (string)dp.GetValue(PasswordProperty);
          }
       
          public static void SetPassword(DependencyObject dp, string value)
          {
              dp.SetValue(PasswordProperty, value);
          }
       
          private static bool GetIsUpdating(DependencyObject dp)
          {
              return (bool)dp.GetValue(IsUpdatingProperty);
          }
       
          private static void SetIsUpdating(DependencyObject dp, bool value)
          {
              dp.SetValue(IsUpdatingProperty, value);
          }
       
          private static void OnPasswordPropertyChanged(DependencyObject sender,
              DependencyPropertyChangedEventArgs e)
          {
              PasswordBox passwordBox = sender as PasswordBox;
              passwordBox.PasswordChanged -= PasswordChanged;
       
              if (!(bool)GetIsUpdating(passwordBox))
              {
                  passwordBox.Password = (string)e.NewValue;
              }
              passwordBox.PasswordChanged += PasswordChanged;
          }
       
          private static void Attach(DependencyObject sender,
              DependencyPropertyChangedEventArgs e)
          {
              PasswordBox passwordBox = sender as PasswordBox;
       
              if (passwordBox == null)
                  return;
       
              if ((bool)e.OldValue)
              {
                  passwordBox.PasswordChanged -= PasswordChanged;
              }
       
              if ((bool)e.NewValue)
              {
                  passwordBox.PasswordChanged += PasswordChanged;
              }
          }
       
          private static void PasswordChanged(object sender, RoutedEventArgs e)
          {
              PasswordBox passwordBox = sender as PasswordBox;
              SetIsUpdating(passwordBox, true);
              SetPassword(passwordBox, passwordBox.Password);
              SetIsUpdating(passwordBox, false);
          }
      }
      

      在 XAML 中,我使用带有附加属性的 PasswordBox 的标准绑定:

      <PasswordBox Name="PasswordBox"
                   local:PasswordHelper.Attach="True"
                   local:PasswordHelper.Password="{Binding Password, Mode=TwoWay}"/>
      

      在后面的代码中,我将ViewModel 绑定到DataContext

      this.WhenActivated(d =>
      {
          this.WhenAnyValue(x => x.ViewModel).BindTo(this, x => x.DataContext).DisposeWith(d);
      });
      

      您可以在GitHub repository 中找到示例应用程序。

      【讨论】:

        猜你喜欢
        • 2019-06-27
        • 2017-11-18
        • 1970-01-01
        • 2019-07-13
        • 2013-11-29
        • 2020-02-01
        • 1970-01-01
        • 2016-02-27
        • 2011-04-09
        相关资源
        最近更新 更多