Es gibt zwei Probleme bei der Verwendung der oben genannten DataTrigger / Binding-Lösung. Das erste ist, dass Sie tatsächlich eine verbindliche Warnung erhalten, dass Sie die relative Quelle für das ausgewählte Element nicht finden können. Das größere Problem ist jedoch, dass Sie Ihre Datenvorlagen überfüllt und für eine ComboBox spezifisch gemacht haben.
Die Lösung, die ich vorstelle, folgt besser den WPF-Entwürfen, indem sie eine verwendet, DataTemplateSelector
für die Sie separate Vorlagen mit ihren Eigenschaften SelectedItemTemplate
und DropDownItemsTemplate
Eigenschaften sowie Auswahlvarianten für beide angeben können .
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;
while(itemToCheck != null && !(itemToCheck is ComboBoxItem) && !(itemToCheck is ComboBox))
itemToCheck = VisualTreeHelper.GetParent(itemToCheck);
var inDropDown = (itemToCheck is ComboBoxItem);
return inDropDown
? DropdownItemsTemplate ?? DropdownItemsTemplateSelector?.SelectTemplate(item, container)
: SelectedItemTemplate ?? SelectedItemTemplateSelector?.SelectTemplate(item, container);
}
}
Hinweis: Der Einfachheit halber verwendet mein Beispielcode hier das neue '?' Merkmal von C # 6 (VS 2015). Wenn Sie eine ältere Version verwenden, entfernen Sie einfach das '?' und prüfen Sie explizit auf null, bevor Sie oben 'SelectTemplate' aufrufen, und geben Sie andernfalls null zurück, wie folgt:
return inDropDown
? DropdownItemsTemplate ??
((DropdownItemsTemplateSelector != null)
? DropdownItemsTemplateSelector.SelectTemplate(item, container)
: null)
: SelectedItemTemplate ??
((SelectedItemTemplateSelector != null)
? SelectedItemTemplateSelector.SelectTemplate(item, container)
: null)
Ich habe auch eine Markup-Erweiterung eingefügt, die die obige Klasse einfach erstellt und zur Vereinfachung in XAML zurückgibt.
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
};
}
}
Und so verwenden Sie es. Schön, sauber und klar und Ihre Vorlagen bleiben "rein"
Hinweis: 'is:' Hier ist meine XML-Zuordnung, wo ich die Klasse in Code eingefügt habe. Stellen Sie sicher, dass Sie Ihren eigenen Namespace importieren und 'is:' entsprechend ändern.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MySelectedItemTemplate},
DropdownItemsTemplate={StaticResource MyDropDownItemTemplate}}" />
Sie können auch DataTemplateSelectors verwenden, wenn Sie ...
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplateSelector={StaticResource MySelectedItemTemplateSelector},
DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
Oder mischen und anpassen! Hier verwende ich eine Vorlage für das ausgewählte Element, aber eine Vorlagenauswahl für die DropDown-Elemente.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MySelectedItemTemplate},
DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
Wenn Sie für die ausgewählten Elemente oder Dropdown-Elemente keine Vorlage oder keinen TemplateSelector angeben, wird einfach wie erwartet wieder auf die reguläre Auflösung von Datenvorlagen basierend auf Datentypen zurückgegriffen. So wird beispielsweise im folgenden Fall die Vorlage des ausgewählten Elements explizit festgelegt, aber die Dropdown-Liste erbt die Datenvorlage, die für den Datentyp des Objekts im Datenkontext gilt.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MyTemplate} />
Genießen!
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')