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 ...
随机推荐
- Beat It
They Told Him他们告诉他: Don't You Ever Come Around Here “你胆敢再来? Don't Wanna See Your Face, 不想再见你, You Be ...
- Python type类具体的三大分类:metaclasses,classes,instance
Python type类视角中的对象体系需要我们不断的学习,其中我们使用的时候需要注意.下面我们就看看如何才能更好的运用Python type类.下面的文章希望大家有所收获. 在单纯的Python t ...
- AttributeError: 'module' object has no attribute 'Thread'
$ python thread.py starting at: 2015-08-05 00:24:24Traceback (most recent call last): File "th ...
- python的正则表达式 re
python的正则表达式 re 本模块提供了和Perl里的正则表达式类似的功能,不关是正则表达式本身还是被搜索的字符串,都可以是Unicode字符,这点不用担心,python会处理地和Ascii字符一 ...
- GCD自己做的一些简单总结
GCD总结 GCD Grand Central Dispatch 牛逼的中枢调度器 GCD中各种队列的执行效果 想看线程 必须是异步函数 并且不是主队列 注意:使用sync函数往当前串行队列添 ...
- Intersection(poj)
Intersection Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 13140 Accepted: 3424 Des ...
- Android开发之TextView高级应用
Android开发之TextView高级应用 我们平时使用TextView往往让它作为一个显示文字的容器,但TextView的功能并不局限于此.以下就和大家分享一下TextView的一些使用技巧. A ...
- 信号量多-threaded同步Semaphore
Semaphore它是JDK1.5一个实现后,外面有个办法同步.Semaphore能够保持其当前的线程接入号码.并提供了一个同步机制. 采用Semaphore时,可以用相同的对资源的访问进行控制的线程 ...
- C#中Cache用法
C#中Cache用法 Cache 是分配在服务器上的一个公共的内存片,所谓公共指的cache只要一创建是任何一个客户端浏览器都可以通过后台代码访问到它,它面向的是所有用户,相对而言sessio ...
- 实现JQuery_Accordion功能
1. 添加AJAX引用 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/li ...