Impossible WPF Part 1: Binding Properties

简介: 原文 http://www.11011.net/wpf-binding-properties Ever wanted to write the following?   I noticed at least one user on the MSDN Forums who did.

原文 http://www.11011.net/wpf-binding-properties

Ever wanted to write the following?

 

<RichTextBoxDocument="{Binding}" />

I noticed at least one user on the MSDN Forums who did. The general answer is that it's not possible - because Document isn't a DependencyProperty. Sometime last year I wrote a utility that I hoped would solve this very problem. Back then, it didn't work properly, but with increased WPF experience I gave it another shot. This time it seems to pass the basic tests I throw at it.

The concept is a "Proxy" FrameworkElement with two DependencyProperties, one Input and one Output. The Input property is tied to the Output such that when Input changes it is applied to Output.

<utils:ProxyIn="{Binding}"Out="{Binding ElementName=richTextBox, Path=Document}" />

This effectively binds the Document property of a RichTextBox. If the above doesn't make sense it's probably due to the default settings on the Out property. Namely BindsTwoWayByDefault and UpateSourceTrigger.PropertyChanged.

I'll post the entire Proxy source at the end of this entry, but for now let's step through some of the more interesting details.

 

FrameworkPropertyMetadata inMetadata = new FrameworkPropertyMetadata(
    delegate(DependencyObject p, DependencyPropertyChangedEventArgs args)
    {
        (p as Proxy).Out = args.NewValue;
    });

The PropertyChangedCallback for the In property does as you probably expected, it just sets the new value on the Out property.

But we also need a PropertyChangedCallback for the Out property. I wanted the Proxy to bind two-way by default so that in the event the source (the non-DependencyProperty) changed, the proxy would overwrite the change with the In value. In some cases it is also necessary to overwrite the initial value. If the In property changes or is bound before Out is bound the In value is not always propagated. Fortunately when Out is bound it calls its own PropertyChangedCallback allowing us to propagate the initial value.

FrameworkPropertyMetadata outMetadata = new FrameworkPropertyMetadata(
    delegate(DependencyObject p, DependencyPropertyChangedEventArgs args)
    {
        Proxy proxy = p as Proxy;
        object expected = proxy.In;
        if (!object.ReferenceEquals(args.NewValue, expected))
        {
                Dispatcher.CurrentDispatcher.BeginInvoke(
                    DispatcherPriority.Background,
                    new Operation(delegate
                    {
                        proxy.Out = proxy.In;
                    }));
        }
    });

The PropertyChangedCallback for Out does just that. It checks if Out is the same as In and if not asynchronously (so as not to confuse the binding engine) overwrites Out with In.

As promised, here is the complete source code.

public class Proxy : FrameworkElement
{
    public static readonly DependencyProperty InProperty;
    public static readonly DependencyProperty OutProperty;

    public Proxy()
    {
        Visibility = Visibility.Collapsed;
    }

    static Proxy()
    {
        FrameworkPropertyMetadata inMetadata = new FrameworkPropertyMetadata(
            delegate(DependencyObject p, DependencyPropertyChangedEventArgs args)
            {
                if (null != BindingOperations.GetBinding(p, OutProperty))
                    (p as Proxy).Out = args.NewValue;
            });

        inMetadata.BindsTwoWayByDefault = false;
        inMetadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

        InProperty = DependencyProperty.Register("In",
            typeof(object),
            typeof(Proxy),
            inMetadata);

        FrameworkPropertyMetadata outMetadata = new FrameworkPropertyMetadata(
            delegate(DependencyObject p, DependencyPropertyChangedEventArgs args)
            {
                ValueSource source = DependencyPropertyHelper.GetValueSource(p, args.Property);

                if (source.BaseValueSource != BaseValueSource.Local)
                {
                    Proxy proxy = p as Proxy;
                    object expected = proxy.In;
                    if (!object.ReferenceEquals(args.NewValue, expected))
                    {
                        Dispatcher.CurrentDispatcher.BeginInvoke(
                            DispatcherPriority.DataBind, new Operation(delegate
                                {
                                    proxy.Out = proxy.In;
                                }));
                    }
                }
            });

        outMetadata.BindsTwoWayByDefault = true;
        outMetadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

        OutProperty = DependencyProperty.Register("Out",
            typeof(object),
            typeof(Proxy),
            outMetadata);
    }

    public object In
    {
        get { return this.GetValue(InProperty); }
        set { this.SetValue(InProperty, value); }
    }

    public object Out
    {
        get { return this.GetValue(OutProperty); }
        set { this.SetValue(OutProperty, value); }
    }
}

And finally, a complete example.

<Windowx:Class="PropertyBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:utils="clr-namespace:"
    >
    <Grid>
        <Grid.DataContext>
            <FlowDocument>
                <Paragraph>Bind <Bold>This!</Bold></Paragraph>
            </FlowDocument>
        </Grid.DataContext>
        <RichTextBoxHeight="200"Name="rtb" />
        <utils:ProxyIn="{Binding}"Out="{Binding ElementName=rtb, Path=Document}" />
    </Grid>
</Window>
目录
相关文章
|
C#
WPF中Binding使用StringFormat格式化字符串方法
原文:WPF中Binding使用StringFormat格式化字符串方法 货币格式 // $123.46 货币格式,一位小数 // $123.5 前文字 //单价:$123.
2305 0
|
存储 自然语言处理 C#
WPF技术之Binding
WPF(Windows Presentation Foundation)是微软推出的一种用于创建应用程序用户界面的框架。Binding(绑定)是WPF中的一个重要概念,它用于在界面元素和数据源之间建立关联。通过Binding,可以将界面元素(如文本框、标签、列表等)与数据源(如对象、集合、属性等)进行绑定,从而实现数据的双向传递和同步更新。
268 2
WPF技术之Binding
WPF-Binding问题-模板样式使用Binding TemplatedParent与TemplateBinding区别
WPF-Binding问题-模板样式使用Binding TemplatedParent与TemplateBinding区别
203 0
|
C#
WPF QuickStart系列之数据绑定(Data Binding)
原文:WPF QuickStart系列之数据绑定(Data Binding) 这篇博客将展示WPF DataBinding的内容。 首先看一下WPF Data Binding的概览, Binding Source可以是任意的CLR对象,或者XML文件等,Binding Target需要有依赖属性。
1236 0
WPF Binding学习(二)
  Binding作为数据的桥梁,连通业务逻辑层的对象(源对象)和UI的控件对象(目标对象)。在这座桥梁上,我们不仅可以控制在源对象与目标对象是双向通行还是单向通行。还可以控制数据的放行时机,甚至可以在这座桥上搭建一些关卡用来转换数据类型或者检验数据的正确性    我们先做一个最基本的例子,  ...
1181 0
|
C#
解答WPF中ComboBox SelectedItem Binding不上的Bug
原文:解答WPF中ComboBox SelectedItem Binding不上的Bug 正在做一个打印机列表,从中选择一个打印机(System.Printing) var printServer = new LocalPrintServer(); PrintQueues = printServer.
1012 0
|
C#
WPF Binding Mode,UpdateSourceTrigger
原文:WPF Binding Mode,UpdateSourceTrigger WPF 绑定模式(mode) 枚举值有5个1:OneWay(源变就更新目标属性)2:TwoWay(源变就更新目标并且目标变就更新源)3:OneTime(只根据源来设置目标,以后都不会变)4:OneWayToSource...
1564 0
|
C# .NET 开发框架
WPF笔记 ( xmlns引用,Resource、Binding 前/后台加载,重新绑定) 2013.6.7更新
原文:WPF笔记 ( xmlns引用,Resource、Binding 前/后台加载,重新绑定) 2013.6.7更新 1、xmlns Mapping URI的格式是 clr-namespace:[;assembly=] (1)如果自定义类和XAML处在同一个Assembly之中,只还需要提供clr-namespace值。
1449 0