Проблема с использованием решения DataTrigger / Binding, упомянутого выше, имеет два аспекта. Во-первых, вы фактически получаете обязательное предупреждение о том, что вы не можете найти относительный источник для выбранного элемента. Однако более серьезная проблема заключается в том, что вы загромождали свои шаблоны данных и сделали их специфичными для ComboBox.
Предлагаемое мной решение лучше соответствует дизайну WPF, поскольку в нем используется DataTemplateSelector
, на котором вы можете указать отдельные шаблоны, используя его свойства SelectedItemTemplate
и DropDownItemsTemplate
, а также варианты «селектора» для обоих.
Примечание. Обновлено для C # 9 с включенной возможностью нулевого значения и использованием сопоставления с образцом во время поиска
public class ComboBoxTemplateSelector : DataTemplateSelector {
public DataTemplate? SelectedItemTemplate { get; set; }
public DataTemplateSelector? SelectedItemTemplateSelector { get; set; }
public DataTemplate? DropdownItemsTemplate { get; set; }
public DataTemplateSelector? DropdownItemsTemplateSelector { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
var itemToCheck = container;
// Search up the visual tree, stopping at either a ComboBox or
// a ComboBoxItem (or null). This will determine which template to use
while(itemToCheck is not null
and not ComboBox
and not ComboBoxItem)
itemToCheck = VisualTreeHelper.GetParent(itemToCheck);
// If you stopped at a ComboBoxItem, you're in the dropdown
var inDropDown = itemToCheck is ComboBoxItem;
return inDropDown
? DropdownItemsTemplate ?? DropdownItemsTemplateSelector?.SelectTemplate(item, container)
: SelectedItemTemplate ?? SelectedItemTemplateSelector?.SelectTemplate(item, container);
}
}
Чтобы упростить использование в XAML, я также включил расширение разметки, которое просто создает и возвращает указанный выше класс в своей функции ProvideValue
.
public class ComboBoxTemplateSelectorExtension : MarkupExtension {
public DataTemplate? SelectedItemTemplate { get; set; }
public DataTemplateSelector? SelectedItemTemplateSelector { get; set; }
public DataTemplate? DropdownItemsTemplate { get; set; }
public DataTemplateSelector? DropdownItemsTemplateSelector { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
=> new ComboBoxTemplateSelector(){
SelectedItemTemplate = SelectedItemTemplate,
SelectedItemTemplateSelector = SelectedItemTemplateSelector,
DropdownItemsTemplate = DropdownItemsTemplate,
DropdownItemsTemplateSelector = DropdownItemsTemplateSelector
};
}
И вот как вы это используете. Красиво, чисто и понятно, а ваши шаблоны остаются "чистыми"
Примечание. «Is:» - это мое сопоставление xmlns, в которое я помещаю класс в коде. Обязательно импортируйте собственное пространство имен и при необходимости измените is :.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MySelectedItemTemplate},
DropdownItemsTemplate={StaticResource MyDropDownItemTemplate}}" />
Вы также можете использовать DataTemplateSelectors, если хотите ...
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplateSelector={StaticResource MySelectedItemTemplateSelector},
DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
Или смешайте и сопоставьте! Здесь я использую шаблон для выбранного элемента, но селектор шаблонов для элементов DropDown.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MySelectedItemTemplate},
DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
Кроме того, если вы не укажете Template или TemplateSelector для выбранных или раскрывающихся элементов, он просто вернется к обычному разрешению шаблонов данных на основе типов данных, опять же, как и следовало ожидать. Так, например, в приведенном ниже случае для выбранного элемента явно установлен шаблон, но раскрывающийся список унаследует тот шаблон данных, который применяется для DataType объекта в контексте данных.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MyTemplate} />
Наслаждаться!
person
Mark A. Donohoe
schedule
29.10.2015
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ComboBoxItem', AncestorLevel='1''. BindingExpression:Path=IsSelected; DataItem=null; target element is 'ContentPresenter' (Name=''); target property is 'NoTarget' (type 'Object')
- person user11909   schedule 06.04.2017