[英]Update XAML control property value from control's code but keep binding
I have written a control with a bindable property. 我用可绑定属性编写了一个控件。 This control also has a method to modify that property's value:
此控件还有一个方法来修改该属性的值:
public class MyControl : ContentView // WPF: inherited from UserControl
{
// Xamarin:
public static readonly BindableProperty MyValueProperty = ...
// WPF:
// public static readonly DependencyProperty MyValueProperty = ...
public int MyValue
{
get { return (int) GetValue(MyValueProperty); }
set { SetValue(MyValueProperty, value); }
}
public void Reset()
{
MyValue = 0;
}
}
I am using that control in a normal XAML page and update MyValue
via binding: 我在普通的XAML页面中使用该控件并通过绑定更新
MyValue
:
<local:MyControl x:Name="TheControl"
MyValue="{Binding MyValueSource, Mode=OneWay}" />
The binding initially propagates changes from MyValueSource
to MyValue
. 绑定最初将更改从
MyValueSource
传播到MyValue
。 But as soon as I call the Reset()
method once, the binding is overwritten by the 0
and updates to MyValueSource
are no longer pulled. 但是一旦我调用
Reset()
方法一次,绑定就被0
覆盖,并且不再拉取对MyValueSource
更新。
I suppose any direct assignment of MyValue
is intended to replace a OneWay
binding. 我想任何
MyValue
直接赋值都是为了取代OneWay
绑定。 With a TwoWay
binding, the change is just propagated back to MyValueSource
and the binding remains functional. 使用
TwoWay
绑定,更改只会传播回MyValueSource
,绑定仍然有效。
If Reset()
was in the view model, I could do this: 如果
Reset()
在视图模型中,我可以这样做:
public void Reset()
{
// TheControl.MyValue = 0; // Bad practice, destroys the binding
MyValueSource = 0; // Good practice, preserves the binding
}
I don't want to implement the reset logic (which is more complex than in this reduced example) in every VM though, so it's located in the view/control. 我不想在每个VM中实现重置逻辑(这比在这个简化示例中更复杂),因此它位于视图/控件中。
So I wonder - can you assign a bindable property's value from the control's code behind and still preserve a possible OneWay
binding? 所以我想知道 - 你可以从控件的代码中分配一个可绑定属性的值并仍然保留一个可能的
OneWay
绑定吗? I know this means the VM does not get the changed value; 我知道这意味着VM没有获得更改的值; binding
OneWay
is likely not correct if the control updates the property as well; 绑定
OneWay
可能不正确,如果控件也更新属性; you should rather use a TwoWay
binding then. 你应该使用
TwoWay
绑定。
But if someone says OneWay
in XAML, I'd rather have it behave that way down to the wire than implement some " OneWay
until you call Reset()
" behavior. 但是,如果有人在XAML中使用
OneWay
,我宁愿让它表现得像是在执行一些“ OneWay
直到你调用Reset()
”行为。
Side note: I am working in Xamarin, but I guess the behavior is the same for WPF. 旁注:我在Xamarin工作,但我猜WPF的行为是一样的。
Taken and fleshed out from @Clemens' comment : 从@Clemens的评论中摘取并充实:
You can use the SetCurrentValue
method on an DependencyObject
(ie the control) to change the current effective value of a DependencyProperty
. 您可以在
DependencyObject
(即控件)上使用SetCurrentValue
方法来更改DependencyProperty
的当前有效值。 Unlike SetValue
, with SetCurrentValue
any triggers, data bindings and styles to that property remain intact. 与
SetValue
不同,使用SetCurrentValue
,该属性的任何触发器,数据绑定和样式都保持不变。
public void Reset()
{
// this.SetValue(MyValueProperty, 0); // Replaces the binding
this.SetCurrentValue(MyValueProperty, 0); // Keeps the binding
}
Remember that if you defined a OneWay
binding, the view model will not be notified about the changed value, and that any change to the VM's MyValueSource
property will override the control's value again (if the property is implemented correctly). 请记住,如果您定义了
OneWay
绑定,则不会通知视图模型更改的值,并且对VM的MyValueSource
属性的任何更改都将再次覆盖控件的值(如果属性正确实现)。
There is currently no proper way to assign a BindableProperty
's value without replacing a OneWay
binding attached to it. 目前没有正确的方法来分配
BindableProperty
的值而不替换附加到它的OneWay
绑定。 BindableObject
(the control's base class) does not have any method comparable to WPF's SetCurrentValue
and SetValue
will allways replace the binding. BindableObject
(控件的基类)没有任何与WPF的SetCurrentValue
相当的方法, SetValue
将总是替换绑定。
However, if you change the binding to BindingMode.TwoWay
, the internal value change is propagated back to the view model. 但是,如果将绑定更改为
BindingMode.TwoWay
,则内部值更改将传播回视图模型。 You should probably do this anyway to keep the control and the VM synchronized. 无论如何,您应该这样做以保持控件和VM同步。
public void Reset()
{
// Replaces any OneWay bindings
// Updates MyValueSource for TwoWay bindings
this.SetValue(MyValueProperty, 0);
}
Here is the Hacky WPF equivalent for Xamarin, for OneWay binding: 这是针对Xamarin的Hacky WPF等价物,用于OneWay绑定:
public static class BindingObjectExtensions
{
public static Binding GetBinding(this BindableObject self, BindableProperty property)
{
if (self == null)
{
throw new ArgumentNullException(nameof(self));
}
if (property == null)
{
throw new ArgumentNullException(nameof(property));
}
var methodInfo = typeof(BindableObject).GetTypeInfo().GetDeclaredMethod("GetContext");
var context = methodInfo?.Invoke(self, new object[] { property });
var propertyInfo = context?.GetType().GetTypeInfo().GetDeclaredField("Binding");
return propertyInfo?.GetValue(context) as Binding;
}
public static void SetCurrentValue(this BindableObject self, BindableProperty property, object value)
{
if (self == null)
{
throw new ArgumentNullException(nameof(self));
}
if (property == null)
{
throw new ArgumentNullException(nameof(property));
}
var backupBinding = self.GetBinding(property);//backup binding
var backupConverter = backupBinding.Converter;//backup orig. converter
self.SetValue(property,value);//removes the binding.
backupBinding.Converter = new DefaultValueConverter {DefaultValue = value};//change the converter
self.SetBinding(property, backupBinding);//target should be updated to the default value
var converterField = backupBinding.GetType().GetTypeInfo().GetDeclaredField("_converter");
converterField.SetValue(backupBinding, backupConverter);//restore the converter
}
}
//the default value converter class
[ContentProperty(nameof(DefaultValue))]
public class DefaultValueConverter : BindableObject, IValueConverter, IMarkupExtension<DefaultValueConverter>
{
public object DefaultValue
{
get => GetValue(DefaultValueProperty);
set => SetValue(DefaultValueProperty, value);
}
public static readonly BindableProperty DefaultValueProperty =
BindableProperty.Create(nameof(DefaultValue), typeof(object), typeof(DefaultValueConverter));
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return DefaultValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DefaultValue;
}
public DefaultValueConverter ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
{
return ((IMarkupExtension<DefaultValueConverter>) this).ProvideValue(serviceProvider);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.