2015-02-02 2 views
1

У меня проблема, которая сводит меня с ума.DependencyProperty в DataTemplate не работает

У меня есть свойство dependecy, Uid, в классе расширения. Я это установил статическую «языковую» строку (инструменты: TranslateExtension.Uid = «MAINWINDOW_ARTICLES»). Затем я использую Text = "{tools: Translate}", чтобы вызвать поиск заданного языка.

Это вызовет метод ProvideValue, который установит привязку. При вызове этого непосредственно из элемента управления, как и на второй кнопке ниже, все работает нормально. Но когда я делаю это из DataTemplate, GetUid вернет пустую строку вместо «MAINWINDOW_ARTICLES». Кажется, я не понимаю, почему. Есть идеи?

исполняемого примера можно увидеть ниже:

<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; } 
} 

ответ

1

В сценарии шаблона расширение разметки становится решено до установки UidProperty (вы можете проверить, что, предоставляя PropertyChangedCallback в UidPropertyIdentifier):

public static readonly DependencyProperty UidProperty = 
    DependencyProperty.RegisterAttached("Uid", typeof(string), 
             typeof(TranslateExtension), 
          new UIPropertyMetadata(string.Empty, OnUidPropertyChanged)); 

private static void OnUidPropertyChanged(DependencyObject d, 
             DependencyPropertyChangedEventArgs args) 
{ 
    // Breakpoint will hit after ProvideValue in case of template. 
} 

Именно поэтому в методе BindDictionary() вы получаете значение uid в качестве значения по умолчанию, которое является String.Empty.


Теперь, для решения как-то вы должны сделать обязательное, когда UidProperty получает изменен (даже в случае UidProperty является переплетен с некоторым значением, вам необходимо обновить привязки), которые вы можете сделать, подцепив на AddValueИзменяет дескриптор свойства зависимостей для целевого объекта. Но у AddValueChanged есть некоторые проблемы с утечкой памяти, связанные с ним (вы можете узнать об этом here, если это интересно).

Итак, что вы можете сделать, это использовать PropertyChangeNotifier класс для прослушивания зависимостей изменения свойств и могут связываться словарь как это:

Смежные вопросы