Search code examples
c#xamlcustom-controlswinui-3

C# WinUI3 modifying AutosuggestBox


I'm trying to modify the standard AutoSuggestBox in a way, that the Listview popup is larger than the Textbox itself:

enter image description here

Unfortunately I'm stuck. I tried to create my own styledefinition:

(Standard version got from Blend)

<Style x:Key="AutoSuggestBoxStyle1" TargetType="AutoSuggestBox">
        <Setter Property="VerticalAlignment" Value="Top"/>
        <Setter Property="IsTabStop" Value="False"/>
        <Setter Property="TextBoxStyle" Value="{StaticResource AutoSuggestBoxTextBoxStyle}"/>
        <Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="AutoSuggestBox">
                    <Grid x:Name="LayoutRoot">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="Orientation">
                                <VisualState x:Name="Landscape"/>
                                <VisualState x:Name="Portrait"/>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <TextBox x:Name="TextBox"
                                 ScrollViewer.BringIntoViewOnFocusChange="False"
                                 DesiredCandidateWindowAlignment="BottomEdge"
                                 Description="{TemplateBinding Description}"
                                 Header="{TemplateBinding Header}"
                                 Margin="0"
                                 PlaceholderText="{TemplateBinding PlaceholderText}"
                                 Style="{TemplateBinding TextBoxStyle}"
                                 UseSystemFocusVisuals="{TemplateBinding UseSystemFocusVisuals}"
                                 Width="{TemplateBinding Width}"
                                 Canvas.ZIndex="0"/>
                        <Popup x:Name="SuggestionsPopup">
                            <Border x:Name="SuggestionsContainer">
                                <ListView x:Name="SuggestionsList"
                                          Background="{ThemeResource AutoSuggestBoxSuggestionsListBackground}"
                                          BorderBrush="{ThemeResource AutoSuggestBoxSuggestionsListBorderBrush}"
                                          BorderThickness="{ThemeResource AutoSuggestListBorderThemeThickness}"
                                          DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
                                          ItemTemplate="{TemplateBinding ItemTemplate}"
                                          ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
                                          ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                                          IsItemClickEnabled="True"
                                          Margin="{ThemeResource AutoSuggestListMargin}"
                                          MaxHeight="{ThemeResource AutoSuggestListMaxHeight}"
                                          Padding="{ThemeResource AutoSuggestListPadding}"/>
                            </Border>
                        </Popup>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

My version:

<Style x:Key="AutoSuggestBoxStyle1" TargetType="AutoSuggestBox">
        <Setter Property="VerticalAlignment" Value="Top"/>
        <Setter Property="IsTabStop" Value="False"/>
        <Setter Property="TextBoxStyle" Value="{StaticResource AutoSuggestBoxTextBoxStyle}"/>
        <Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="AutoSuggestBox">
                    <StackPanel x:Name="LayoutRoot">
                        
                        <TextBox x:Name="TextBox"
                                 ScrollViewer.BringIntoViewOnFocusChange="False"
                                 DesiredCandidateWindowAlignment="BottomEdge"
                                 Description="{TemplateBinding Description}"
                                 Header="{TemplateBinding Header}"
                                 Margin="0"
                                 PlaceholderText="{TemplateBinding PlaceholderText}"
                                 Style="{TemplateBinding TextBoxStyle}"
                                 UseSystemFocusVisuals="{TemplateBinding UseSystemFocusVisuals}"
                                 Width="100"
                                 Canvas.ZIndex="0"/>
                        <Popup x:Name="SuggestionsPopup">
                            <Border x:Name="SuggestionsContainer" Width="250">
                                <ListView x:Name="SuggestionsList"
                                          Background="{ThemeResource AutoSuggestBoxSuggestionsListBackground}"
                                          BorderBrush="{ThemeResource AutoSuggestBoxSuggestionsListBorderBrush}"
                                          BorderThickness="{ThemeResource AutoSuggestListBorderThemeThickness}"
                                          DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
                                          ItemTemplate="{TemplateBinding ItemTemplate}"
                                          ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
                                          ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                                          IsItemClickEnabled="True"
                                          Margin="{ThemeResource AutoSuggestListMargin}"
                                          MaxHeight="{ThemeResource AutoSuggestListMaxHeight}"
                                          Padding="{ThemeResource AutoSuggestListPadding}"/>
                            </Border>
                        </Popup>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

Unfortunately the Popupwidth is always as wide as the TextBox

Then I tried to create my own custom control and tried to rebuild the functionality of the AutoSuggestBox.

Now I don't know how to get XAML elements from the styled TextBox which is used in the AutoSuggestBox

<Setter Property="TextBoxStyle" Value="{StaticResource AutoSuggestBoxTextBoxStyle}"/>

I want to access the parts of the styled TextBox to add some functionalities and to add the QueryIcon to the related Button

<Style x:Key="AutoSuggestBoxTextBoxStyle" TargetType="TextBox">
        <Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}"/>
        <Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}"/>
        <Setter Property="Foreground" Value="{ThemeResource TextControlForeground}"/>
        <Setter Property="Background" Value="{ThemeResource TextControlBackground}"/>
        <Setter Property="BorderBrush" Value="{ThemeResource TextControlBorderBrush}"/>
        <Setter Property="SelectionHighlightColor" Value="{ThemeResource TextControlSelectionHighlightColor}"/>
        <Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}"/>
        <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
        <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto"/>
        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/>
        <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False"/>
        <Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TextBox">
                    <Grid>
                        ...
                        <Border x:Name="BorderElement" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.ColumnSpan="3" Grid.RowSpan="1" Grid.Row="1"/>
                        <ContentPresenter x:Name="HeaderContentPresenter" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.ColumnSpan="3" Foreground="{ThemeResource TextControlHeaderForeground}" FontWeight="Normal" Margin="{ThemeResource AutoSuggestBoxTopHeaderMargin}" Grid.Row="0" TextWrapping="Wrap" Visibility="Collapsed" x:DeferLoadStrategy="Lazy"/>
                        <ScrollViewer x:Name="ContentElement" AutomationProperties.AccessibilityView="Raw" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" IsTabStop="False" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled"/>
                        <ContentControl x:Name="PlaceholderTextContentPresenter" Content="{TemplateBinding PlaceholderText}" Grid.ColumnSpan="3" Foreground="{ThemeResource TextControlPlaceholderForeground}" IsTabStop="False" IsHitTestVisible="False" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1"/>
                        <Button x:Name="DeleteButton" AutomationProperties.AccessibilityView="Raw" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="1" FontSize="{TemplateBinding FontSize}" IsTabStop="False" MinWidth="34" Grid.Row="1" Style="{StaticResource DeleteButtonStyle}" VerticalAlignment="Stretch" Visibility="Collapsed"/>
                        <Button x:Name="QueryButton" AutomationProperties.AccessibilityView="Raw" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="2" FontSize="{TemplateBinding FontSize}" IsTabStop="False" MinWidth="34" Grid.Row="1" Style="{StaticResource QueryButtonStyle}" VerticalAlignment="Stretch" Width="{TemplateBinding Height}"/>
                        <ContentPresenter x:Name="DescriptionPresenter" AutomationProperties.AccessibilityView="Raw" Content="{TemplateBinding Description}" Grid.ColumnSpan="3" Foreground="{ThemeResource SystemControlDescriptionTextForegroundBrush}" Grid.Row="2" x:Load="False"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

Then in the function override void OnApplyTemplate() I can get the elements from my own style template, but I can't figure out how to get the elements coming from the textbox-style?

public sealed class CustomControl1 : Control
{
    public CustomControl1()
    {
        this.DefaultStyleKey = typeof(CustomControl1);
    }

    private Popup popup;
    private TextBox textbox;

    protected override void OnApplyTemplate()
    {
        textbox = GetTemplateChild("QueryTextBox") as TextBox;
        popup = GetTemplateChild("SuggestionsPopup") as Popup;

        base.OnApplyTemplate();
    }
}

Any help would be much appreciated. Or maybe do you know if the source code from the AutoSuggestBox is available on GitHub?


Solution

  • You need something like this:

    if (GetTemplateChild(nameof(TextBox)) is TextBox textBox)
    {
        this.TextBox = textBox;
        this.TextBox.Loaded += (s, e) =>
        {
            QueryButton = this.TextBox.FindDescendant(name: "QueryButton") as Button;
        };
    }
    

    BTW, the FindDescendant() extension method is from the CommunityToolkit.WinUI.UI NuGet package.