2017-07-06 55 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

*在组合框本身。在组合框本身中,它显示正确的语言名称但未翻译,并在下拉菜单中显示正确的翻译。 –

相关问题