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];

此代码将导致:

  1. 水果 - 柠檬
  2. 汽车 - 日产

但我有一个解决方案,即使您设置的 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 代码未更改。 它看起来如何: 。 如果代码有问题,我很乐意提供帮助。 问候。