Wpf Depended combobox 在 .Net 4.5 中不显示 SelectedItem
Wpf Depended combobox doesn't dispaly SelectedItem in .Net 4.5
我有主组合框(类别)和依赖组合框(子类别)。我希望它在 window 打开时显示 SelectedItems。在 .Net 4.0 中一切正常,但在 .Net 4.5 中不起作用。我有两台装有这些 .Net 版本的计算机。
在 .net 4.5 中。只有主组合框显示 SelectedItem,依赖项不显示。我该如何解决?
我做了测试项目,有兴趣的可以复制粘贴。我不知道如何让它变小,sry。但它是简单、清晰的代码示例 100% 生成问题。
XAML:
<Window x:Class="GridTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Converter="clr-namespace:GridTest"
Title="TestWindow"
Height="300"
Width="300">
<Window.Resources>
<Converter:CategoryConverter x:Key="CategoryConverter"/>
</Window.Resources>
<Grid>
<DataGrid Name="_dataGrid"
CanUserAddRows="False"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0"
Name="_categories"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedCategory, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</ComboBox>
<ComboBox Grid.Column="1"
SelectedItem="{Binding SelectedSubcategory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource CategoryConverter}">
<Binding Path="Subcategories"/>
<Binding Path="SelectedItem"
ElementName="_categories"/>
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
代码:
public class CategoryConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] == null) return null;
var subcategories = values[0] as List<Subcategory>;
if (subcategories == null) return null;
var category = values[1] as Category;
if (category == null) return subcategories;
return subcategories.Where(g => g.CategoryId == category.Id);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public enum CategoryKinds
{
Car = 0,
Fruit = 1,
}
public class Category
{
public Int32 Id { get; set; }
public String Name { get; set; }
public override Boolean Equals(object obj)
{
var c = obj as Category;
if (c == null) return false;
return Id == c.Id;
}
}
public class Subcategory
{
public Int32 Id { get; set; }
public String Name { get; set; }
public Int32 CategoryId { get; set; }
public override Boolean Equals(object obj)
{
var sc = obj as Subcategory;
if (sc == null) return false;
return Id == sc.Id;
}
}
public class DataGridItem
{
public List<Category> Categories { get; set; }
public Category SelectedCategory { get; set; }
public List<Subcategory> Subcategories { get; set; }
public Subcategory SelectedSubcategory { get; set; }
public DataGridItem()
{
Categories = new List<Category>
{
new Category
{
Id = (Int32)CategoryKinds.Car, Name = "Car"
},
new Category
{
Id = (Int32)CategoryKinds.Fruit, Name = "Fruit"
}
};
Subcategories = new List<Subcategory>
{
new Subcategory
{
Id = 1,
Name = "Volvo",
CategoryId = (Int32) CategoryKinds.Car
},
new Subcategory
{
Id = 2,
Name = "Nissan",
CategoryId = (Int32) CategoryKinds.Car
},
new Subcategory
{
Id = 3,
Name = "Banana",
CategoryId = (Int32)CategoryKinds.Fruit
},
new Subcategory
{
Id = 4,
Name = "Lemon",
CategoryId = (Int32)CategoryKinds.Fruit
},
};
}
}
/// <summary>
/// Interaction logic for TestWindow.xaml
/// </summary>
public partial class TestWindow : Window
{
public List<DataGridItem> GridItems { get; set; }
public TestWindow()
{
InitializeComponent();
DataContext = this;
GridItems = new List<DataGridItem>
{
new DataGridItem
{
SelectedCategory = new Category
{
Id = (Int32)CategoryKinds.Car, Name = "Car"
},
SelectedSubcategory = new Subcategory
{
Id = 2,
Name = "Nissan",
CategoryId = (Int32) CategoryKinds.Car
}
},
new DataGridItem
{
SelectedCategory = new Category
{
Id = (Int32)CategoryKinds.Fruit, Name = "Fruit"
},
SelectedSubcategory = new Subcategory
{
Id = 4,
Name = "Lemon",
CategoryId = (Int32) CategoryKinds.Car
}
}
};
_dataGrid.ItemsSource = GridItems;
}
}
更新
使用 Ilan 建议的方法,charly_b 代码可以正常工作。
GridItems = new List<DataGridItem>
{
new DataGridItem(),
new DataGridItem()
};
GridItems[1].SelectedCategory = GridItems[1].Categories[0];
GridItems[1].SelectedSubcategory = GridItems[1].Subcategories[1];
GridItems[0].SelectedCategory = GridItems[0].Categories[1];
GridItems[0].SelectedSubcategory = GridItems[0].Subcategories[3];
此代码将导致:
- 水果 - 柠檬
- 汽车 - 日产
但我有一个解决方案,即使您设置的 SelectedItem 不属于 Combobox 的 ItemsSource 也能正常工作。您可以像这样覆盖 GetHashCode
方法:
public override int GetHashCode()
{
return Name.GetHashCode();
}
显然,在 .Net 4.5 中,一些在 Combobox 的 ItemsSource 中搜索 SelectedItem 的 WPF 方法与 .Net 4.0 有不同的实现,现在它们使用 GetHashCode 方法:)
Combobox SelectedItem 对象必须包含在 Combobox 的 ItemsSource 列表中。
为了使您的 Programm 正常工作,您可以将 SelectedSubCategory 属性 替换为以下代码:(我不会在生产代码中这样使用它,但它演示了它是如何工作的)
private Subcategory SelectedSubcategoryM;
public Subcategory SelectedSubcategory
{
get
{
return this.SelectedSubcategoryM;
}
set
{
this.SelectedSubcategoryM = (from aTest in this.Subcategories
where aTest.Id == value.Id
select aTest).Single();
}
}
尝试下一个更改,最佳做法是使用源集合项目来定义所选项目。首先,使用新项来定义选择(在 4.5 和 4 dot.net 版本中)是一个架构错误。其次,我建议您使用 mvvm 方法(包括 INotifyPropertyChange 实现)来开发 wpf 相关应用程序,然后必须将所有选择逻辑移至 ViewModel 并与后面的代码(xaml.cs 文件)分离。
public MainWindow()
{
InitializeComponent();
DataContext = this;
var f = new DataGridItem();
var firstselectedCategory = f.Categories.FirstOrDefault();
if (firstselectedCategory != null)
{
f.SelectedCategory = firstselectedCategory;
f.SelectedSubcategory =
f.Subcategories.FirstOrDefault(subcategory => subcategory.CategoryId == firstselectedCategory.Id);
}
else
{
f.SelectedCategory = null;
f.SelectedSubcategory = null;
}
var s = new DataGridItem();
var secondSelectedCategory = s.Categories.FirstOrDefault(category => !Equals(category, f.SelectedCategory));
if (secondSelectedCategory != null)
{
s.SelectedCategory = secondSelectedCategory;
s.SelectedSubcategory =
s.Subcategories.FirstOrDefault(subcategory => subcategory.CategoryId == secondSelectedCategory.Id);
}
else
{
s.SelectedCategory = null;
s.SelectedSubcategory = null;
}
GridItems = new List<DataGridItem>
{
f,s,
};
#region
//GridItems = new List<DataGridItem>
//{
// new DataGridItem
// {
// SelectedCategory = new Category
// {
// Id = (Int32) CategoryKinds.Car,
// Name = "Car"
// },
// SelectedSubcategory = new Subcategory
// {
// Id = 2,
// Name = "Nissan",
// CategoryId = (Int32) CategoryKinds.Car
// }
// },
// new DataGridItem
// {
// SelectedCategory = new Category
// {
// Id = (Int32) CategoryKinds.Fruit,
// Name = "Fruit"
// },
// SelectedSubcategory = new Subcategory
// {
// Id = 4,
// Name = "Lemon",
// CategoryId = (Int32) CategoryKinds.Fruit
// }
// }
//};
#endregion
_dataGrid.ItemsSource = GridItems;
}
xaml 代码未更改。
它看起来如何:
。
如果代码有问题,我很乐意提供帮助。
问候。
我有主组合框(类别)和依赖组合框(子类别)。我希望它在 window 打开时显示 SelectedItems。在 .Net 4.0 中一切正常,但在 .Net 4.5 中不起作用。我有两台装有这些 .Net 版本的计算机。
在 .net 4.5 中。只有主组合框显示 SelectedItem,依赖项不显示。我该如何解决?
我做了测试项目,有兴趣的可以复制粘贴。我不知道如何让它变小,sry。但它是简单、清晰的代码示例 100% 生成问题。
XAML:
<Window x:Class="GridTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Converter="clr-namespace:GridTest"
Title="TestWindow"
Height="300"
Width="300">
<Window.Resources>
<Converter:CategoryConverter x:Key="CategoryConverter"/>
</Window.Resources>
<Grid>
<DataGrid Name="_dataGrid"
CanUserAddRows="False"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0"
Name="_categories"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedCategory, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</ComboBox>
<ComboBox Grid.Column="1"
SelectedItem="{Binding SelectedSubcategory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource CategoryConverter}">
<Binding Path="Subcategories"/>
<Binding Path="SelectedItem"
ElementName="_categories"/>
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
代码:
public class CategoryConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] == null) return null;
var subcategories = values[0] as List<Subcategory>;
if (subcategories == null) return null;
var category = values[1] as Category;
if (category == null) return subcategories;
return subcategories.Where(g => g.CategoryId == category.Id);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public enum CategoryKinds
{
Car = 0,
Fruit = 1,
}
public class Category
{
public Int32 Id { get; set; }
public String Name { get; set; }
public override Boolean Equals(object obj)
{
var c = obj as Category;
if (c == null) return false;
return Id == c.Id;
}
}
public class Subcategory
{
public Int32 Id { get; set; }
public String Name { get; set; }
public Int32 CategoryId { get; set; }
public override Boolean Equals(object obj)
{
var sc = obj as Subcategory;
if (sc == null) return false;
return Id == sc.Id;
}
}
public class DataGridItem
{
public List<Category> Categories { get; set; }
public Category SelectedCategory { get; set; }
public List<Subcategory> Subcategories { get; set; }
public Subcategory SelectedSubcategory { get; set; }
public DataGridItem()
{
Categories = new List<Category>
{
new Category
{
Id = (Int32)CategoryKinds.Car, Name = "Car"
},
new Category
{
Id = (Int32)CategoryKinds.Fruit, Name = "Fruit"
}
};
Subcategories = new List<Subcategory>
{
new Subcategory
{
Id = 1,
Name = "Volvo",
CategoryId = (Int32) CategoryKinds.Car
},
new Subcategory
{
Id = 2,
Name = "Nissan",
CategoryId = (Int32) CategoryKinds.Car
},
new Subcategory
{
Id = 3,
Name = "Banana",
CategoryId = (Int32)CategoryKinds.Fruit
},
new Subcategory
{
Id = 4,
Name = "Lemon",
CategoryId = (Int32)CategoryKinds.Fruit
},
};
}
}
/// <summary>
/// Interaction logic for TestWindow.xaml
/// </summary>
public partial class TestWindow : Window
{
public List<DataGridItem> GridItems { get; set; }
public TestWindow()
{
InitializeComponent();
DataContext = this;
GridItems = new List<DataGridItem>
{
new DataGridItem
{
SelectedCategory = new Category
{
Id = (Int32)CategoryKinds.Car, Name = "Car"
},
SelectedSubcategory = new Subcategory
{
Id = 2,
Name = "Nissan",
CategoryId = (Int32) CategoryKinds.Car
}
},
new DataGridItem
{
SelectedCategory = new Category
{
Id = (Int32)CategoryKinds.Fruit, Name = "Fruit"
},
SelectedSubcategory = new Subcategory
{
Id = 4,
Name = "Lemon",
CategoryId = (Int32) CategoryKinds.Car
}
}
};
_dataGrid.ItemsSource = GridItems;
}
}
更新
使用 Ilan 建议的方法,charly_b 代码可以正常工作。
GridItems = new List<DataGridItem>
{
new DataGridItem(),
new DataGridItem()
};
GridItems[1].SelectedCategory = GridItems[1].Categories[0];
GridItems[1].SelectedSubcategory = GridItems[1].Subcategories[1];
GridItems[0].SelectedCategory = GridItems[0].Categories[1];
GridItems[0].SelectedSubcategory = GridItems[0].Subcategories[3];
此代码将导致:
- 水果 - 柠檬
- 汽车 - 日产
但我有一个解决方案,即使您设置的 SelectedItem 不属于 Combobox 的 ItemsSource 也能正常工作。您可以像这样覆盖 GetHashCode
方法:
public override int GetHashCode()
{
return Name.GetHashCode();
}
显然,在 .Net 4.5 中,一些在 Combobox 的 ItemsSource 中搜索 SelectedItem 的 WPF 方法与 .Net 4.0 有不同的实现,现在它们使用 GetHashCode 方法:)
Combobox SelectedItem 对象必须包含在 Combobox 的 ItemsSource 列表中。
为了使您的 Programm 正常工作,您可以将 SelectedSubCategory 属性 替换为以下代码:(我不会在生产代码中这样使用它,但它演示了它是如何工作的)
private Subcategory SelectedSubcategoryM;
public Subcategory SelectedSubcategory
{
get
{
return this.SelectedSubcategoryM;
}
set
{
this.SelectedSubcategoryM = (from aTest in this.Subcategories
where aTest.Id == value.Id
select aTest).Single();
}
}
尝试下一个更改,最佳做法是使用源集合项目来定义所选项目。首先,使用新项来定义选择(在 4.5 和 4 dot.net 版本中)是一个架构错误。其次,我建议您使用 mvvm 方法(包括 INotifyPropertyChange 实现)来开发 wpf 相关应用程序,然后必须将所有选择逻辑移至 ViewModel 并与后面的代码(xaml.cs 文件)分离。
public MainWindow()
{
InitializeComponent();
DataContext = this;
var f = new DataGridItem();
var firstselectedCategory = f.Categories.FirstOrDefault();
if (firstselectedCategory != null)
{
f.SelectedCategory = firstselectedCategory;
f.SelectedSubcategory =
f.Subcategories.FirstOrDefault(subcategory => subcategory.CategoryId == firstselectedCategory.Id);
}
else
{
f.SelectedCategory = null;
f.SelectedSubcategory = null;
}
var s = new DataGridItem();
var secondSelectedCategory = s.Categories.FirstOrDefault(category => !Equals(category, f.SelectedCategory));
if (secondSelectedCategory != null)
{
s.SelectedCategory = secondSelectedCategory;
s.SelectedSubcategory =
s.Subcategories.FirstOrDefault(subcategory => subcategory.CategoryId == secondSelectedCategory.Id);
}
else
{
s.SelectedCategory = null;
s.SelectedSubcategory = null;
}
GridItems = new List<DataGridItem>
{
f,s,
};
#region
//GridItems = new List<DataGridItem>
//{
// new DataGridItem
// {
// SelectedCategory = new Category
// {
// Id = (Int32) CategoryKinds.Car,
// Name = "Car"
// },
// SelectedSubcategory = new Subcategory
// {
// Id = 2,
// Name = "Nissan",
// CategoryId = (Int32) CategoryKinds.Car
// }
// },
// new DataGridItem
// {
// SelectedCategory = new Category
// {
// Id = (Int32) CategoryKinds.Fruit,
// Name = "Fruit"
// },
// SelectedSubcategory = new Subcategory
// {
// Id = 4,
// Name = "Lemon",
// CategoryId = (Int32) CategoryKinds.Fruit
// }
// }
//};
#endregion
_dataGrid.ItemsSource = GridItems;
}
xaml 代码未更改。
它看起来如何: