C# DataTemplate中的DependencyProperty不工作
我有一个让我发疯的问题 我在extensionclass中有一个DependecProperty Uid。我使用此选项设置静态“语言”字符串(工具:TranslateExtension.Uid=“MAINWINDOW\u ARTICLES”)。然后我使用Text=“{tools:Translate}”触发对集合语言的查找 这将调用ProvideValue方法,该方法将设置绑定。当直接从控件调用时,就像下面的第二个按钮一样,一切正常。但是当我在DataTemplate中执行此操作时,GetUid将返回一个空字符串,而不是“MAINWINDOW\u ARTICLES”。我似乎不明白为什么。有什么想法吗 下面可以看到一个可运行的示例:C# DataTemplate中的DependencyProperty不工作,c#,wpf,extension-methods,dependency-properties,C#,Wpf,Extension Methods,Dependency Properties,我有一个让我发疯的问题 我在extensionclass中有一个DependecProperty Uid。我使用此选项设置静态“语言”字符串(工具:TranslateExtension.Uid=“MAINWINDOW\u ARTICLES”)。然后我使用Text=“{tools:Translate}”触发对集合语言的查找 这将调用ProvideValue方法,该方法将设置绑定。当直接从控件调用时,就像下面的第二个按钮一样,一切正常。但是当我在DataTemplate中执行此操作时,GetUid将
<Window x:Class="DXSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tools="clr-namespace:DXSample"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="template">
<TextBlock tools:TranslateExtension.Uid="MAINWINDOW_ARTICLES" Text="{tools:Translate}"/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button ContentTemplate="{StaticResource template}"/>
<Button Content="{tools:Translate}" tools:TranslateExtension.Uid="MAINWINDOW_ARTICLES"/>
</StackPanel>
</Window>
[ContentProperty("Parameters")]
public class TranslateExtension : MarkupExtension
{
private DependencyProperty property;
private DependencyObject target;
private readonly Collection<BindingBase> parameters = new Collection<BindingBase>();
public Collection<BindingBase> Parameters
{
get { return parameters; }
}
private bool IsDataBound
{
get { return BindingOperations.IsDataBound(target, property); }
}
public static string GetUid(DependencyObject obj)
{
return (string)obj.GetValue(UidProperty);
}
public static void SetUid(DependencyObject obj, string value)
{
obj.SetValue(UidProperty, value);
}
public static readonly DependencyProperty UidProperty = DependencyProperty.RegisterAttached("Uid",
typeof(string),
typeof(TranslateExtension),
new UIPropertyMetadata(string.Empty));
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget service =
serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (service == null)
throw new InvalidOperationException("IProvideValueTarget service is unavailable");
DependencyProperty dependencyProperty = service.TargetProperty as DependencyProperty;
if (dependencyProperty == null)
throw new ArgumentException("Target property must be of type DependencyProperty");
DependencyObject dependencyObject = service.TargetObject as DependencyObject;
if (dependencyObject == null)
return this;
target = dependencyObject;
property = dependencyProperty;
BindDictionary();
return dependencyObject.GetValue(dependencyProperty);
}
private void element_Loaded(object sender, RoutedEventArgs e)
{
if (!IsDataBound)
BindDictionary();
}
private void element_Unloaded(object sender, RoutedEventArgs e)
{
if (IsDataBound)
BindingOperations.ClearBinding(target, property);
}
private void BindDictionary()
{
string uid = GetUid(target);
if (string.IsNullOrEmpty(uid))
{
Debug.WriteLine("UID NULL OR EMPTY");
return;
}
string vid = property.Name;
Binding binding = new Binding("Dictionary");
binding.Source = LanguageContext.Instance;
binding.Mode = BindingMode.TwoWay;
//LanguageConverter converter = new LanguageConverter(uid, vid);
if (parameters.Count == 0)
{
//binding.Converter = converter;
BindingOperations.SetBinding(target, property, binding);
}
else
{
MultiBinding multiBinding = new MultiBinding();
multiBinding.Mode = BindingMode.TwoWay;
//multiBinding.Converter = converter;
multiBinding.Bindings.Add(binding);
if (string.IsNullOrEmpty(uid))
{
Binding uidBinding = parameters[0] as Binding;
if (uidBinding == null)
throw new ArgumentException("Uid Binding parameter must be the first, and of type Binding");
}
foreach (Binding parameter in parameters)
multiBinding.Bindings.Add(parameter);
BindingOperations.SetBinding(target, property, multiBinding);
}
}
}
public class LanguageContext
{
static LanguageContext _languageContext;
public static LanguageContext Instance { get { if (_languageContext == null) _languageContext = new LanguageContext { Dictionary = "test string" }; return _languageContext; } }
public string Dictionary { get; set; }
}
[内容属性(“参数”)]
公共类TranslateExtension:MarkupExtension
{
私有财产;
私有依赖对象目标;
私有只读集合参数=新集合();
公共收集参数
{
获取{返回参数;}
}
私有布尔值是数据绑定的
{
获取{return BindingOperations.IsDataBound(target,property);}
}
公共静态字符串GetUid(DependencyObject obj)
{
返回(字符串)obj.GetValue(UidProperty);
}
公共静态void SetUid(DependencyObject对象,字符串值)
{
对象设置值(UidProperty,value);
}
公共静态只读DependencyProperty UidProperty=DependencyProperty.RegisterAttached(“Uid”,
类型(字符串),
类型(TranslateExtension),
新的UIPropertyMetadata(string.Empty));
公共覆盖对象ProviderValue(IServiceProvider服务提供程序)
{
IPR提供目标服务=
GetService(typeof(IProvideValueTarget))作为IProvideValueTarget;
if(服务==null)
抛出新的InvalidOperationException(“IProvideValueTarget服务不可用”);
DependencyProperty DependencyProperty=service.TargetProperty作为DependencyProperty;
if(dependencProperty==null)
抛出新ArgumentException(“目标属性必须是DependencyProperty类型”);
DependencyObject DependencyObject=service.TargetObject作为DependencyObject;
if(dependencyObject==null)
归还这个;
目标=依赖对象;
属性=从属属性;
BindDictionary();
返回dependencyObject.GetValue(dependencyProperty);
}
已加载私有void元素(对象发送方、路由目标)
{
如果(!IsDataBound)
BindDictionary();
}
私有void元素\u已卸载(对象发送方、路由目标)
{
如果(IsDataBound)
BindingOperations.ClearBinding(目标、属性);
}
私有void绑定字典()
{
字符串uid=GetUid(目标);
if(string.IsNullOrEmpty(uid))
{
Debug.WriteLine(“UID NULL或空”);
返回;
}
字符串vid=property.Name;
绑定=新绑定(“词典”);
binding.Source=LanguageContext.Instance;
binding.Mode=BindingMode.TwoWay;
//LanguageConverter=new LanguageConverter(uid,vid);
如果(parameters.Count==0)
{
//binding.Converter=转换器;
设置绑定(目标、属性、绑定);
}
其他的
{
MultiBinding MultiBinding=新的MultiBinding();
multiBinding.Mode=BindingMode.TwoWay;
//多重绑定。转换器=转换器;
multiBinding.Bindings.Add(绑定);
if(string.IsNullOrEmpty(uid))
{
Binding uidBinding=参数[0]作为绑定;
if(uidBinding==null)
抛出新ArgumentException(“Uid绑定参数必须是第一个,并且类型为Binding”);
}
foreach(参数中的绑定参数)
multiBinding.Bindings.Add(参数);
SetBinding(目标、属性、多重绑定);
}
}
}
公共类语言上下文
{
静态语言语境_语言语境;
公共静态LanguageContext实例{get{if(\u LanguageContext==null)\u LanguageContext=new LanguageContext{Dictionary=“test string”};返回{u LanguageContext;}}
公共字符串字典{get;set;}
}
在模板方案中,标记扩展在设置UidProperty之前得到解决(您可以通过在UidPropertyIdentifier中提供PropertyChangedCallback来验证):
这就是为什么在BindDictionary()方法中,您会得到uid的值作为默认值,即String.Empty
现在,为了解决这个问题,您必须在UidProperty发生更改时进行绑定(即使UidProperty绑定了某个值,您也需要更新绑定),这可以通过挂接到目标对象的依赖项属性描述符的AddValueChanged来实现。但是AddValueChanged有一些与之相关的内存泄漏问题(如果您感兴趣,可以阅读更多关于它的内容) 所以,您可以使用类来侦听依赖项属性的更改,并可以像这样绑定到字典:
public override object ProvideValue(IServiceProvider serviceProvider)
{
.....
DependencyObject dependencyObject = service.TargetObject as DependencyObject;
if (dependencyObject == null)
return this;
PropertyChangeNotifier notifier = new PropertyChangeNotifier(dependencyObject,
TranslateExtension.UidProperty);
notifier.ValueChanged += (s, e) => BindDictionary();
.......
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
.....
DependencyObject dependencyObject = service.TargetObject as DependencyObject;
if (dependencyObject == null)
return this;
PropertyChangeNotifier notifier = new PropertyChangeNotifier(dependencyObject,
TranslateExtension.UidProperty);
notifier.ValueChanged += (s, e) => BindDictionary();
.......
}