2015-02-02 2 views
1

나는 미치게 만드는 문제가 있습니다.DataTemplate의 DependencyProperty가 작동하지 않습니다.

나는 extensionclass에 종속 속성 인 Uid를 가지고 있습니다. 이 정적 "언어"문자열을 설정하려면 (tools : 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 (당신이 UidPropertyIdentifier에 PropertyChangedCallback을 제공하여 다음을 확인 할 수 있습니다) 설정의 사전 해결지고 :

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() 메서드에서 기본값 인 String.Empty로 uid 값을 가져옵니다.


이제 솔루션을 어떻게 든 당신은 에있는 UidProperty이이 변경됩니다 때에 접선으로 할 수있는 (심지어 경우에 UidProperty 어떤 값으로 바인더 제본되어, 당신이 당신의 바인딩을 업데이트해야) 바인딩 할 대상 개체에 대한 종속성 속성 설명자의 AddValueChanged입니다. 하지만 AddValueChanged는 메모리 누수 문제가 있습니다 (intereseted 경우 here에 대해 자세히 읽을 수 있습니다).

:

그럼, 당신이 할 수있는 것은 프로퍼티의 변경을 의존성을 듣고 PropertyChangeNotifier 클래스를 사용하여 다음과 같이 사전에 결합 할 수있다