2017-12-18 366 views
0

我有以下的類層次結構:爲什麼ListView中的DataTemplate不會自動更新其綁定?

namespace WpfBindingProblem 
{ 
    public class Top 
    { 
     public IList<Mid> MidList { get; } = new ObservableCollection<Mid>(); 
    } 

    public class Mid 
    { 
     public IList<Bot> BotList { get; } = new ObservableCollection<Bot>(); 
    } 

    public class Bot 
    { 
    } 
} 

而且我有這樣的XAML窗口:

<Window x:Class="WpfBindingProblem.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:WpfBindingProblem" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="217.267" Width="333.686"> 
    <Window.DataContext> 
     <local:Top/> 
    </Window.DataContext> 
    <Window.Resources> 
     <local:TriggersToString x:Key="TriggersToString"/> 
    </Window.Resources> 
    <Grid> 
     <ListView Margin="10" ItemsSource="{Binding MidList}" x:Name="ThatList"> 
      <ListView.Resources> 
       <DataTemplate DataType="{x:Type local:Mid}"> 
        <TextBlock Text="{Binding BotList, Converter={StaticResource TriggersToString}}" /> 
       </DataTemplate> 
      </ListView.Resources> 
      <ListView.ContextMenu> 
       <ContextMenu> 
        <MenuItem Header="Add mid" Click="AddMid"/> 
        <MenuItem Header="Add bot to selected mid" Click="AddBot" /> 
       </ContextMenu> 
      </ListView.ContextMenu> 
      <ListView.View> 
       <GridView> 
        <GridViewColumn/> 
       </GridView> 
      </ListView.View> 
     </ListView> 
    </Grid> 
</Window> 

有了這些處理程序:

namespace WpfBindingProblem 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void AddMid(object sender, RoutedEventArgs e) 
     { 
      if(DataContext is Top p) 
      { 
       p.MidList.Add(new Mid()); 
      } 
     } 

     private void AddBot(object sender, RoutedEventArgs e) 
     { 
      if(ThatList.SelectedItem is Mid c) 
      { 
       c.BotList.Add(new Bot()); 
      } 
     } 
    } 
} 

而這種轉換器(作爲一個替身適用於任意轉換器):

namespace WpfBindingProblem 
{ 
    [ValueConversion(typeof(IList<Bot>), typeof(string))] 
    public class TriggersToString : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      if(value is IList<Bot> list) 
      { 
       return list.Count.ToString(); 
      } 
      throw new InvalidOperationException(); 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      throw new InvalidOperationException(); 
     } 
    } 
} 

在出現,當我們運行這個例子,我可以右擊並選擇「添加中旬」這樣的Mid一個實例添加到Top數據上下文和列表視圖相應更新,顯示窗口號碼0(如變換邏輯)

然而,當我點擊「添加漫遊器來選擇中間」,的Bot一個實例被添加到所選擇的Mid(I可以驗證這一點使用斷點),但是列表視圖是沒有相應的更新(我預計0改爲1,但轉換器不爲Mid特定情況下再次調用)。

爲什麼會發生這種變化不會觸發GUI的更新?

我知道我可以通過一些竅門解決這個問題(比如將數據上下文設置爲null並返回,或者可能通過使用依賴屬性調用顯式更新),但有兩個原因可以避免這種情況:

  • 我的實際代碼比這個MCVE更復雜,它看起來非常難看。
  • 我已經爲所有需要的ObservableCollection s和INotifyPropertyChanged接口灑滿了所有我的(實際)類,這樣我就不需要執行手動更新了 - 所以我覺得在這種情況下應該會發生自動更新,除非我錯過了一些東西。
+2

''應該更新UI。這是因爲當你添加一個元素時,BotList屬性本身不會改變。但是,它的計數屬性呢。 – Clemens

+0

@Clemens我明白了。但是,這是特定於此特定的轉換器,其邏輯可以在XAML中複製。有沒有更通用的方法,以便我可以使用任意的轉換器? –

+0

我不知道任何。通過在BotList中添加或刪除元素,{Binding BotList}不會被觸發。 – Clemens

回答

1

爲什麼會發生這種變化不會觸發GUI的更新?

因爲綁定的源屬性(BotList)沒有更新。只有當綁定的屬性數據更新的轉換器被調用。

你可以使用一個MultiBinding通過@Sinatr的建議,或者你可以

  • 直接綁定到集合的Count屬性:

    <TextBlock Text="{Binding BotList.Count}" /> 
    
  • 實現在MidINotifyPropertyChanged接口並在每次向其中添加項目時提高BotList屬性的PropertyChanged事件。處理CollectionChanged

您也可以將您的轉換邏輯視圖模型,結合這一個的屬性,也提高PropertyChanged它,只要你想要的結合被刷新。

1

您可以使用多綁定:

<TextBlock> 
    <TextBlock.Text> 
     <MultiBinding Converter="{StaticResource TriggersToString}"> 
      <Binding Path="BotList" /> 
      <Binding Path="BotList.Count" /> 
     </MultiBinding> 
    </TextBlock.Text> 
</TextBlock> 

和多值轉換器:

public class TriggersToString : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) => 
     (values[0] as IList<Bot>)?.Count.ToString(); // first binding 

    ... 
} 

這樣,每當任何綁定更新的轉換器被調用。

相關問題