2015-11-13 47 views
2

我有以下轉換器:用x綁定到與轉換器電流的DataContext:綁定

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

注意,x的默認模式:綁定是一次性(綁定默認爲單向)。這可能是根本原因嗎?也許這個值在第一個綁定時真的是空的...... – gregkalapos

+0

不,沒有骰子:(我試着指定綁定模式: '' 仍NPE。無論如何感謝:) –

+1

你真的確定嗎?無論是否使用「Mode = OneWay」,它都會首先爲空 - 但如果沒有它,它永遠不會改變。所以既然你沒有在你的轉換器中有一個空檢查,你的程序可能永遠不會出現這樣的情況,即實際上存在一個值?! –

回答

1

{x:Bind}使用生成的代碼來實現其優點,並在{x:Bind}中使用不同的Path時,生成的代碼有一些差異。

這裏我使用一個簡單的例子。對於完整的樣品,請在GitHub檢查。

在樣品,我有一個視圖模型像以下:

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

當初始化頁面,element3.DataContextChanged += bindings.DataContextChangedHandler;將首先執行。在此之後,DataContextChangedHandler方法將被稱爲DataContextChanged事件在初始化時引發。並且將執行ProcessBindings方法來更新帶有綁定數據的列表項容器元素。

DataContextChangedHandler方法中,它調用this.Update();方法,最後調用Update_(global::xBindWithConverter.Item obj, int phase)方法。但是當調用DataContextChangedHandler方法時,它的值args.NewValue的值爲null,所以objUpdate_(global::xBindWithConverter.Item obj, int phase)方法也是null

而在XAML使用{x:Bind 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((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); 
    } 
} 

由於objnull,所以在你的Convertnullvalue,最後它在value.GetType()拋出一個NPE。

但是,如果我們在{x:Bind}使用另一個Path{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許特爾說,你可以在你的轉換器像添加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個好主意:用綁定一般而言,使用x:bing的目標是提高性能,而轉換器將顯着影響代碼的性能。您可以在模型中輕鬆創建只讀字段,並且在更改數據時,您可以引發屬性更改的事件並將其轉換。

例如,

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