WPF的SelectedValue不是从视图模型属性
更新我在我的主窗口下面的下拉框: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 我该如何解决此问题?
选定的项目必须被视为等于集合中的项目。但是,您始终返回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>
如果您更愿意保持Language
类Languages
,刚恢复SelectedValuePath="Id"
的组合框,并设置第一Binding
的Path
在MultiBinding
来代替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
再次被设置 - 它已经具有相同的值。如果你省略了递归守护,LanguageId
的set
块会再次提升PropertyChanged
为Languages
,这将会更新SelectedItem
等等 - 并且在堆栈溢出之前我们已经进入比赛。
谢谢埃德,这对我来说没有窍门,唯一的问题是显示的是正确的值,但它只在组合列表中正确转换,而不是在值字段中正确转换。但那是我需要弄清楚自己的东西:)。 –
值字段? –
*在组合框本身。在组合框本身中,它显示正确的语言名称但未翻译,并在下拉菜单中显示正确的翻译。 –
为什么你需要这个? '''NotifyPropertyChanged(“Languages”);''' – tym32167
所选项目必须等于集合中的一个项目。您总是从'语言'中返回一个新对象的集合,所以当选择改变时,您将ItemsSource设置为一个新的集合,该集合不包含刚刚选择的对象。所以ComboBox将所选项目的值丢弃为无效。你可以通过在'Language'上重写'Equals(Language)'来解决这个问题,或者(通过保持你的'语言'永久化并重用它们来更好地解决这个问题。 –
我需要这样做,因为我以这种方式更改了应用程序的语言,并且组合框中的项目也需要进行翻译。 –