原文 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>

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

  1. Impossible WPF Part 2: Binding Expressions

    原文 http://www.11011.net/wpf-binding-expressions Back in April I posted an idea for building an expre ...

  2. 【转】WPF中的Binding技巧(二)

    WPF中的Binding技巧(二)     接上篇, 我们来看一看Elementname,Source,RelativeSource 三种绑定的方式 1.ElementName顾名思义就是根据Ui元素 ...

  3. [XAML]类似WPF绑定的Binding的读取方法

    在WPF的XAML里,依赖属性可以使用基于BindingBase之类的MarkupExtensin 读取XAML时,会自动的把该BindingBase转换为BindingExpressionBase ...

  4. [WPF系列]-使用Binding来同步不同控件的Dependency property

    简介 项目中经常会用到,同步两个控件的值,本文就简单列举两种方式来同步不同控件的两个Dependency Property. 示例 效果图: 只使用C#代码: //获取slider1的ValueDep ...

  5. (WPF) 再议binding:点击User Control时,User Control变换颜色或做其他的处理。

    Binding 是前台UI(显示层)和后台代码(数据层)的桥梁.理论上当后台的数据变动时,显示的数据或样式应该随之而变.这些是动态的. 对于Binding的设置可以在前台Xaml,也可以在后台Code ...

  6. 解读WPF中的Binding

    1.Overview 基于MVVM实现一段绑定大伙都不陌生,Binding是wpf整个体系中最核心的对象之一这里就来解读一下我花了纯两周时间有哪些秘密.这里我先提出几个问题应该是大家感兴趣的,如下: ...

  7. (WPF, MVVM) Textbox Binding

    参考:http://msdn.microsoft.com/en-us/library/system.windows.data.updatesourcetrigger(v=vs.110).aspx Te ...

  8. (WPF, MVVM) Slider Binding.

    对于Button的Command的绑定可以通过实现ICommand接口来进行,但是Slider并没有Command属性. 另外如果要实现MVVM模式的话,需要将一些Method和Slider的Even ...

  9. (WPF) MVVM: DataGrid Binding

    Binding到DataGrid的时候,需要用到ObservableCollection. public ObservableCollection<Customer> Customers ...

随机推荐

  1. USB VID PID 查询

    USB VID PID 查询:http://www.linux-usb.org/usb.ids 说明: USB设备中有VID何PID,分别表示此USB设备是哪个厂商的哪种设备. 一个USB的VID对应 ...

  2. OTL使用总结

    在VC中访问Oracle,可以使用ADO或ODBC,如果你比较强大,也可以直接使用OCI API,但我个人认为OTL是最佳选择,它是一套数据库访问C++模板库,全部代码都在otlv4.h头文件中,通过 ...

  3. mysql下的SELECT INTO语句

    在mysql下使用SELECT INTO语句会产生ERROR 1327 (42000): Undeclared variable:new_tablename 此时要使用: CREATE TABLE C ...

  4. Installing on CentOS/RHEL / KB forum / Ajenti

    Installing on CentOS/RHEL / KB forum / Ajenti Ajenti → KB → Installation Is it?: Inappropriate Spam ...

  5. BI系统的应用组织思路与数据分析模式

    BI商业智能软件一般都会提供若干数据整合.数据查询.分析与评价.数据可视化及数据分享的手段,但是在BI项目的构建与实施过程中,如果不按照一定的应用组织思路.数据分析模式及分析流程使用这些工具或手段,呈 ...

  6. poj1077 Eight【爆搜+Hash(脸题-_-b)】

    转载请注明出处,谢谢:http://www.cnblogs.com/KirisameMarisa/p/4298840.html   ---by 墨染之樱花 题目链接:http://poj.org/pr ...

  7. Xcode - 详解真机测试步骤

    第一种从iOS9.0之后推出的免费开发者账号 1.注册开发者 * 注册Apple ID * 使用Apple ID登录苹果开发者中心,注册成为开发者 * 此过程为免费,只是为了让普通的Apple ID具 ...

  8. BZOJ 1261: [SCOI2006]zh_tree( 区间dp )

    dp(l, r)表示[l, r]这段作为一棵树的最小访问代价. 对于dp(l, r), 我们枚举它的根x, 则dp(l, r) = min(dp(l, x-1)+dp(x+1, r)+C*fx) + ...

  9. spring多数据源的配置

    C3P0和DBCP的区别 C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展.目前使用它的开源项目有Hibernate,Spring等.   d ...

  10. linux分区工具fdisk的使用

    fdisk是linux下的一块分区工具,使用简单方便,由于是对系统进行修改,需要root权限. 常用参数如下: fdisk  -l : 列出所有的硬盘信息 直接传入设备名称可进入对该硬盘分区.例如,f ...