Impossible WPF Part 1: Binding Properties

原文 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>
时间: 2024-11-30 14:40:47

Impossible WPF Part 1: Binding Properties的相关文章

WPF/Silverlight2.0 Binding(数据绑定)机制详解

何为数据绑定 在WPF技术中控件基类(FrameworkElement.FrameworkContentElement)中DataContext属性实现了绑 定机制,在XAML中也支持此机制.当一个控件的DataContext发生变化时,其子控件的DataContext也会继 承父控件的DataContext(前提是这个子控件没有另外赋值).子控件的属性获取数据源中的数据,支持 XAML通过Binding标记获取数据源中的值.数据源更新时刷新其每个子控件中的数据更新,实现一呼百应 的效果! Bi

WPF 依赖属性Binding更改通知

问题描述 解决方案 解决方案二:绑定到该依赖属性的值调用set方法时就会触发了解决方案三:值发生改变的时候呗,设置属性值的时候解决方案四:对wpf不是很熟,不过List类发生变更后好像是不会通知到控件的,需要实现INotifyPropertyChanged接口,然后在属性的set中引发PropertyChanged事件解决方案五:引用3楼qq_26456659的回复: 对wpf不是很熟,不过List类发生变更后好像是不会通知到控件的,需要实现INotifyPropertyChanged接口,然后

Asp.Net Mvc: Model Binding 机制分析

环境: Windows 2008, VS 2008 SP1, Asp.Net Mvc RC1 请求过程片段: 在请求的Action被调用之前,ControllerActionInvoker.InvokeAction()方法被调用,在这个方法里 面,有一个ReflectedActionDescriptor的实例会被构建,这个实例包含Action的描述信息. 接着,ControllerActionInvoker.GetParameterValues()方法被调用,通过传入的之前创建的 Reflect

WPF 引用DLL纯图像资源包类库中的图片

原文:WPF 引用DLL纯图像资源包类库中的图片 1.建立WPF应用程序              过程略.   2.创建类库项目(图片资源包)       创建图片资源类库项目MyImages,删除class1.cs,在项目属性的资源选项中选择"图像"类型,并在"添加资源"中点击"添加现有的文件",把图像加入到资源.并把访问修饰符改为Public.   3.在WPF应用程序中引用类库项目        在WPF中通过 MyImages.Prop

IronPython v2.7发布 .NET及Mono上的Python实现

IronPython 是一种在 http://www.aliyun.com/zixun/aggregation/13480.html">.NET 及 Mono上的 Python 实现,由微软的 Jim Hugunin 所发起,是一个开源的项目,基于微软的 DLR 引擎:托管于微软的开源网站 CodePlex(www.codeplex.com).IronPython 的官方并未实现 Python 通用类库,仅实现了部分核心类,社区的开源类库实现有: fepy(http://fepy.sour

重新想象 Windows 8 Store Apps (30) - 信息: 获取包信息, 系统信息, 硬件信息, PnP信息, 常用设备信息

原文:重新想象 Windows 8 Store Apps (30) - 信息: 获取包信息, 系统信息, 硬件信息, PnP信息, 常用设备信息 [源码下载] 重新想象 Windows 8 Store Apps (30) - 信息: 获取包信息, 系统信息, 硬件信息, PnP信息, 常用设备信息 作者:webabcd 介绍重新想象 Windows 8 Store Apps 之 信息 获取包信息 获取系统信息 获取硬件信息 获取即插即用(PnP: Plug and Play)的设备的信息 获取常

《Pro ASP.NET MVC 3 Framework》学习笔记之三十【模型绑定】

模型绑定(Model Binding)是使用浏览器发起Http请求时的数据创建.NET对象的过程.我们每一次定义带参数的action方法时就已经依靠了模型绑定--这些参数对象是通过模型绑定创建的.这一章会介绍模型绑定的原理以及针对高级使用必要的定制模型绑定的技术. 理解模型绑定(Understanding Model Binding) 想象下我们创建了一个控制器如下: View Code using System; using System.Web.Mvc; using MvcApp.Model

WPF TreeView Binding

Bind Treeview In WPF TreeView Xmal <TreeView x:Name="TvWorkItem" Grid.Column="0" Grid.Row="2" Background="Black" ItemsSource="{Binding MenuDataSource}" SelectedItemChanged="TreeView_OnSelectedItemC

WPF学习之绑定—Validation Rule和Binding Group

在上一篇文章中我们讨论了有关WPF绑定的知识点,现在我们可以很容易的将业务数据作为源绑定到WPF控件并可以通过创建不同的Data Template后来以特定的样式展现.而作为这个最常用的功能我们可以通过Two Way的绑定模式与界面交互,而在这个时候我们就需要类似于ASP.NET中Validator一样的东西来限制或者说校验数据有效性.ValidationRule就是为这样的应用而生的.本文将详细讨论如何应用验证规则和自定义错误模板. 我们首先来看看WPF中有关数据验证规则相关的几个常用类: ·