开发者

Can the binding source be updated manually from within the target control in WPF

I have a scenario for which I'll offer a simple example to help illustrate.

Lets say I'm writing a custom CheckBox-like control which has the ability to become checked when particular event fires. The event could be anything such as MouseEnter or maybe even some other non-UI event such as when a network connection is dropped. The point is that at some point I need to manually set the value on the source of the binding.

My assumption is that this type of functionality has to be possible in order for controls like TextBox to work since they can operate in a way that allows the Text property to be updated to values based on what the keyboard is typing but without clearing any binding that my exist on the Text property. So I'm trying to get at the basic way to achieve this.

Attempt#1

Setting the target property of the binding (ex. IsChecked) as shown below will cause the binding to be cleared. So this option will not work (CORRECTION! See my answer below).

this.IsChecked = true;

or alternatively:

this.SetValue(CheckBox.IsCheckedProperty, true);

Attempt#2

The BindingBase class has a property called 开发者_如何转开发ProvideValue which as described in the MSDN documentation "Returns an object that should be set on the property where this binding and extension are applied." This sounds promising so I've attempted to implement my own IServiceProvider in the two ways shown below.

Implementation A:

public class CheckProvider : IServiceProvider
{
    public object Value { get; set; }

    public object GetService(Type serviceType)
    {
        return Value;
    }
}

Implementation B:

public class CheckProvider : IServiceProvider
{
    public object Value { get; set; }

    public object GetService(Type serviceType)
    {
        return new ObjectProvider() { TargetObject = new TempDependencyObject() { IsSelected = Value }, TargetProperty = TempDependencyObject.IsSelectedProperty };
    }

    public class TempDependencyObject : DependencyObject
    {
        public object IsSelected
        {
            get { return (object)GetValue(IsSelectedProperty); }
            set { SetValue(IsSelectedProperty, value); }
        }

        public static readonly DependencyProperty IsSelectedProperty =
            DependencyProperty.Register("IsSelected", typeof(object), typeof(TempDependencyObject));
    }

    public class ObjectProvider : System.Windows.Markup.IProvideValueTarget
    {

        public object TargetObject { get; set; }

        public object TargetProperty { get; set; }
    }
}

And here is how I'm attempting to manually update the binding source using these classes.

myControl.DataContext = obj;
myBinding.ProvideValue(new CheckProvider() { Value = true });

So far I've had no luck so if somebody could offer some pointers it would be very appreciated.


Couple of things, you stated...

TextBox to work since they can operate in a way that allows the Text property to be updated to values based on what the keyboard is typing but without clearing any binding that my exist on the Text property

This comes by way in which the TextBox behaves in regards to the UpdateSourceTrigger. It by default is set for LostFocus. Once LostFocus occurs the actual value is pushed to the object which is bound to the control. If this were set as PropertyChanged it would occur after every key stroke.

To get the behavior you desire you could set the UpdatesourceTrigger property to Explicit within your XAML...

<TextBox Name="MyTextBox"
         Text="{Binding Path=FirstName, UpdateSourceTrigger=Explicit}" />

Then in your code behind at any point you could update the source explicitly...

BindingExpression be = MyTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();

This would tie into your need to update the source when some random event occurs or when some other series of events takes place.

If you are trying to set the value of a bindable property external to the control and expect those bound to the property to not change; that is not possible out of the box. You would need to derive your own control and manage it internally, exposing a DependencyProperty that could take the type you want to update. If you go into VS and type propdp there is a code snippet for a DependencyProperty.

You would get something like...

    public List<byte> Text
    {
        get { return (List<byte>)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(List<byte>), typeof(RichTextBoxExtended), new UIPropertyMetadata());

Text is a bindable property on this custom type which derives from RichTextBox. Therefore if someone had an instance of this type they could call the property itself to set and change the value. When doing this all object consuming this property and optionaly bound to it will be notified.


The short answer is to my question is YES! After looking over the code for the CheckBox within Reflector I realized that in that code they are setting IsChecked directly in response to a Click event and it works as expected. Originally, I was assuming that this would clear the underlying binding but instead setting the dependency property instead seams to do exactly what I was looking for in that it sets the target value of the property and then updates the source of the binding if the binding uses TwoWay or OneWayToSource binding modes.

So to be clear the two examples below.

So this option should work to update the Dependency Property and hence the source of the binding that may be set on that property.

this.IsChecked = true;

or alternatively:

this.SetValue(CheckBox.IsCheckedProperty, true);

Thanks to Aaron for his assistance when trying to work this out.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜