【发布时间】:2018-05-29 11:05:08
【问题描述】:
我正在尝试创建一个 MarkupExtension,它将:
- 找到目标对象所在的DataTemplate/ContentTemplate。
- 在同一模板中查找另一个对象。
- 将目标对象的属性绑定到在该模板中找到的对象的属性。
这样做的原因是我希望能够使用ElementName 来绑定DataTemplates 中的源,这通常是不可能的。
我编写了以下 MarkupExtension(注意:这是一个快速的第一个版本,我只是希望它能够正常工作,此时不太关心优雅或效率):
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Threading;
namespace Speedocs.WPF.MarkupExtensions
{
public sealed class DataTemplateElementBinding : MarkupExtension
{
#region fields
private FrameworkElement _targetObject;
private DependencyProperty _targetProperty;
private ContentPresenter _templatedParent;
#endregion
#region properties
public string ElementName { get; set; }
public string Path { get; set; }
#endregion
#region Overrides of MarkupExtension
public override object ProvideValue(IServiceProvider serviceProvider)
{
var target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (target != null)
{
if (target.TargetObject.GetType().Name == "SharedDp") return this;
_targetObject = target.TargetObject as FrameworkElement;
if (_targetObject == null)
{
return null;
}
_targetProperty = target.TargetProperty as DependencyProperty;
if (_targetObject == null)
{
return null;
}
// now that the target object has been loaded, find the requsted element
// in the DataTemplate that contains this object, and bind the requested property
// to that element
_targetObject.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, (Action) (() =>
{
_templatedParent = _targetObject.TemplatedParent as ContentPresenter;
if (_templatedParent == null) return;
var sourceObject =
_templatedParent.ContentTemplate.FindName(ElementName, _templatedParent);
var binding = new Binding(Path) {Source = sourceObject};
_targetObject.SetBinding(_targetProperty, binding);
}));
}
return null;
}
#endregion
}
}
这个 MarkupExtension 的作用是:
- 如果从 IProvideValueTarget 返回的 TargetObject 是 SharedDp,则返回 MarkupExtension 本身,以便在目标值加载时再次调用它。
- 加载模板时,会再次调用ProvideValue。
- 然后我们从目标对象中获取 TemplatedParent,使用 FindName 在模板中找到源对象,并绑定到它。
问题是当我调用_templatedParent.ContentTemplate.FindName(ElementName, _templatedParent); 时出现错误
This operation is valid only on elements that have this template applied.
现在,我知道这个错误,它出现在这里没有意义,因为此时模板必须已经加载...如果没有,ProvideValue 就不会被调用第二次时间。
如您所见,我也尝试使用DispatcherPriority.Loaded 调用Dispatcher.BeginInvoke,但没有成功。
请帮忙:-\
【问题讨论】:
标签: wpf