2015-11-13 2 views
2

У меня есть следующий конвертер:Связывание с текущим DataContext с преобразователем с помощью х: Bind

public class MyConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, string language) 
    { 
     Debug.WriteLine(value.GetType());    

     //The rest of the code    
    } 

    public object ConvertBack(object value, Type targetType, object parameter, string language) 
    { 
     throw new NotImplementedException(); 
    } 
} 

И в XAML, который пытается использовать преобразователь:

<ListView ItemsSource="{x:Bind StickersCVS.View}" > 
    <ListView.ItemTemplate> 
     <DataTemplate x:DataType="models:StickerCategory"> 
      <TextBlock Foreground="{x:Bind Converter={StaticResource MyConverter}}"/> 
     </DataTemplate> 
    </ListView.ItemTemplate> 
</ListView> 

Это дает мне NPE в value.GetType() , по-видимому, полученное значение равно null.

Если изменить следующую часть:

<TextBlock Foreground="{x:Bind Converter={StaticResource MyConverter}}"/>

к

<TextBlock Foreground="{Binding Converter={StaticResource MyConverter}}"/>

Затем он работает. Debug правильно выводит StickerCategory как тип значения. Любая причина, почему x:Bind проходит null в конвертер и как мне заставить его работать с x:Bind? Я пытаюсь передать DataContext в свой конвертер.

+2

Обратите внимание, что режим по умолчанию для х: Привязка является одноразовая (по умолчанию Связывание с OneWay). Может ли это быть основной причиной? Может быть, значение действительно имеет нулевое значение при первом привязке .... – gregkalapos

+0

Нет, нет кости :(, я попытался указать режим привязки: '' Все еще NPE. Спасибо в любом случае :) –

+1

вы действительно уверены в этом? Очень правдоподобно, что с «Mode = OneWay» с или без него он будет первым, но без него он никогда не изменится. Итак, поскольку у вас нет нулевой проверки в вашем конвертере, ваша программа, вероятно, никогда не приходит к делу, что на самом деле _is_ значение присутствует ?! –

ответ

1

{x:Bind} использует сгенерированный код для достижения своих преимуществ и при использовании разных Path в {x:Bind} сгенерированный код имеет некоторые отличия.

Здесь я использую простой образец, например. Для полного образца, пожалуйста, проверьте на GitHub.

В образце, у меня есть ViewModel, как следующее:

public class MyViewModel 
{ 
    public MyViewModel() 
    { 
     MyList = new List<Item>() 
     { 
      new Item {Name="1",Number=1 }, 
      new Item {Name="2",Number=2 }, 
      new Item {Name="3",Number=3 } 
     }; 
    } 

    public List<Item> MyList { get; set; } 
} 

public class Item 
{ 
    public string Name { get; set; } 
    public int Number { get; set; } 

    public override string ToString() 
    { 
     return string.Format("Name: {0}, Number {1}", this.Name, this.Number); 
    } 
} 

Когда мы используем {x:Bind Name, Converter={StaticResource ItemConvert}} в MainPage.xaml

<ListView ItemsSource="{x:Bind ViewModel.MyList}"> 
    <ListView.ItemTemplate> 
     <DataTemplate x:DataType="local:Item"> 
      <TextBlock Text="{x:Bind Converter={StaticResource ItemConvert}}" /> 
     </DataTemplate> 
    </ListView.ItemTemplate> 
</ListView> 

Он генерирует следующий код в MainPage. g.cs

public void DataContextChangedHandler(global::Windows.UI.Xaml.FrameworkElement sender, global::Windows.UI.Xaml.DataContextChangedEventArgs args) 
{ 
    global::xBindWithConverter.Item data = args.NewValue as global::xBindWithConverter.Item; 
    if (args.NewValue != null && data == null) 
    { 
     throw new global::System.ArgumentException("Incorrect type passed into template. Based on the x:DataType global::xBindWithConverter.Item was expected."); 
    } 
    this.SetDataRoot(data); 
    this.Update(); 
} 

// IDataTemplateExtension 

public bool ProcessBinding(uint phase) 
{ 
    throw new global::System.NotImplementedException(); 
} 

public int ProcessBindings(global::Windows.UI.Xaml.Controls.ContainerContentChangingEventArgs args) 
{ 
    int nextPhase = -1; 
    switch(args.Phase) 
    { 
     case 0: 
      nextPhase = -1; 
      this.SetDataRoot(args.Item as global::xBindWithConverter.Item); 
      if (!removedDataContextHandler) 
      { 
       removedDataContextHandler = true; 
       ((global::Windows.UI.Xaml.Controls.TextBlock)args.ItemContainer.ContentTemplateRoot).DataContextChanged -= this.DataContextChangedHandler; 
      } 
      this.initialized = true; 
      break; 
    } 
    this.Update_((global::xBindWithConverter.Item) args.Item, 1 << (int)args.Phase); 
    return nextPhase; 
} 
... 
public void Update() 
{ 
    this.Update_(this.dataRoot, NOT_PHASED); 
    this.initialized = true; 
} 

И

global::Windows.UI.Xaml.Controls.TextBlock element3 = (global::Windows.UI.Xaml.Controls.TextBlock)target; 
MainPage_obj3_Bindings bindings = new MainPage_obj3_Bindings(); 
returnValue = bindings; 
bindings.SetDataRoot((global::xBindWithConverter.Item) element3.DataContext); 
bindings.SetConverterLookupRoot(this); 
element3.DataContextChanged += bindings.DataContextChangedHandler; 
global::Windows.UI.Xaml.DataTemplate.SetExtensionInstance(element3, bindings); 

При инициализации Page, element3.DataContextChanged += bindings.DataContextChangedHandler; будет выполняться в первую очередь. После этого метод DataContextChangedHandler будет вызываться как DataContextChanged Событие возникает при инициализации. И метод ProcessBindings будет выполнен для обновления элемента контейнера элемента списка с привязанными данными.

В способе DataContextChangedHandler он вызывает метод this.Update();, который в конце вызывает метод Update_(global::xBindWithConverter.Item obj, int phase). Но когда вызывается метод DataContextChangedHandler, его значение args.NewValue равно null, поэтому obj в Update_(global::xBindWithConverter.Item obj, int phase) метод также null.

А при использовании {x:Bind Converter={StaticResource ItemConvert}} в XAML, сгенерированный код Update_(global::xBindWithConverter.Item obj, int phase) является:

// Update methods for each path node used in binding steps. 
private void Update_(global::xBindWithConverter.Item obj, int phase) 
{ 
    if((phase & ((1 << 0) | NOT_PHASED)) != 0) 
    { 
     XamlBindingSetters.Set_Windows_UI_Xaml_Controls_TextBlock_Text(this.obj3.Target as global::Windows.UI.Xaml.Controls.TextBlock, (global::System.String)this.LookupConverter("ItemConvert").Convert(obj, typeof(global::System.String), null, null), null); 
    } 
} 

Как obj является null, поэтому value в вашем Convert является null и, наконец, он бросает NPE в value.GetType().

Но если мы будем использовать другой Path в {x:Bind} как {x:Bind Name, Converter={StaticResource ItemConvert}}, сгенерированный код Update_(global::xBindWithConverter.Item obj, int phase) отличается:

// Update methods for each path node used in binding steps. 
private void Update_(global::xBindWithConverter.Item obj, int phase) 
{ 
    if (obj != null) 
    { 
     if ((phase & (NOT_PHASED | (1 << 0))) != 0) 
     { 
      this.Update_Name(obj.Name, phase); 
     } 
    } 
} 
private void Update_Name(global::System.String obj, int phase) 
{ 
    if((phase & ((1 << 0) | NOT_PHASED)) != 0) 
    { 
     XamlBindingSetters.Set_Windows_UI_Xaml_Controls_TextBlock_Text(this.obj3.Target as global::Windows.UI.Xaml.Controls.TextBlock, (global::System.String)this.LookupConverter("ItemConvert").Convert(obj, typeof(global::System.String), null, null), null); 
    } 
} 

Он будет определить, является ли obj является null. Поэтому метод XamlBindingSetters.Set_Windows_UI_Xaml_Controls_TextBlock_Text здесь не будет вызываться, а NullReferenceException не будет.

Чтобы решить эту проблему, так же, как сказал @Markus Hütter, вы можете добавить null проверку в конверторе, как:

public object Convert(object value, Type targetType, object parameter, string language) 
{ 
    if (value != null) 
    { 
     System.Diagnostics.Debug.WriteLine(value.GetType()); 
     return value.ToString(); 
    } 
    else 
    { 
     System.Diagnostics.Debug.WriteLine("value is null"); 
     return null; 
    } 
} 
1

Я не думаю, что это хорошая идея, чтобы использовать х: связывают с конвертер в целом, цель использования x: bing - увеличить производительность, в то время как конвертер значительно повлияет на производительность кода. вы можете легко создать поле readonly в своей модели, а когда ваши данные будут изменены, вы можете поднять событие с измененными свойствами и преобразовать его там.

Например,

[JsonIgnore] 
    public double IsUnreadOpacity 
    { 
     get { return (IsUnread) ? 1 : 0; } 
    } 

public bool IsUnread 
    { 
     get { return _isUnread; } 
     set 
     { 
      if (value == _isUnread) 
       return; 
      Set(ref _isUnread, value); 
      RaisePropertyChanged(nameof(IsUnreadOpacity)); 
     } 
    } 

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

  • Нет связанных вопросов^_^