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>
Impossible WPF Part 1: Binding Properties的更多相关文章
- Impossible WPF Part 2: Binding Expressions
原文 http://www.11011.net/wpf-binding-expressions Back in April I posted an idea for building an expre ...
- 【转】WPF中的Binding技巧(二)
WPF中的Binding技巧(二) 接上篇, 我们来看一看Elementname,Source,RelativeSource 三种绑定的方式 1.ElementName顾名思义就是根据Ui元素 ...
- [XAML]类似WPF绑定的Binding的读取方法
在WPF的XAML里,依赖属性可以使用基于BindingBase之类的MarkupExtensin 读取XAML时,会自动的把该BindingBase转换为BindingExpressionBase ...
- [WPF系列]-使用Binding来同步不同控件的Dependency property
简介 项目中经常会用到,同步两个控件的值,本文就简单列举两种方式来同步不同控件的两个Dependency Property. 示例 效果图: 只使用C#代码: //获取slider1的ValueDep ...
- (WPF) 再议binding:点击User Control时,User Control变换颜色或做其他的处理。
Binding 是前台UI(显示层)和后台代码(数据层)的桥梁.理论上当后台的数据变动时,显示的数据或样式应该随之而变.这些是动态的. 对于Binding的设置可以在前台Xaml,也可以在后台Code ...
- 解读WPF中的Binding
1.Overview 基于MVVM实现一段绑定大伙都不陌生,Binding是wpf整个体系中最核心的对象之一这里就来解读一下我花了纯两周时间有哪些秘密.这里我先提出几个问题应该是大家感兴趣的,如下: ...
- (WPF, MVVM) Textbox Binding
参考:http://msdn.microsoft.com/en-us/library/system.windows.data.updatesourcetrigger(v=vs.110).aspx Te ...
- (WPF, MVVM) Slider Binding.
对于Button的Command的绑定可以通过实现ICommand接口来进行,但是Slider并没有Command属性. 另外如果要实现MVVM模式的话,需要将一些Method和Slider的Even ...
- (WPF) MVVM: DataGrid Binding
Binding到DataGrid的时候,需要用到ObservableCollection. public ObservableCollection<Customer> Customers ...
随机推荐
- LintCode-不同的子序列
题目描述: 给出字符串S和字符串T,计算S的不同的子序列中T出现的个数. 子序列字符串是原始字符串通过删除一些(或零个)产生的一个新的字符串,并且对剩下的字符的相对位置没有影响.(比如,“ACE”是“ ...
- Linux下使用ps命令来查看Oracle相关的进程
Linux下可以使用ps命令来查看Oracle相关的进程 Oracle Listener 这个命令会列出Oracle Net Listener的进程 [oracle@ www.linuxidc.com ...
- hadoop学习;安装jdk,workstation虚拟机v2v迁移;虚拟机之间和跨物理机之间ping网络通信;virtualbox的centos中关闭防火墙和检查服务启动
JDK 在Ubuntu下的安装 与 环境变量的配置 前期准备工作: 找到 JDK 和 配置TXT文件 并拷贝到桌面下 不是目录 而是文件拷贝到桌面下 以下的命令部分就直接复制粘贴就能够了 1.配 ...
- Foundation Sorting: Quicksort
/* Quick Sorting. * Implementation history:. * 2013-09-15, Mars Fu, first version. */ /* [Quicksort ...
- uva311 - Packets(贪心)
题目:311 - Packets 题目大意:给出1*1, 2*2,3 *3, 4*4, 5*5, 6*6的箱子的个数,如今有若干个6*6的箱子,问最少用多少个箱子能够将给定的箱子都装进去. 解题思路: ...
- epoll相关
1) 能不能给一个使用epoll相关API进行IO监控的示例?在<<epoll学习笔记>>中有一个简单的示例说明epoll相关API的使用, 但是这个示例是非常简单的, 它仅仅 ...
- C#学习日记之数据库连接
一.webconfig设置和参数解释 在C#中新建一个网站时,webconfig文件中会有一个默认的数据库连接语句,如下 <connectionStrings> <add name= ...
- 循环结构中break、continue、return和exit的区别
1. break break语句的使用场合主要是switch语句和循环结构.在循环结构中使用break语句,如果执行了break语句,那么就退出循环,接着执行循环结构下面的第一条语句.如果在多重嵌套循 ...
- Amazon Hiring Campus 2013 - Final 6
Let's assume that there is a simple market for beans. Every day there is a published bean price in t ...
- 数据切分——Mysql分区表的管理与维护
关于Mysql分区表的介绍可以参考: http://blog.csdn.net/jhq0113/article/details/44592865 关于Mysql分区表的创建可以参考: http://b ...