Привязка WPF ComboBox к не строковому объекту

Я использую MVVM (MVVM Light Toolkit) и имею свойство модели представления, которое предоставляет список объектов. Объекты содержат два свойства, обе строки, которые соответствуют аббревиатуре и описанию. Я хочу, чтобы ComboBox отображал пару как «аббревиатура - описание». Если я использую шаблон данных, он делает это легко.

У меня есть еще одно свойство в модели представления, которое представляет объект, который должен отображаться как выбранный - выбранный элемент в ComboBox. Я привязываю ItemsSource к списку, поскольку он представляет собой совокупность доступных выборов, и пытаюсь привязать SelectedItem к этому объекту. Я убиваю себя, пытаясь понять, почему я не могу заставить его работать, и с каждым часом чувствую себя мошенником.

Пытаясь понять, почему это работает, я применил тот же подход только со списком строк и выбранной строкой. Это прекрасно работает. Итак, это явно имеет какое-то отношение к типизации ... может быть, что-то в выборе равенства? Или, может быть, это связано с шаблоном данных?

Вот XAML:

<Window x:Class="MvvmLight1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="300"
        Width="300"
        DataContext="{Binding Main, Source={StaticResource Locator}}">

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
            <DataTemplate x:Key="DataTemplate1">
                <StackPanel Orientation="Horizontal">
                    <TextBlock TextWrapping="Wrap" Text="{Binding CourtCode}"/>
                    <TextBlock TextWrapping="Wrap" Text=" - "/>
                    <TextBlock TextWrapping="Wrap" Text="{Binding CourtDescription}"/>
                </StackPanel>
            </DataTemplate>
        </ResourceDictionary>
    </Window.Resources>

    <Grid x:Name="LayoutRoot">
        <ComboBox x:Name="cmbAbbrevDescriptions" Height="35" Margin="25,75,25,25"  VerticalAlignment="Top" ItemsSource="{Binding Codes}" ItemTemplate="{DynamicResource DataTemplate1}" SelectedItem="{Binding selectedCode}" />
        <ComboBox x:Name="cmbStrings" Height="35" Margin="25" VerticalAlignment="Top" ItemsSource="{Binding strs}" SelectedItem="{Binding selectedStr}"/>
    </Grid>
</Window>

И, если это полезно, вот ViewModel:

using GalaSoft.MvvmLight;
using MvvmLight1.Model;
using System.Collections.Generic;

namespace MvvmLight1.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        public const string CodesPropertyName = "Codes";
        private List<Court> _codes = null;
        public List<Court> Codes
        {
            get
            {
                return _codes;
            }

            set
            {
                if (_codes == value)
                {
                    return;
                }

                var oldValue = _codes;
                _codes = value;

                // Update bindings and broadcast change using GalaSoft.Utility.Messenging
                RaisePropertyChanged(CodesPropertyName, oldValue, value, true);
            }
        }

        public const string selectedCodePropertyName = "selectedCode";
        private Court _selectedCode = null;
        public Court selectedCode
        {
            get
            {
                return _selectedCode;
            }

            set
            {
                if (_selectedCode == value)
                {
                    return;
                }

                var oldValue = _selectedCode;
                _selectedCode = value;

                // Update bindings and broadcast change using GalaSoft.Utility.Messenging
                RaisePropertyChanged(selectedCodePropertyName, oldValue, value, true);
            }
        }

        public const string strsPropertyName = "strs";
        private List<string> _strs = null;
        public List<string> strs
        {
            get
            {
                return _strs;
            }

            set
            {
                if (_strs == value)
                {
                    return;
                }

                var oldValue = _strs;
                _strs = value;

                // Update bindings and broadcast change using GalaSoft.Utility.Messenging
                RaisePropertyChanged(strsPropertyName, oldValue, value, true);
            }
        }

        public const string selectedStrPropertyName = "selectedStr";
        private string _selectedStr = "";
        public string selectedStr
        {
            get
            {
                return _selectedStr;
            }

            set
            {
                if (_selectedStr == value)
                {
                    return;
                }

                var oldValue = _selectedStr;
                _selectedStr = value;

                // Update bindings and broadcast change using GalaSoft.Utility.Messenging
                RaisePropertyChanged(selectedStrPropertyName, oldValue, value, true);
            }
        }


        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            Codes = new List<Court>();

            Court code1 = new Court();
            code1.CourtCode = "ABC";
            code1.CourtDescription = "A Court";

            Court code2 = new Court();
            code2.CourtCode = "DEF";
            code2.CourtDescription = "Second Court";

            Codes.Add(code1);
            Codes.Add(code2);


            Court code3 = new Court();
            code3.CourtCode = "DEF";
            code3.CourtDescription = "Second Court";
            selectedCode = code3;

            selectedStr = "Hello";
            strs = new List<string>();
            strs.Add("Goodbye");
            strs.Add("Hello");
            strs.Add("Ciao");

        }
    }
}

А вот и до смешного тривиальный класс:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvvmLight1.Model
{
    public class Court
    {
        public string CourtCode { get; set; }

        public string CourtDescription { get; set; }
    }
}

Спасибо!


person Mike L    schedule 07.06.2010    source источник


Ответы (2)


Среда выполнения не знает, что code2 и code3 должны быть равны.

см. http://msdn.microsoft.com/en-us/library/ms173147(VS.80).aspx

public override bool Equals(object o)
{
   var court = o as Court;
   if(court == null)
      return false;
   return CourtCode == court.CourtCode;
}
person Oggy    schedule 07.06.2010
comment
Потрясающие! Это не имело ничего общего с WPF, привязкой и т. Д. Это была просто глупая ошибка в отношении равенства типов. Большое спасибо! - person Mike L; 07.06.2010

Ваш SelectedCode Code3 отсутствует в коллекции кодов. Добавьте его, чтобы выделение работало должным образом.

Ваш selectedStr работает, потому что он находится в коллекции. [strs.Add("Hello");]

person Amsakanna    schedule 07.06.2010
comment
или переопределите метод equals в классе Court, не забудьте также переопределить метод GetHashCode. - person Oggy; 07.06.2010
comment
Правильно, code3 нет в коллекции кодов. Но это эквивалент code2 ... и здесь мне не хватает лодки. Я пытаюсь сказать из всех этих возможных типов, выберите тот тип, который соответствует этому. - person Mike L; 07.06.2010
comment
@Oggy - Если я понимаю, вы говорите, что ComboBox пытается установить выбранный элемент, но не может определить равенство между экземплярами объекта? - person Mike L; 07.06.2010