2010-02-06 10 views
7

Chcę wyświetlić niestandardowy szablon/element jako wybrany element w ComboBox (ten element faktycznie nie istnieje na liście pozycji i jest inaczej aktualizowany). Nie musi to być nawet element, wystarczy, że wyświetli się niestandardowy widok.WPF: Jak dostosować SelectionBoxItem w ComboBox

Jak mogę to zrobić, pozostając w aktualnym motywie ComboBox (aby nie było możliwe zastąpienie ControlTemplate)? O ile widzę, wszystkie właściwości SelectionBox * nie są edytowalne i wewnętrznie ComboBox używa nienazwanych ContentPresenter.

+0

Jest to laktacja, która może zmylić użytkowników, a ludzie oczekują, że pole combogramu będzie się zachowywać jak pudełko kombinowane. –

+0

Cóż, to jest ComboBox z CheckBoxami, które mogą być rzadkie, ale nie powiedziałbym, że to zbyt zagmatwane (lub Zło). Korzystanie z dodatkowego okna wyskakującego przypomina przesadę, a ten element nie jest tak ważny, aby być pełnowymiarową listą CheckBox. –

+0

Oczywiście nie mogę pokazać wybranego elementu, ponieważ nie mam jednego wybranego elementu. –

Odpowiedz

18

zrobiłbym to tak:

<Window.Resources> 

    <DataTemplate x:Key="NormalItemTemplate" ...> 
    ... 
    </DataTemplate> 

    <DataTemplate x:Key="SelectionBoxTemplate" ...> 
    ... 
    </DataTemplate> 

    <DataTemplate x:Key="CombinedTemplate"> 
    <ContentPresenter x:Name="Presenter" 
     Content="{Binding}" 
     ContentTemplate="{StaticResource NormalItemTemplate}" /> 
    <DataTemplate.Triggers> 
     <DataTrigger 
     Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}" 
     Value="{x:Null}"> 
     <Setter TargetName="Presenter" Property="ContentTemplate" 
       Value="{StaticResource SelectionBoxTemplate}" /> 
     </DataTrigger> 
    </DataTemplate.Triggers> 
    </DataTemplate> 

</Window.Resources> 

... 

<ComboBox 
    ItemTemplate="{StaticResource CombinedTemplate}" 
    ItemsSource="..." 
    ... /> 

Powodem tego działa jest to, że CombinedTemplate zwykle używa tylko NormalItemTemplate zaprezentować swoje dane, ale jeśli nie ma ComboBoxItem przodek zakłada to w polu wyboru tak używa SelectionBoxTemplate.

Należy zauważyć, że trzy DataTemplates mogły być uwzględnione w każdym poziomie ResourceDictionary (nie tylko na poziomie Window) lub nawet bezpośrednio w ComboBox, w zależności od preferencji.

+0

Dzięki, na pewno spróbuję tego. –

+0

Jest to zarówno eleganckie * i * działające rozwiązanie, wielkie dzięki! –

+2

Jednak generuje to wyjątek wiązania: 'Nie można odnaleźć źródła dla wiązania z odniesieniem 'RelativeSource FindAncestor, AncestorType =' System.Windows.Controls.ComboBoxItem ', AncestorLevel =' 1'''. Myślę, że ustawienie 'ItemTemplateSelector' jest lepszym podejściem. Oto przykład: http://social.msdn.microsoft.com/Forums/vstudio/en-US/0467c9ca-efb2-4506-96e7-08ce3356860a/combobox-one-template-for-selected-item-one-for -na liście rozwijanej? forum = wpf –

-1

Musisz zajrzeć do Triggers i Styles. Warto również spojrzeć na niektóre z moich starszych pytania tutaj na StackOverflow, który pomógł mi zdobyć te problemy:

+0

Dzięki, niestety, było to zbyt abstrakcyjne w porównaniu do innych odpowiedzi. Mam ogólne pojęcie o działaniu wyzwalaczy, to było rozwiązanie {x: Null}, którego nie mogłem wymyślić. –

0

Jeśli mam to prosto, chcesz kontrolę że ma coś dowolnie wyświetlanego wraz z rozwijanym przyciskiem, który wyświetla listę elementów z polami wyboru obok nich?

Nie chciałbym nawet próbować zmienić stylu, aby osiągnąć ten cel w postaci ComboBox. Problem polega na tym, że ComboBox jest bardziej wyspecjalizowany na innej ścieżce niż to, czego potrzebujesz. Jeśli spojrzysz na ComboBox ControlTemplate Example, zobaczysz, że po prostu używa kontrolki Popup, aby wyświetlić listę możliwych wartości.

Możesz pobrać fragmenty tego szablonu jako wskazówki do stworzenia obiektu UserControl, który jest łatwiejszy do zrozumienia i zapewnia lepsze rozwiązania. Będziesz nawet mógł dodać właściwość SelectedItems i taka, której nie zapewnia ComboBox.

Przykład tego, co mam na myśli przez wskazówki: Popup ma właściwość IsOpen. W szablonie sterowania jest ustawiona na {TemplateBinding IsDropDownOpen}, co oznacza, że ​​klasa ComboBox ma zmienną IsDropDownOpen w celu kontrolowania rozwinięcia/zwinięcia Popup.

+0

Problem z niestandardowymi elementami sterującymi polega na tym, że nie są one stylowane za pomocą wbudowanych stylów. Skonstruowałem już niestandardową formantu z SelectedItems, ale wewnątrz opiera się na ComboBox, ponieważ chcę, aby domyślne style działały z nim bez powtarzania. –

0

komentarz Aleksieja Mitev w sprawie Ray Burns' answer zainspirowało mnie do napisania poniższego rozsądnie krótki klasę użytkową, która teraz używać we wszystkich moich projektach WPF:

public class ComboBoxItemTemplateSelector : DataTemplateSelector 
{ 
    public List<DataTemplate> SelectedItemTemplates { get; } = new List<DataTemplate>(); 
    public List<DataTemplate> DropDownItemTemplates { get; } = new List<DataTemplate>(); 

    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     return GetVisualParent<ComboBoxItem>(container) == null 
      ? ChooseFrom(SelectedItemTemplates, item) 
      : ChooseFrom(DropDownItemTemplates, item); 
    } 

    private static DataTemplate ChooseFrom(IEnumerable<DataTemplate> templates, object item) 
    { 
     if (item == null) 
      return null; 
     var targetType = item.GetType(); 
     return templates.FirstOrDefault(t => (t.DataType as Type) == targetType); 
    } 

    private static T GetVisualParent<T>(DependencyObject child) where T : Visual 
    { 
     while (child != null && !(child is T)) 
      child = VisualTreeHelper.GetParent(child); 
     return child as T; 
    } 
} 

Mając to na pasku narzędzi, to możliwe, aby napisać XAML w ten sposób:

<UserControl.Resources> 
    <DataTemplate x:Key="SelectedItemTemplateForInt" DataType="{x:Type system:Int32}"> 
     <!-- ... --> 
    </DataTemplate> 

    <DataTemplate x:Key="SelectedItemTemplateForDouble" DataType="{x:Type system:Double}"> 
     <!-- ... --> 
    </DataTemplate> 

    <DataTemplate x:Key="DropDownItemTemplateForInt" DataType="{x:Type system:Int32}"> 
     <!-- ... --> 
    </DataTemplate> 

    <DataTemplate x:Key="DropDownItemTemplateForDouble" DataType="{x:Type system:Double}"> 
     <!-- ... --> 
    </DataTemplate> 
</UserControl.Resources> 

<ComboBox> 
    <ComboBox.ItemTemplateSelector> 
     <local:ComboBoxItemTemplateSelector> 
      <local:ComboBoxItemTemplateSelector.SelectedItemTemplates> 
       <StaticResource ResourceKey="SelectedItemTemplateForInt" /> 
       <StaticResource ResourceKey="SelectedItemTemplateForDouble" /> 
      </local:ComboBoxItemTemplateSelector.SelectedItemTemplates> 

      <local:ComboBoxItemTemplateSelector.DropDownItemTemplates> 
       <StaticResource ResourceKey="DropDownItemTemplateForInt" /> 
       <StaticResource ResourceKey="DropDownItemTemplateForDouble" /> 
      </local:ComboBoxItemTemplateSelector.DropDownItemTemplates> 
     </local:ComboBoxItemTemplateSelector> 
    </ComboBox.ItemTemplateSelector> 
</ComboBox>