wpf 没有 setter 的属性上的数据绑定

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18043877/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 09:21:56  来源:igfitidea点击:

DataBinding on properties with no setter

c#wpfdata-bindingmvvm

提问by Victor Mukherjee

How can I bind data to a property with only a getter and no setter to access it from view model in wpf? I am working with PasswordBoxand want to bind its SecureStringproperty to a ViewModel property. How can I do that?

如何将数据绑定到只有 getter 而没有 setter 的属性以从 wpf 中的视图模型访问它?我正在使用PasswordBox并希望将其SecureString属性绑定到 ViewModel 属性。我怎样才能做到这一点?

回答by Vyacheslav Volkov

I use this class and the System.Windows.Interactivitylibrary to get access for properties with no setter:

我使用这个类和System.Windows.Interactivity库来访问没有 setter 的属性:

public sealed class PropertyManager : TriggerAction<FrameworkElement>
{
    #region Fields

    private bool _bindingUpdating;
    private PropertyInfo _currentProperty;
    private bool _propertyUpdating;

    #endregion

    #region Dependency properties

    /// <summary>
    ///     Identifies the <see cref="Binding" /> dependency property.
    /// </summary>
    public static readonly DependencyProperty BindingProperty =
        DependencyProperty.Register("Binding", typeof(object), typeof(PropertyManager),
            new PropertyMetadata((o, args) =>
            {
                var propertyManager = o as PropertyManager;
                if (propertyManager == null ||
                    args.OldValue == args.NewValue) return;
                propertyManager.TrySetProperty(args.NewValue);
            }));

    /// <summary>
    ///     Identifies the <see cref="SourceProperty" /> dependency property.
    /// </summary>
    public static readonly DependencyProperty SourcePropertyProperty =
        DependencyProperty.Register("SourceProperty", typeof(string), typeof(PropertyManager),
            new PropertyMetadata(default(string)));

    /// <summary>
    ///     Binding for property <see cref="SourceProperty" />.
    /// </summary>
    public object Binding
    {
        get { return GetValue(BindingProperty); }
        set { SetValue(BindingProperty, value); }
    }

    /// <summary>
    ///     Name property to bind.
    /// </summary>
    public string SourceProperty
    {
        get { return (string)GetValue(SourcePropertyProperty); }
        set { SetValue(SourcePropertyProperty, value); }
    }

    #endregion

    #region Methods

    /// <summary>
    ///     Invokes the action.
    /// </summary>
    /// <param name="parameter">
    ///     The parameter to the action. If the action does not require a parameter, the parameter may be
    ///     set to a null reference.
    /// </param>
    protected override void Invoke(object parameter)
    {
        TrySetBinding();
    }

    /// <summary>
    ///     Tries to set binding value.
    /// </summary>
    private void TrySetBinding()
    {
        if (_propertyUpdating) return;
        PropertyInfo propertyInfo = GetPropertyInfo();
        if (propertyInfo == null) return;
        if (!propertyInfo.CanRead)
            return;
        _bindingUpdating = true;
        try
        {
            Binding = propertyInfo.GetValue(AssociatedObject, null);
        }
        finally
        {
            _bindingUpdating = false;
        }
    }

    /// <summary>
    ///     Tries to set property value.
    /// </summary>
    private void TrySetProperty(object value)
    {
        if (_bindingUpdating) return;
        PropertyInfo propertyInfo = GetPropertyInfo();
        if (propertyInfo == null) return;
        if (!propertyInfo.CanWrite)
            return;
        _propertyUpdating = true;
        try
        {
            propertyInfo.SetValue(AssociatedObject, value, null);
        }
        finally
        {
            _propertyUpdating = false;
        }
    }

    private PropertyInfo GetPropertyInfo()
    {
        if (_currentProperty != null && _currentProperty.Name == SourceProperty)
            return _currentProperty;
        if (AssociatedObject == null)
            throw new NullReferenceException("AssociatedObject is null.");
        if (string.IsNullOrEmpty(SourceProperty))
            throw new NullReferenceException("SourceProperty is null.");
        _currentProperty = AssociatedObject
            .GetType()
            .GetProperty(SourceProperty);
        if (_currentProperty == null)
            throw new NullReferenceException("Property not found in associated object, property name: " +
                                                SourceProperty);
        return _currentProperty;
    }

    #endregion
}

To use this class in XAML you need to add a reference to System.Windows.Interactivity library and add this namespaces:

要在 XAML 中使用此类,您需要添加对 System.Windows.Interactivity 库的引用并添加以下命名空间:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behaviors="clr-namespace:YOUR NAMESPACE WHERE YOU PUT THE PropertyManager CLASS"

You also need to specify the event that will be updated value, in this case PasswordChangedand specify a property that you want to bind, in this case Password:

您还需要指定将更新值的事件,在本例中,PasswordChanged并指定要绑定的属性,在本例中Password

<PasswordBox>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="PasswordChanged">
            <behaviors:PropertyManager
                Binding="{Binding Path=MyPasswordProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                SourceProperty="Password" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</PasswordBox>

This class is versatile and can work with any of the properties also supports two-way binding.

这个类是通用的,可以与任何属性一起使用,还支持双向绑定。

回答by Rohit Vats

You can bind with Get only propertyby setting the binding modeto OneWayon your binding -

您可以Get only property通过将绑定模式设置为绑定OneWay绑定 -

<PasswordBox Text="{Binding SecureString, Mode=OneWay}"

回答by denis morozov

bind in xaml:

在 xaml 中绑定:

<PasswordBox Text="{Binding SecureString, Mode=OneWay}"...

if you don't want it changed from xaml binding

如果您不希望它从 xaml 绑定更改

public string SecureString
{
   get { return _secureString;}
   private set
   {
      if(_secureString == value) return;
      _secureString = value;
      RaisePropertyChanged(() => SecureString);
   }

 public void SetSecureString(string newSecureString)
 {
     SecureString = newSecureString;
 }

the consumer of your ViewModel should be able to set SecureStringthough that method..

您的 ViewModel 的使用者应该能够SecureString通过该方法设置..