ComboBox ItemTemplate работает только в выпадающем меню
Я пытаюсь показать ComboBox, чей элемент ItemsSource представляет собой набор элементов управления (он является частью PropertyGrid, ComboBox должен отображать имена элементов управления, и пользователь должен иметь возможность выбрать один из элементов управления). Вот чрезвычайно упрощенное воспроизведение проблемы:
<ComboBox ItemsSource="{Binding GroupBoxes}" SelectedValue="{Binding SelectedGroupBox}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
GroupBoxes и SelectedGroupBox - DependencyProperties типа ObservableCollection и GroupBox.
Работа Bindings - имена элементов управления отображаются в ComboBox-DropDown, и если я выберу другой элемент, я вижу, что свойство SelectedGroupBox обновлено правильно. Проблема: выбранный элемент никогда не отображается в ComboBox. Установка свойства SelectedGroupBox из кода также работает должным образом - ComboBox вызывает SelectionChanged и его SelectedValue верен, но он все еще не отображает текущее значение.
Если я делаю то же самое с любым другим типом класса, все работает так, как ожидалось.
Поиск ответа Я столкнулся с множеством сообщений от людей, имеющих похожие проблемы с звучанием, но почти все они были связующими пролетами, что здесь не так.
Edit:
Чтобы упростить проверку, вот код позади. Просто оставьте выше XAML в новом окне и код ниже в коде.
public MainWindow() {
InitializeComponent();
this.DataContext = this;
this.GroupBoxes = new ObservableCollection<GroupBox>();
this.GroupBoxes.Add(new GroupBox() { Name = "AAA", Header = "AAA", Height = 100, Background = Brushes.Purple });
this.GroupBoxes.Add(new GroupBox() { Name = "BBB", Header = "BBB", Height = 100, Background = Brushes.Purple });
this.GroupBoxes.Add(new GroupBox() { Name = "CCC", Header = "CCC", Height = 100, Background = Brushes.Purple });
this.GroupBoxes.Add(new GroupBox() { Name = "DDD", Header = "DDD", Height = 100, Background = Brushes.Purple });
this.GroupBoxes.Add(new GroupBox() { Name = "EEE", Header = "EEE", Height = 100, Background = Brushes.Purple });
}
#region GroupBoxesProperty
public static readonly DependencyProperty GroupBoxesProperty = DependencyProperty.Register(
"GroupBoxes", typeof(ObservableCollection<GroupBox>), typeof(MainWindow)
);
public ObservableCollection<GroupBox> GroupBoxes {
get { return (ObservableCollection<GroupBox>)GetValue(GroupBoxesProperty); }
set { SetValue(GroupBoxesProperty, value); }
}
#endregion
#region SelectedGroupBoxProperty
public static readonly DependencyProperty SelectedGroupBoxProperty = DependencyProperty.Register(
"SelectedGroupBox", typeof(GroupBox), typeof(MainWindow),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (s, e) => (s as MainWindow).OnSelectedGroupBoxChanged())
);
public GroupBox SelectedGroupBox {
get { return (GroupBox)GetValue(SelectedGroupBoxProperty); }
set { SetValue(SelectedGroupBoxProperty, value); }
}
void OnSelectedGroupBoxChanged() {
Console.WriteLine("selection is now " + this.SelectedGroupBox.Name);
}
#endregion
Ответы
Ответ 1
ComboBox по некоторым очень сложным причинам предоставляет свойство только для чтения, называемое SelectionBoxItem. Презентатор содержимого в шаблоне ComboBox связывается с этим свойством. Именно SelectionBoxItem предоставляет строковое представление элементов, отличных от UI, что позволяет вам видеть выбранное значение. Использование этого свойства - это то, что препятствует тому, чтобы контент-презентатор использовал шаблоны данных. Вот почему шаблон применяется к выпадающему, но не выбранному элементу. Вот часть шаблона ComboBox по умолчанию, вызывающая проблему:
<ContentPresenter IsHitTestVisible="false"
Margin="8,1,1,1"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
Однако вы можете создать свой собственный стиль ComboBox, который переопределяет ContentPresenter по умолчанию и использует SelectedItem вместо SelectionBoxItem и ItemTemplate вместо SelectionItemBoxTemplate. Это решит проблему.
Ответ 2
Используйте DisplayMemberPath
вместо привязки к имени:
<Combobox DisplayMemberPath="Name" ... />
Причина видимого вами поведения заключается в том, что вам нужно установить другое свойство для выбранного шаблона элемента: http://msdn.microsoft.com/en-us/library/system.windows.controls.combobox.selectionboxitemtemplate.aspx
Обновление: я написал свой ответ, не проверяя ваш код, извините за это. Теперь я прочитал ваш код и заметил, что вы привязываете свойство SelectedValue
. Я не думаю, что это лучшее свойство для привязки в вашем случае, обычно свойство SelectedItem
должно использоваться. Я помню, что мне никогда не приходилось ничего делать с телом SelectionBoxItem
, упомянутым в другом ответе, вероятно, потому, что свойства SelectedValue
и SelectedItem
ведут себя по-разному, и я стараюсь использовать SelectedItem
всякий раз, когда могу. В вашем случае я тоже буду использовать его.