我正在尝试显示一个ComboBox,其ItemsSource是一组控件(它是PropertyGrid的一部分,ComboBox应该显示控件的名称,用户应该能够选择其中一个控件)。这是一个非常简化的问题再现:
<ComboBox ItemsSource="{Binding GroupBoxes}" SelectedValue="{Binding SelectedGroupBox}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
GroupBoxes和SelectedGroupBox是ObservableCollection和GroupBox类型的DependencyProperties。
Bindings工作 - 控件名称显示在ComboBox-DropDown中,如果我选择一个不同的项目,我可以看到SelectedGroupBox属性已正确更新。问题:所选项目永远不会显示在ComboBox中。从代码设置SelectedGroupBox属性也可以按预期工作 - ComboBox引发SelectionChanged并且其SelectedValue是正确的,但它仍然不显示当前值。
如果我对任何其他类型的类做同样的事情,一切都按预期工作。
寻找答案我遇到了许多有类似声音问题的人的帖子,但几乎所有这些都是绑定问题,而这里并非如此。
修改
为了简化尝试,这里是代码背后的代码。只需将上面的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
答案 0 :(得分:19)
由于一些非常复杂的原因,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。这将解决问题。
答案 1 :(得分:2)
使用DisplayMemberPath
而不是绑定到名称:
<Combobox DisplayMemberPath="Name" ... />
您看到的行为的原因可能是您需要为所选项目的模板设置另一个属性:http://msdn.microsoft.com/en-us/library/system.windows.controls.combobox.selectionboxitemtemplate.aspx
更新:我在没有检查您的代码的情况下写了我的答案,对不起。现在我已经阅读了您的代码,并注意到您正在绑定SelectedValue
属性。我不认为这是在你的情况下绑定的最佳属性,通常应该使用属性SelectedItem
。我记得我从来没有对其他答案中提到的SelectionBoxItem
内容做任何事情,这可能是因为SelectedValue
和SelectedItem
属性表现不同而且每当我倾向于使用SelectedItem
能够。在你的情况下,我也会使用它。
答案 2 :(得分:0)
此 link 提供了解决方案和演示。诀窍是在需要控制 SelectedItem 外观的情况下使用 DataTemplateSelector 而不是 DataTemplate,例如:
<ComboBox ItemsSource="{Binding GroupBoxes}" SelectedValue="{Binding SelectedGroupBox}">
<ComboBox.ItemTemplateSelector>
<ts:ComboBoxItemTemplateSelector>
<ts:ComboBoxItemTemplateSelector.SelectedTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ts:ComboBoxItemTemplateSelector.SelectedTemplate>
</ts:ComboBoxItemTemplateSelector>
</ComboBox.ItemTemplateSelector>
</ComboBox>
public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
public DataTemplate SelectedTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return SelectedTemplate;
}
}