Проблема с использованием решения DataTrigger / Binding, упомянутого выше, имеет две стороны.Во-первых, вы фактически получаете связующее предупреждение о том, что не можете найти относительный источник для выбранного элемента.Однако большая проблема заключается в том, что вы загромождали свои шаблоны данных и делали их специфичными для ComboBox.
Решение, которое я представляю, лучше соответствует проектам WPF в том смысле, что в нем используется DataTemplateSelector
, для которого можно указать отдельныешаблоны, использующие его свойства SelectedItemTemplate
и DropDownItemsTemplate
, а также варианты «селектора» для обоих.
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 != null && !(itemToCheck is ComboBoxItem) && !(itemToCheck is ComboBox))
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);
}
}
Примечание. Для простоты в моем примере кода здесь используется новый символ «?».особенность C # 6 (VS 2015).Если вы используете старую версию, просто удалите «?»и явно проверять нулевое значение перед вызовом 'SelectTemplate' выше и возвращать нулевое значение в противном случае, например так:
return inDropDown
? DropdownItemsTemplate ??
((DropdownItemsTemplateSelector != null)
? DropdownItemsTemplateSelector.SelectTemplate(item, container)
: null)
: SelectedItemTemplate ??
((SelectedItemTemplateSelector != null)
? SelectedItemTemplateSelector.SelectTemplate(item, container)
: null)
Я также включил расширение разметки, которое просто создает и возвращает вышеуказанный класс для удобства в XAML.
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)
{
return new ComboBoxTemplateSelector(){
SelectedItemTemplate = SelectedItemTemplate,
SelectedItemTemplateSelector = SelectedItemTemplateSelector,
DropdownItemsTemplate = DropdownItemsTemplate,
DropdownItemsTemplateSelector = DropdownItemsTemplateSelector
};
}
}
А вот как вы это используете.Хорошо, чисто и понятно, а ваши шаблоны остаются «чистыми»
Примечание: «is:» - вот мое отображение xmlns для того, где я поместил класс в код.Убедитесь, что вы импортировали свое собственное пространство имен и изменили «:» соответствующим образом.
<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} />
Наслаждайтесь!