2017-07-06 28 views
0

更新我在我的主窗口下面的下拉框:WPF的SelectedValue不是從視圖模型屬性

<ComboBox SelectedValue="{Binding LanguageId}" SelectedValuePath="Id" DisplayMemberPath="Name" ItemsSource="{Binding Languages}"/> 
在我的ViewModel

我有以下兩個屬性設置:

public List<Language> Languages 
{ 
    get 
    { 
    return new List<Language>() 
    { 
    new Language { Id = 0, Name = ml.ml_string(100, "Language1") }, 
    new Language { Id = 1, Name = ml.ml_string(101, "Language2") }, 
    new Language { Id = 2, Name = ml.ml_string(102, "Language3") }, 
    new Language { Id = 3, Name = ml.ml_string(103, "Language4") } 
    }; 
    } 
} 

public int LanguageId 
{ 
    get 
    { 
    return _languageId; 
    } 
    set 
    { 
    _languageId = value; 
    NotifyPropertyChanged("Languages"); 
    NotifyPropertyChanged(); 
    } 
} 

所以我想是要在組合框中選擇一種語言後通知我的語言屬性,但是當前當我這樣做時,它根本不顯示任何值(請參閱圖片): combobox error 我該如何解決此問題?

+0

爲什麼你需要這個? '''NotifyPropertyChanged(「Languages」);''' – tym32167

+0

所選項目必須等於集合中的一個項目。您總是從'語言'中返回一個新對象的集合,所以當選擇改變時,您將ItemsSource設置爲一個新的集合,該集合不包含剛剛選擇的對象。所以ComboBox將所選項目的值丟棄爲無效。你可以通過在'Language'上重寫'Equals(Language)'來解決這個問題,或者(通過保持你的'語言'永久化並重用它們來更好地解決這個問題。 –

+0

我需要這樣做,因爲我以這種方式更改了應用程序的語言,並且組合框中的項目也需要進行翻譯。 –

回答

0

選定的項目必須被視爲等於集合中的項目。但是,您始終返回Languages的新對象集合,因此當選擇更改時,您將ComboBox.ItemsSource設置爲不包含剛選擇的對象的新集合。因此,ComboBox將所選項目值丟棄爲無效。正如你發現的那樣,使用SelectedValuePath不能解決這個問題。

你可以通過重寫Equals(Language)Language通過保持你的Languages圍繞永久和重用他們解決這個問題,或者(更好)。

如果你需要翻譯Name屬性,你可以這樣做with a value converter

我建議不要覆蓋Equals()的原因是,在C#中,你習慣於假設比較引用類型的兩個實例意味着Object.ReferenceEquals(),並且打破該假設會導致錯誤。我從經驗講。

由於我們不再使用Name屬性,我們可以只改變Languages到語言ID的List<int> - 在這種情況下,我們可以把它只讀的,從來就不願去重新創建列表。我在這裏完成了。

public class LanguageNameConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (values[0] is int) 
     { 
      var itemLanguageId = (int)values[0]; 
      var displayLanguageId = (int)values[1]; 

      // Do stuff here to get the translated name of the item language 
      var itemLanguageNameTranslated = $"Name of language {itemLanguageId} in language {displayLanguageId}"; 

      return itemLanguageNameTranslated; 
     } 
     else return values[0]?.GetType(); 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

XAML:

<ComboBox 
    SelectedValue="{Binding LanguageId}" 
    ItemsSource="{Binding Languages}" 
    > 
    <ComboBox.ItemTemplate> 
     <DataTemplate> 
      <Label> 
       <Label.Content> 
        <MultiBinding 
         Converter="{StaticResource LanguageNameConverter}" 
         > 
         <Binding Path="." /> 
         <Binding 
          Path="DataContext.LanguageId" 
          RelativeSource="{RelativeSource AncestorType=ComboBox}" 
          /> 
        </MultiBinding> 
       </Label.Content> 
      </Label> 
     </DataTemplate> 
    </ComboBox.ItemTemplate> 
</ComboBox> 

如果您更願意保持LanguageLanguages,剛恢復SelectedValuePath="Id"的組合框,並設置第一BindingPathMultiBinding來代替Id.

<ComboBox 
    SelectedValue="{Binding LanguageId}" 
    SelectedValuePath="Id" 
    ... 

...

<MultiBinding 
    Converter="{StaticResource LanguageNameConverter}" 
    > 
    <Binding Path="Id" /> 
    <Binding 
     Path="DataContext.LanguageId" 
     RelativeSource="{RelativeSource AncestorType=ComboBox}" 
     /> 
</MultiBinding> 

等號清除溶液

這裏的Equals()解決方案;我們嘗試了它,但是在選擇更改之後,保​​留舊的SelectedItem並且舊的名稱以先前選擇的語言存在問題。這是因爲Equals()覆蓋的事情。所以我們使用了多值轉換器,它不需要任何解決方法或有趣的業務。

只是做事情WPF希望的方式,沒有人受傷。這是規則。

public class Language 
{ 
    public int Id { get; set; } 
    public String Name { get; set; } 

    public override bool Equals(object obj) 
    { 
     if (obj is Language) 
     { 
      return ((Language)obj).Id == Id; 
     } 
     return base.Equals(obj); 
    } 

    public override int GetHashCode() 
    { 
     return Id.GetHashCode(); 
    } 
} 

如果你選擇了這個方案,你還需要一個遞歸後衛添加到您的LanuageId屬性:

private int _languageId; 
public int LanguageId 
{ 
    get 
    { 
     return _languageId; 
    } 
    set 
    { 
     if (_languageId != value) 
     { 
      _languageId = value; 
      OnPropertyChanged("Languages"); 
      OnPropertyChanged(); 
     } 
    } 
} 

更換ComboBox.ItemsSource現在將導致實際SelectedItem改變到一個新的對象實例,其中更新SelectedValue,這將導致LanguageId再次被設置 - 它已經具有相同的值。如果你省略了遞歸守護,LanguageIdset塊會再次提升PropertyChangedLanguages,這將會更新SelectedItem等等 - 並且在堆棧溢出之前我們已經進入比賽。

+0

謝謝埃德,這對我來說沒有竅門,唯一的問題是顯示的是正確的值,但它只在組合列表中正確轉換,而不是在值字段中正確轉換。但那是我需要弄清楚自己的東西:)。 –

+0

值字段? –

+0

*在組合框本身。在組合框本身中,它顯示正確的語言名稱但未翻譯,並在下拉菜單中顯示正確的翻譯。 –

相關問題