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 ...
随机推荐
- Liunx 环境下vsftpd的三种实现方法(超详细参数)
以下文章介绍Liunx 环境下vsftpd的三种实现方法 ftp://vsftpd.beasts.org/users/cevans/vsftpd-2.0.3.tar.gz,目前已经到2.0.3版本.假 ...
- 在SQL2005中部署CLR 程序集
原文 在SQL2005中部署CLR 程序集 有关于CLR函数的用途和用法,请了解 SQL Server CLR 极速入门,启用.设计.部署.运行 http://www.yongfa365.com/It ...
- 转:详细解说 STL 排序(Sort)
详细解说 STL 排序(Sort) 详细解说 STL 排序(Sort) 作者Winter 详细解说 STL 排序(Sort) 0 前言: STL,为什么你必须掌握 1 STL提供的Sort 算法 1. ...
- HDU 1157 Who's in the Middle
#include <cstdio> #include <algorithm> using namespace std; int main() { int n; while(sc ...
- 百度复制SQL语句
本词条从基础知识.判断对象和应用技巧等方面,介绍了SQL(Structured Query Language)结构化查询语言的应用方法. 目录 1基础 ▪ 创建数据库▪ 删除数据库▪ 备份sql se ...
- SDWebImage内部实现过程
入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处 ...
- BZOJ 1692: [Usaco2007 Dec]队列变换( 贪心 )
数据 n <= 30000 , 然后 O( n² ) 的贪心也过了..... USACO 数据是有多弱啊 = = ( ps : BZOJ 1640 和此题一模一样 , 双倍经验 ) ------ ...
- Spring AOP报错
八月 01, 2016 10:08:48 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRe ...
- [Just a feeling]
The possibility of enhancing one's knowledge is limitless. Graduation only marks a stage of one's ed ...
- JS实现常用的分享到按钮
我们阅读博客的时候经常会用到这样功能,当然有时候也会想把自己的网站上也加入类似的分享功能,各大厂商已经给出了相应的API,点击一个按钮即可弹出窗口进入分享,我们事先可以设置一些参数,一般常用的就是 网 ...