Search code examples
wpfcomboboxmaterial-designhint

WPF MaterialDesignFilledComboBox - Center HintText vertically


I have this ComboBox using the MaterialDesignFilledComboBox style.

<ComboBox ItemsSource="{Binding SomeData}"
          Style="{StaticResource MaterialDesignFilledComboBox}"
          materialDesign:HintAssist.Hint="Some hint text"
</ComboBox>

Notice the assignment of the materialDesign:HintAssist.Hint="Some hint text" property.

When I have not chosen an item in the ComboBox's drop-down-menu, the Hint property ("Some hint text") is the only visible text in the ComboBox. That is fine.
Above the Hint, there is an "unused space".
If I select an item in the drop-down-menu, the "unused space" is occupied by the Hint and showing the "Some hint text".

Question
My designer dislike the "above space" when there is no item selected in the drop-down-menu.
Is it possible to center the Hint vertically in the ComboBox when there is no item selected?

My findings
Looking into the MaterialDesignFilledComboBox's template (MaterialDesignFloatingHintComboBoxTemplate).
I found the SmartHint control at rows 374 - 397. This control seem to be the placeholder for the Hint.
If I add this style as a resource to the ComboBox, I have some limitied control of the SmartHint object in my ComboBox.
For instance:

<ComboBox ItemsSource="{Binding SomeData}"
          Style="{StaticResource MaterialDesignFilledComboBox}"
          materialDesign:HintAssist.Hint="Some hint text"
    <ComboBox.Resources>
        <Style TargetType="materialDesign:SmartHint">
            <Setter Property="VirtualizingPanel.Visibility" Value="Collapsed" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="VerticalContentAlignment" Value="Center" />
        </Style>
    <ComboBox.Resources>
</ComboBox>

The setter with the Visibility = Collapsed actually works.
The setters of the VerticalAlignment and the VerticalContentAlignment properties don't work. Even if they would work, they would not given me the exact result I want, but prove that I can control the position of the control in some way.

Any suggestions?

/BR
Steffe


Solution

  • Try to set the HintAssist.IsFloating attached property of the ComboBox to false:

    <ComboBox ItemsSource="{Binding SomeData}"
              Style="{StaticResource MaterialDesignFilledComboBox}"
              materialDesign:HintAssist.IsFloating="False"
              materialDesign:HintAssist.Hint="Some hint text" />
    

    You could set it conditionally depending on whether an item is selected using a Style:

    <ComboBox ItemsSource="{Binding SomeData}" materialDesign:HintAssist.Hint="Some hint text">
        <ComboBox.Style>
            <Style TargetType="ComboBox" BasedOn="{StaticResource MaterialDesignFilledComboBox}">
                <Setter Property="materialDesign:HintAssist.IsFloating" Value="True" />
                <Style.Triggers>
                    <Trigger Property="SelectedItem" Value="{x:Null}">
                        <Setter Property="materialDesign:HintAssist.IsFloating" Value="False" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ComboBox.Style>
    </ComboBox>
    

    Thanks to @mm8 I found a way to partially solve this problem...But there is a problem with the graphics of the ComboBox When I select the first item in the drop-down-menu, both the hint text and the text of the selected item, get all merged/mangled together into an unreadable mess.

    That's a flaw of the third-party control.

    You could work around it by handling the SelectionChanged and programmatically call the ApplyTemplate method of the SmartHint:

    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ComboBox cmb = (ComboBox)sender;
        HintAssist.SetIsFloating(cmb, cmb.SelectedItem != null);
        SmartHint smartHint = FindVisualChild<SmartHint>(cmb);
        if (smartHint != null)
        {
            smartHint.ApplyTemplate();
        }
    }
    
    private static T FindVisualChild<T>(Visual visual) where T : Visual
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
        {
            Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
            if (child != null)
            {
                T correctlyTyped = child as T;
                if (correctlyTyped != null)
                {
                    return correctlyTyped;
                }
    
                T descendent = FindVisualChild<T>(child);
                if (descendent != null)
                {
                    return descendent;
                }
            }
        }
    
        return null;
    }
    

    XAML:

    <ComboBox ItemsSource="{Binding SomeData}"
              SelectionChanged="OnSelectionChanged"
              Style="{StaticResource MaterialDesignFilledComboBox}"
              materialDesign:HintAssist.IsFloating="False"
              materialDesign:HintAssist.Hint="Some hint text" />