Vấn đề với việc sử dụng giải pháp DataTrigger/Binding được đề cập ở trên là hai lần. Đầu tiên là bạn thực sự kết thúc với một cảnh báo ràng buộc rằng bạn không thể tìm thấy nguồn tương đối cho mục đã chọn. Vấn đề lớn hơn tuy nhiên là bạn đã lộn xộn lên các mẫu dữ liệu của bạn và làm cho chúng cụ thể cho một ComboBox.
Giải pháp tôi trình bày tốt hơn theo thiết kế WPF ở chỗ nó sử dụng một DataTemplateSelector mà bạn có thể chỉ định các mẫu riêng biệt bằng cách sử dụng thuộc tính SelectedItemTemplate và DropDownItemsTemplate cũng như Selectors cho cả hai.
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 parent = container;
// Search up the visual tree, stopping at either a ComboBox or
// a ComboBoxItem (or null). This will determine which template to use
while(parent != null && !(parent is ComboBoxItem) && !(parent is ComboBox))
parent = VisualTreeHelper.GetParent(parent);
// If you stopped at a ComboBoxItem, you're in the dropdown
var inDropDown = (parent is ComboBoxItem);
return inDropDown
? DropdownItemsTemplate ?? DropdownItemsTemplateSelector?.SelectTemplate(item, container)
: SelectedItemTemplate ?? SelectedItemTemplateSelector?.SelectTemplate(item, container);
}
}
Note: For simplicity, my example code here uses the new '?.' feature of C#6 (VS 2015). If you're using an older version, simply remove the '?' and explicitly check for null before calling 'SelectTemplate' above and return null otherwise like so:
return inDropDown
? DropdownItemsTemplate ??
((DropdownItemsTemplateSelector != null)
? DropdownItemsTemplateSelector.SelectTemplate(item, container)
: null)
: SelectedItemTemplate ??
((SelectedItemTemplateSelector != null)
? SelectedItemTemplateSelector.SelectTemplate(item, container)
: null)
Tôi cũng đã bao gồm một phần mở rộng đánh dấu mà chỉ đơn giản tạo ra và trả về lớp trên để thuận tiện trong 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
};
}
}
Và dưới đây là cách bạn sử dụng. Thoải mái, sạch sẽ và rõ ràng và các mẫu của bạn ở lại 'tinh khiết'
Note: 'is:' here is my xmlns mapping for where I put the class in code. Make sure to import your own namespace and change 'is:' as appropriate.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MySelectedItemTemplate},
DropdownItemsTemplate={StaticResource MyDropDownItemTemplate}}" />
Bạn cũng có thể sử dụng DataTemplateSelectors nếu bạn thích ...
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplateSelector={StaticResource MySelectedItemTemplateSelector},
DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
Hoặc trộn và kết hợp! Ở đây tôi đang sử dụng một mẫu cho mục đã chọn, nhưng một bộ chọn mẫu cho các mục DropDown.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MySelectedItemTemplate},
DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
Ngoài ra, nếu bạn không chỉ định một mẫu hoặc một TemplateSelector cho các mục đã chọn hoặc thả xuống, nó chỉ đơn giản là rơi trở lại thường xuyên giải quyết các mẫu dữ liệu dựa trên các kiểu dữ liệu, một lần nữa, như bạn mong đợi. Vì vậy, ví dụ, trong trường hợp dưới đây, mục đã chọn có khuôn mẫu được đặt rõ ràng, nhưng trình đơn thả xuống sẽ kế thừa bất kỳ mẫu dữ liệu nào áp dụng cho DataType của đối tượng trong ngữ cảnh dữ liệu.
<ComboBox x:Name="MyComboBox"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{is:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource MyTemplate} />
Tận hưởng!
Giải pháp của bạn hoạt động, nhưng tôi gặp lỗi trong cửa sổ Kết quả. 'System.Windows.Lỗi dữ liệu: 4: Không thể tìm thấy nguồn để liên kết với tham chiếu 'RelativeSource FindAncestor, AncestorType =' System.Windows.Controls.ComboBoxItem ', AncestorLevel =' 1 ''. BindingExpression: Đường dẫn = IsSelected; DataItem = null; phần tử đích là 'ContentPresenter' (Tên = ''); thuộc tính đích là 'NoTarget' (gõ 'Object') ' – user2190035
Tôi nhớ cũng thấy các lỗi này. Nhưng tôi không còn trong dự án (hoặc thậm chí trong công ty), vì vậy tôi không thể kiểm tra điều này, xin lỗi. – Peter