WPF Combobox: Different template in textbox and drop-downlist

前端 未结 5 1228
面向向阳花
面向向阳花 2020-11-29 05:25

This is my combo-box.

    

        
相关标签:
5条回答
  • 2020-11-29 05:58

    There is a pretty good answer to your question here if you don't want to change the ComboBoxes style: https://stackoverflow.com/a/2277488/1070906

    It uses a Trigger in the DataTemplate which looks if there is a ComboBoxItem somewhere above in the visual tree, which is not the case in the selection box.

    0 讨论(0)
  • 2020-11-29 06:04

    You could override the ComboBox and change the SelectionBoxItemTemplate directly.

    public class SelectionComboBox : ComboBox
    {
        #region Properties
    
        #region Dependency Properties
        public DataTemplate AltSelectionBoxItemTemplate
        {
            get { return (DataTemplate)GetValue(AltSelectionBoxItemTemplateProperty); }
            set { SetValue(AltSelectionBoxItemTemplateProperty, value); }
        }
    
        public static readonly DependencyProperty AltSelectionBoxItemTemplateProperty =
            DependencyProperty.Register("AltSelectionBoxItemTemplate", typeof(DataTemplate), typeof(SelectionComboBox), new UIPropertyMetadata(null, (s, e) =>
            {
                // For new changes...
                if ((s is SelectionComboBox) && ((SelectionComboBox)s).Presenter != null && (e.NewValue is DataTemplate))
                    ((SelectionComboBox)s).Presenter.ContentTemplate = (DataTemplate)e.NewValue;
    
                // Set the new value
                ((SelectionComboBox)s).AltSelectionBoxItemTemplate = (DataTemplate)e.NewValue;
            }));
        #endregion
    
        #region Internals
    
        #region Elements
        ContentPresenter Presenter { get; set; }
        #endregion
    
        #endregion
    
        #endregion
    
        #region Constructors
        #endregion
    
        #region Methods
    
        #region Overrides
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
    
            Presenter = this.GetTemplateChild("contentPresenter") as ContentPresenter;
    
            // Directly Set the selected item template
            if (AltSelectionBoxItemTemplate != null) Presenter.ContentTemplate = AltSelectionBoxItemTemplate;
        }
        #endregion
    
        #endregion
    
    }
    

    Once you define the control, you could style it.

    <controls:SelectionComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ..., Mode=TwoWay}">
        <controls:SelectionComboBox.AltSelectionBoxItemTemplate>
            <DataTemplate>
                <!-- My Template Goes Here... -->
            </DataTemplate>
        </controls:SelectionComboBox.AltSelectionBoxItemTemplate>
    </controls:SelectionComboBox>
    
    0 讨论(0)
  • 2020-11-29 06:07

    Instead of using the read-only SelectionBoxItemTemplate property I created a new (attached, writable) property and used that one in my style. I also added a trigger to my style to not break all the comboboxes that are not using my new attached property...

    Use it like this:

    <ComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ..., Mode=TwoWay}">
        <controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate>
            <DataTemplate DataType="{x:Type ...}">
                ... Template for the selection box ...
            </DataTemplate>
        </controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate>
        <ComboBox.ItemTemplate>
            <DataTemplate DataType="{x:Type ...}">
                ... Template for the popup ...
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
    

    You just have to add this class to your project:

    public class ComboBoxSelectionBoxAltTemplateBehaviour
    {
        public static readonly DependencyProperty SelectionBoxAltTemplateProperty = DependencyProperty.RegisterAttached(
            "SelectionBoxAltTemplate", typeof (DataTemplate), typeof (ComboBoxSelectionBoxAltTemplateBehaviour), new PropertyMetadata(default(DataTemplate)));
    
        public static void SetSelectionBoxAltTemplate(DependencyObject element, DataTemplate value)
        {
            element.SetValue(SelectionBoxAltTemplateProperty, value);
        }
    
        public static DataTemplate GetSelectionBoxAltTemplate(DependencyObject element)
        {
            return (DataTemplate) element.GetValue(SelectionBoxAltTemplateProperty);
        }
    
    }
    

    and change your ComboBox style to use the SelectionBoxAltTemplate attached property if set (or because I could not set a trigger to "not null", I set it back to the default SelectionBoxItemTemplate if the attached one is null):

    The ContentPresenter inside the ControlTemplate of the ComboBox Style:

    <ContentPresenter Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate}" />
    

    And the Trigger to provide backwards compatibility to ComboBoxed without the attached Property:

    <ControlTemplate.Triggers>
        <Trigger Property="controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate" Value="{x:Null}">
            <Setter Property="ContentTemplate" Value="{Binding SelectionBoxItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" TargetName="ContentSite" />
        </Trigger>
        ...
    </ControlTemplate.Triggers>
    

    Full Style:

    <Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
        <Setter Property="SnapsToDevicePixels" Value="true" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="FontSize" Value="12" />
        <Setter Property="Background" Value="{StaticResource ComboBoxBackground}"/>
        <Setter Property="BorderBrush" Value="{StaticResource ComboBoxBorder}"/>
        <Setter Property="Margin" Value="6"/>
        <Setter Property="Padding" Value="3,3,5,3"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ComboBox}">
                    <Grid>
                        <Grid.ColumnDefinitions>                    
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Border Name="Border" Grid.ColumnSpan="2" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
                        <ToggleButton Name="ToggleButton2" Focusable="False" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Grid.ColumnSpan="2" Background="Transparent"/>
                        <!-- Allows clicking anywhere on the combobox, not only the visible button on the right -->
                        <ToggleButton Focusable="false" Grid.Column="1" x:Name="ToggleButton" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Style="{StaticResource ComboBoxToggleButton}"/>
                        <ContentPresenter HorizontalAlignment="Left" Margin="{TemplateBinding Control.Padding}" x:Name="ContentSite" VerticalAlignment="Center" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" IsHitTestVisible="False" />
    
                        <TextBox Visibility="Hidden"  HorizontalAlignment="Left" Margin="{TemplateBinding Control.Padding}" x:Name="PART_EditableTextBox" Style="{x:Null}" VerticalAlignment="Center" Focusable="True" Background="Transparent"  />
    
                        <Popup IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" x:Name="Popup" Focusable="False" AllowsTransparency="True" PopupAnimation="Slide">
                            <Grid MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}" x:Name="DropDown" SnapsToDevicePixels="True">
                                <Border x:Name="DropDownBorder" Background="{StaticResource ComboBoxBackground}" BorderBrush="{StaticResource ComboBoxBorder}" BorderThickness="1" Padding="0,4">
                                    <ScrollViewer SnapsToDevicePixels="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="True" Style="{x:Null}" >
                                        <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
                                    </ScrollViewer>
                                </Border>
                            </Grid>
                        </Popup>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate" Value="{x:Null}">
                            <Setter Property="ContentTemplate" Value="{Binding SelectionBoxItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" TargetName="ContentSite" />
                        </Trigger>
                        <Trigger Property="HasItems" Value="false">
                            <Setter Property="MinHeight" Value="95" TargetName="DropDownBorder" />
                        </Trigger>
                        <Trigger Property="IsGrouping" Value="true">
                            <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
                        </Trigger>
                        <Trigger Property="IsEditable" Value="true">
                            <Setter Property="IsTabStop" Value="false" />
                            <Setter Property="Visibility" Value="Visible" TargetName="PART_EditableTextBox" />
                            <Setter Property="Visibility" Value="Hidden" TargetName="ContentSite" />
                        </Trigger>
    
                        <Trigger Property="IsMouseOver" Value="true" SourceName="ToggleButton2">
                            <Setter Property="Background" Value="{StaticResource ComboBoxMouseOver}" />
                        </Trigger>
                        <Trigger Property="HasItems" Value="False">
                            <Setter Property="IsEnabled" Value="False"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    However this might not work with ItemTemplateSelctors, only with one single template - but you could easily add an attached property "SelectionBoxAltTemplateSelector" which provides the selector and passes that one to the style.

    0 讨论(0)
  • 2020-11-29 06:12

    If the ComboBox's IsEditable property is set to True, you can set the "TextSearch.TextPath" property of the ComboBox to the property name you want to show. So in your case:

    <ComboBox IsEditable="True" TextSearch.TextPath="FullName" .../>
    
    0 讨论(0)
  • 2020-11-29 06:13

    Unfortunately, the SelectionBoxItemTemplate is a readonly property, so we have to do a bit more work. By doing the ItemTemplate to be how you want the item to appear when selected, you can edit the ItemContainerStyle to provide a ControlTemplate that includes the other fields you want to display.

    <ComboBox Height="45" HorizontalAlignment="Left" Margin="184,66,0,0" Name="ComboBox1" VerticalAlignment="Top" Width="216">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding FullName}" Width="150" />
            </DataTemplate>
        </ComboBox.ItemTemplate>
        <ComboBox.ItemContainerStyle>
            <Style TargetType="{x:Type ComboBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ComboBoxItem}">
                            <Border x:Name="Bd"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    Background="{TemplateBinding Background}">
                                <StackPanel Orientation="Horizontal">
                                    <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                                    <Label Content="{Binding Title}" Width="100"/>
                                    <Label Content="{Binding BranchName}" />
                                </StackPanel>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsHighlighted" Value="True">
                                    <Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
                                </Trigger>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ComboBox.ItemContainerStyle>
    </ComboBox>
    

    For the ComboBoxItem template, I just modified the default one, so it should be fully functional.

    0 讨论(0)
提交回复
热议问题