c# - 使用非字符串 DataBinding 值的 MarkupExtension
问题描述
我正在尝试为 WPF 创建一个 MarkupExtension 以用于翻译。我在这里发现了一些类似的问题,包括
使用 DataBinding 值的 MarkupExtension
最终,这导致Torvin 的反应看起来非常有希望。但是,就像评论中的一个人一样,我有一个问题,即通过 获得的值target.GetValue()
总是返回 null。
这是一些代码。
最终我有一组静态类,其中包含一个静态 KeyDefinition 对象,如下所示
Public class KeyDefinition
{
Public string Key {get; set;}
Public string DefaultValue {get; set;}
}
键与 JSON 资源相关联,而 DefaultValue 是英文翻译,我们可以将其用于 xaml 的设计时显示。
本地化通过像这样的静态类发生Localize.GetResource(key)
我的目标是像这样编写 XAML
<TextBlock Text="{Localize {Binding KeyDefinitionFromDataContext}}">
whereKeyDefinitionFromDataContext
是视图模型中的一个属性,它返回对KeyDefinition
对象的引用。
根据 Torvin 的回复,我像这样创建了一个 MarkupExtension
public class LocalizeExtension : MarkupExtension
{
private readonly BindingBase _binding;
private static readonly DependencyProperty _valueProperty = DependencyProperty.RegisterAttached("Value", typeof(KeyDefinition), typeof(LocalizeExtension));
[ConstructorArgument("keyDefinition")
public KeyDefinition KeyDefinition {get; set;}
public LocalizeExtension(Binding binding)
{
_binding = binding;
}
public LocalizeExtension(KeyDefinition keyDefinition)
{
KeyDefinition = keyDefinition;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var pvt = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var target = pvt.TargetObject as DependencyObject;
var property = pvt.TargetProperty as DependencyProperty;
//If inside a template, WPF will call again when its applied
if (target == null)
return this;
BindingOperations.SetBinding(target, property, _binding);
KeyDefinition = (KeyDefinition)target.GetValue(_valueProperty);
BindingOperations.ClearBinding(target, property);
return Localize.GetResource(KeyDefinition.Key);
}
}
现在请原谅我,因为我平时不做WPF的工作,但是这个任务已经落到了我的身上。每当我运行此代码时,返回的值总是Null
. 我试过直接使用字符串而不是“KeyDefinition”对象,但遇到了同样的问题。
我认为在这里让我感到困惑的是目标上的 DependencyProperty 是如何设置的,因为它是私有的。
任何帮助表示赞赏。谢谢!
解决方案
这不是它的工作方式。的结果MarkupExtension
总是null
,因为那是你返回的。您必须知道Binding
(the BindingExpression
) 在调用扩展时未解析。XAML 引擎调用扩展并在Binding
. 通常,MarkupExtension
将返回 的结果Binding.ProvideValue(serviceProvider)
,即 a BindingExpressionBase
。XAML 引擎稍后将使用此表达式通过实际附加绑定来生成数据。
换句话说,您过早地返回结果。
除此之外,您还必须知道MarkupExtension.ProvideValue
仅调用一次。这意味着您的扩展不处理属性更改(以防绑定源更改)并且清除绑定不是对绑定的期望处理。它实际上甚至无法处理OneTime
绑定模式。
在本地化的上下文中,期望源属性发生变化是非常有意义的,至少在用户更改本地化时是这样。
您的代码中有更多错误,例如未设置的_valueProperty
字段。DependencyProperty
在不扩展的类型上定义 a 的目的是什么DependencyObject
?它甚至是私人的!您还应该避免混合属性和字段。更好地定义(只读)属性而不是字段。从您的扩展返回this
(类型的实例)在预期类型不是例如 a - return的情况下MarkupExtension
将不起作用。object
string
null
你想要的很容易实现。
首先,您必须将 附加Binding
到代理对象以允许绑定引擎激活BindingExpression
(在示例中这是BindingResolver
类)。
其次,您必须配置传入绑定以在更新目标时发出通知。然后监听Binding.TargetUpdated
事件实现OneWay
绑定。要实现TwoWay
和OneWayToSource
绑定模式,您还必须启用和观察Binding.SourceUpdated
事件。
最后,从源/绑定代理检索更改的值,将其设置为MarkupExtension
.
由于数据绑定通常涉及DataContext
作为源,即需要可视化树才能解析,因此绑定代理是一个简单的附加属性。这样做的好处是我们可以使用DataContext
目标元素的原始元素,而不必担心如何将我们的代理注入到可视化树中。
本地化扩展.cs
public class LocalizeExtension : MarkupExtension
{
private Binding Binding { get; };
private DependencyObject LocalizationTarget { get; set; }
private DependencyProperty LocalizationTargetProperty { get; set; }
private object LocalizationSource { get; set; }
private string LocalizationPropertyName { get; set; }
private bool IsInitialized { get; set; }
public LocalizeExtension(Binding binding)
{
this.Binding = binding;
this.Binding.NotifyOnTargetUpdated = true;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var serviceProvider = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
this.LocalizationTarget = serviceProvider.TargetObject as DependencyObject;
// If inside a template, WPF will call again when its applied
if (this.LocalizationTarget == null)
{
return null;
}
this.LocalizationTargetProperty = serviceProvider.TargetProperty as DependencyProperty;
BindingOperations.SetBinding(this.LocalizationTarget, BindingResolver.ResolvedBindingValueProperty, this.Binding);
Binding.AddTargetUpdatedHandler(this.LocalizationTarget, OnBindingSourceUpdated);
return null;
}
private void OnBindingSourceUpdated(object sender, EventArgs e)
{
if (!this.IsInitialized)
{
InitializeLocalizationSourceInfo();
}
LocalizeBindingSource();
}
private void InitializeLocalizationSourceInfo()
{
BindingExpression bindingExpression = BindingOperations.GetBindingExpression(this.LocalizationTarget, BindingResolver.ResolvedBindingValueProperty);
this.LocalizationSource = bindingExpression.ResolvedSource;
this.LocalizationPropertyName = bindingExpression.ResolvedSourcePropertyName;
this.IsInitialized = true;
}
private void LocalizeBindingSource()
{
object unlocalizedValue = BindingResolver.GetResolvedBindingValue(this.LocalizationTarget);
object localizedValue = LocalizeValue(unlocalizedValue);
this.LocalizationTarget.SetValue(this.LocalizationTargetProperty, localizedValue);
}
private object LocalizeValue(object value)
{
return value is KeyDefinition keyDefinition
? Localize.GetResource(keyDefinition.Key)
: string.Empty;
}
}
绑定解析器.cs
class BindingResolver : DependencyObject
{
public static object GetResolvedBindingValue(DependencyObject obj) => (object)obj.GetValue(ResolvedBindingValueProperty);
public static void SetResolvedBindingValue(DependencyObject obj, object value) => obj.SetValue(ResolvedBindingValueProperty, value);
public static readonly DependencyProperty ResolvedBindingValueProperty =
DependencyProperty.RegisterAttached(
"ResolvedBindingValue",
typeof(object),
typeof(BindingResolver),
new PropertyMetadata(default));
}
推荐阅读
- c# - Microsoft.CognitiveServices.Speech.SpeechRecognizer - 获取文件中结果的时间偏移,持续识别
- mysql - 插入两个表不起作用。有正确的方法吗?
- python - 计算行数并以更少的数量打印
- vim - 在vim中替换匹配分隔符内的单个字符
- java - 在 ListView 中从 XML 资源文件添加项目
- javascript - 如何在 element-ui vuejs 中使用 :error 获得成功 validateState
- amazon-web-services - AWS Cognito 用户注册前验证。检查用户是否存在于其他数据库中
- php - Paypal Payflow 透明重定向错误代码:PL001
- mongodb - 如何在开玩笑测试中正确关闭猫鼬连接
- testing - 集成测试,通配符忽略标签