2015-11-13 43 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)); 
     } 
    }