XAML 使用ReactiveUI与PasswordBox进行双向绑定

5ktev3wc  于 2023-09-28  发布在  React
关注(0)|答案(3)|浏览(140)

我有一个问题,如何正确地绑定在双向密码在PasswordBox。我目前的解决方案几乎奏效。问题是,在打字时光标停留在开始,不去与文本在结束。输入12345,我的View Model会收到值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不能从视图绑定到视图模型,我需要使用事件。

bjp0bcyl

bjp0bcyl1#

我不认为有一个'官方'的方式来做到这一点,我不知道如果你应该。
但您可以在设置值后手动设置光标位置来解决此问题(source)。

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

“hack”绑定:

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

yfwxisqw2#

我最近遇到了类似的问题,下一个解决方案对我来说效果很好。也许能帮到别人。

ViewModel.WhenAnyValue(x => x.Password)
    .Where(password => string.Equals(password, PasswordBox.Password, StringComparison.CurrentCulture) is false)
    .ObserveOn(RxApp.MainThreadScheduler)
    .Subscribe(password => PasswordBox.Password = password)
    .DisposeWith(disposableRegistration);

Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(
        h => PasswordBox.PasswordChanged += h,
        h => PasswordBox.PasswordChanged -= h)
    .Select(_ => PasswordBox.Password)
    .Where(password => string.Equals(password, ViewModel.Password, StringComparison.CurrentCulture) is false)
    .ObserveOn(RxApp.MainThreadScheduler)
    .Subscribe(password => ViewModel.Password = password)
    .DisposeWith(disposableRegistration);
dba5bblo

dba5bblo3#

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中找到示例应用程序。

相关问题