【发布时间】:2022-11-20 02:40:55
【问题描述】:
“绑定”的工作示例:
我有一个 UserControl,我在主窗口中使用它:
<userControls:NoMarkupControl/>
我的 MainWindow 的 ViewModel 包含此属性:
private string _exampleText = "example";
public string ExampleText
{
get { return _exampleText; }
set
{
_exampleText = value;
OnPropertyChanged();
}
}
在 UserControl 中,我将我的 ViewModel 绑定到这个属性:
<TextBlock Text="{Binding ExampleText}"/>
因此,当我启动应用程序时,会显示“示例”。一切正常。
不使用自定义标记扩展的示例:
现在我有一个 MarkupExtension:
public class ExampleTextExtension : MarkupExtension
{
private static readonly List<DependencyProperty> StorageProperties = new List<DependencyProperty>();
private readonly object _parameter;
private DependencyProperty _dependencyProperty;
public ExampleTextExtension(object parameter)
{
_parameter = parameter;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
DependencyObject targetObject;
if (target?.TargetObject is DependencyObject dependencyObject &&
target.TargetProperty is DependencyProperty)
{
targetObject = dependencyObject;
}
else
{
return this;
}
_dependencyProperty = SetUnusedStorageProperty(targetObject, _parameter);
return GetLocalizedText((string)targetObject.GetValue(_dependencyProperty));
}
private static string GetLocalizedText(string text)
{
return text == null ? null : $"markup: {text}";
}
private static DependencyProperty SetUnusedStorageProperty(DependencyObject obj, object value)
{
var property = StorageProperties.FirstOrDefault(p => obj.ReadLocalValue(p) == DependencyProperty.UnsetValue);
if (property == null)
{
property = DependencyProperty.RegisterAttached("Storage" + StorageProperties.Count, typeof(object), typeof(ExampleTextExtension), new PropertyMetadata());
StorageProperties.Add(property);
}
if (value is MarkupExtension markupExtension)
{
var resolvedValue = markupExtension.ProvideValue(new ServiceProvider(obj, property));
obj.SetValue(property, resolvedValue);
}
else
{
obj.SetValue(property, value);
}
return property;
}
private class ServiceProvider : IServiceProvider, IProvideValueTarget
{
public object TargetObject { get; }
public object TargetProperty { get; }
public ServiceProvider(object targetObject, object targetProperty)
{
TargetObject = targetObject;
TargetProperty = targetProperty;
}
public object GetService(Type serviceType)
{
return serviceType.IsInstanceOfType(this) ? this : null;
}
}
}
我再次有一个 UserControl,我在主窗口中使用它:
<userControls:MarkupControl/>
我的 MainWindow 的 ViewModel 和上面一样。
在 UserControl 中,我像这样绑定到我的 TextBlock Text 属性:
<TextBlock Text="{markupExtensions:ExampleText {Binding ExampleText}}"/>
结果我的 UserControl 什么都不显示。我本来希望显示“标记:示例”
在这种情况下,绑定以某种方式不起作用。
有人知道如何解决这个问题吗?
附加信息:
它像这样使用时有效(依赖属性 MarkupText 在用户控件中创建):
<userControls:MarkupControl MarkupText={markupExtensions:ExampleText {Binding ExampleText}}/>
<TextBlock Text="{Binding Text, ElementName=MarkupControl}"/>
【问题讨论】:
-
为什么要标记扩展?而不是动态资源或只是视图模型中的属性?
-
您必须将传入的 Binding 设置为依赖属性才能激活它。绑定引擎实际上完成了将目标属性连接到源属性的所有工作。绑定引擎是依赖属性基础结构的一部分。这就是为什么绑定目标必须是一个依赖属性。您需要创建一个中间依赖属性来解析绑定。处理绑定事件 SourceUpdated 和 TargetUpdated 以捕获更新后的值。然后处理/操纵它并将其发送到您的自定义标记扩展的目标。
-
要附加 Binding,您的中间属性必须由 DependencyObject 定义。这意味着您需要创建一个专用类来解析绑定。
-
@Andy 我创建这个标记扩展只是为了显示什么不起作用,我真正的标记扩展处理某种语言更改。我也可以在 VM 中执行此操作,但我认为标记扩展可以使它更干净并且(如果工作的话)更易于使用
-
@BionicCode 我不确定我是否理解你。我以为我已经在使用一个依赖属性:
property = DependencyProperty.RegisterAttached("Storage" + StorageProperties.Count, typeof(object), typeof(ExampleTextExtension), new PropertyMetadata());在这里我将 dp 链接到一个依赖对象:var resolvedValue = markupExtension.ProvideValue(new ServiceProvider(obj, property)); obj.SetValue(property, resolvedValue);你能发布一个例子或者试着说明你的意思吗?绑定基本上可以正常工作,只是不适用于我的问题中发布的情况
标签: wpf binding markup-extensions