UWP開發隨筆——UWP新控件!AutoSuggestBox!

摘要

要開發一款優秀的application,控件確定是必不可少的,uwp就爲開發者提供了各類各樣的系統控件,AutoSuggestBox就是uwp極具特點的控件之一,也是相對於以前win8.1的uap較新的控件,今天咱們就來談談AutoSuggestBox的基本用法及其自定義UI方法。express

A Simplest Sample

話很少說,先來個小Demo。app

<!-- MainPage.xaml -->
<Page
    x:Class="AutoSuggestBoxSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AutoSuggestBoxSample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel VerticalAlignment="Center" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <AutoSuggestBox x:Name="autoSuggestBox"
                        PlaceholderText="Type a control name"
                        TextChanged="AutoSuggestBox_TextChanged" 
                        QueryIcon="Find" 
                        QuerySubmitted="AutoSuggestBox_QuerySubmitted"
                        Width="300"
                        HorizontalAlignment="Center"/>
        <TextBlock x:Name="textBlock" HorizontalAlignment="Center" Margin="10"/>
    </StackPanel>
</Page>

 

//MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace AutoSuggestBoxSample
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {

        public MainPage()
        {
            suggestions = new ObservableCollection<string>();
            this.InitializeComponent();
        }

        private ObservableCollection<String> suggestions;

        private void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
        {
            if (args.ChosenSuggestion != null)
                textBlock.Text = args.ChosenSuggestion.ToString();
            else
                textBlock.Text = sender.Text;
        }

        private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
        {
            suggestions.Clear();
            suggestions.Add(sender.Text + "1");
            suggestions.Add(sender.Text + "2");
            sender.ItemsSource = suggestions;
        }
    }
}


運行結果如圖所示:this

Understand the code above

上面的代碼定義了一個最簡單的AutoSuggestBox,先看xaml部分,在AutoSuggestBox標籤部分,定義了PlaceHolderText,就是在沒有輸入的時候顯示的Hint。QueryIcon是控件最右邊的搜索按鈕,等號右邊的Find是系統自帶的icon,固然也能夠選擇其它icon,後面咱們會說,這個屬性能夠不設置,這樣的話,AutoSuggestBox則不會顯示最右邊的搜索按鈕。而TextChanged和QuerySubmitted對應的是兩個事件,事件的定義在MainPage.xaml.cs裏面。這裏注意一下,除了這兩個事件外,還有一個會常常用到而且容易理解錯誤的事件SuggestionChosen,每次在List選擇以後,會自動觸發這個事件,除此以外,QuerySubmitted也會觸發一次,因此在使用過程當中,搞清楚二者之間的關係是必要的。spa

What if the data set is something else?

細心的讀者可能會發現,在上面的例子中,咱們僅僅是顯示了一個字符串列表,但是在實際開發中,誰又能保證suggestions只能是字符串呢?這固然是不可能的。幸運的是,AutoSuggestBox的Suggestion List其實就是一個ListView,咱們能夠經過定義ItemTemplate來使ListViewItem展現不一樣的數據,對AutoSuggestBox來講,固然也是可行的。舉例來講,當數據集爲SampleSuggest對象:pwa

    public class SampleSuggest
    {
        public int Index { set; get; }
        public String Value { set; get; }
    }

 

若要正常顯示ListViewItem,則須要在XAML裏面定義AutoSuggestBox的ItemTemplate屬性,以下所示:rest

        <AutoSuggestBox x:Name="autoSuggestBox"
                        PlaceholderText="Type a control name"
                        TextChanged="AutoSuggestBox_TextChanged" 
                        QueryIcon="Find" 
                        QuerySubmitted="AutoSuggestBox_QuerySubmitted"
                        Width="300"
                        HorizontalAlignment="Center">
            <AutoSuggestBox.ItemTemplate>
                <DataTemplate x:DataType="local:SampleSuggest">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{x:Bind Path=Index}"/>
                        <TextBlock Margin="10,0,0,0" Text="{x:Bind Path=Value}"/>
                    </StackPanel>
                </DataTemplate>
            </AutoSuggestBox.ItemTemplate>
        </AutoSuggestBox>

這樣ListViewItem就能按照咱們的定義顯示了:code

可是,當咱們點擊列表時,TextBox裏面顯示的倒是AutoSuggestBoxSample.SampleSuggest,這顯然不是咱們想要的。由於在選擇ListViewItem的時候,系統會自動調用選中數據的ToString()方法,將獲得的字符串顯示在TextBox中。爲了放着這種狀況發生,咱們能夠選擇重寫SampleSuggest的ToString()方法,或者爲AutoSuggestBox添加 SuggestionChosen事件,在事件中根據Suggestion決定TextBox中顯示的內容,在這裏,文章將使用第二種方法。orm

首先,在AutuSuggestBox的XAML定義中,添加如下屬性:xml

SuggestionChosen="AutoSuggestBox_SuggestionChosen"

而後,在相應的cs文件中添加以下代碼:對象

 

        private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
        {
            SampleSuggest suggest = args.SelectedItem as SampleSuggest;
            if (suggest == null)
                return;
            sender.Text = suggest.Value;
        }

 

這樣,一個AutoSuggestBox的功能就基本完成了!!

[Ignorable]

A story which is not interesting


這裏,請容許小編插入一段不是頗有趣的故事……不喜歡的讀者能夠直接跳過……
小編接到任務,須要開發一個這樣的控件:


小編看到,馬上就開心了,這不是AutoSuggestBox嘛!!簡單!!
因而,小編很興奮地寫下了如下代碼:

<AutoSuggestBox x:Name="autoSuggestBox"
                        PlaceholderText="anything to search?"
                        BorderThickness="0"
                        Background="#232323"
                        Foreground="White"
                        QueryIcon="Find"
                        VerticalContentAlignment="Center"
                        Width="500"
                        Height="50"
                        HorizontalAlignment="Center"/>

 結果出來的結果是這個樣子的……

此時,小編的心情是這樣的……

因而乎,小編便下定決心要改改這個AutoSuggestBox,就有了下邊的內容……

Style the AutoSuggestBox

經過以上的程序,咱們實現了AutoSuggestBox的基本功能,可是這樣一個控件通常都不會知足咱們程序開發的需求,由於它長得彷佛不那麼漂亮,或者說,不那麼知足咱們的需求。因此,咱們要本身定義AutoSuggestBox的style,讓它看上去更有範兒!

在VS designer的Document Outline面板中右鍵點擊定義的AutoSuggestBox -> Edit Template -> Edit a Copy…,則會在XAML裏生成AutoSuggestBox的Style。

經過觀察如下由VS生成的Style代碼,咱們能夠知道,這個神奇的AutoSuggestBox其實就是由一個TextBox和一個Popup構成的:

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="AutoSuggestBox">
                        <Grid>
                            <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" Header="{TemplateBinding Header}" Margin="0" PlaceholderText="{TemplateBinding PlaceholderText}" Style="{TemplateBinding TextBoxStyle}" Width="{TemplateBinding Width}" Canvas.ZIndex="0"/>
                            <Popup x:Name="SuggestionsPopup">
                                <Border x:Name="SuggestionsContainer">
                                    <Border.RenderTransform>
                                        <TranslateTransform x:Name="UpwardTransform"/>
                                    </Border.RenderTransform>
                                    <ListView x:Name="SuggestionsList" BorderBrush="{ThemeResource SystemControlForegroundBaseMediumLowBrush}" BorderThickness="{ThemeResource AutoSuggestListBorderThemeThickness}" Background="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" DisplayMemberPath="{TemplateBinding DisplayMemberPath}" IsItemClickEnabled="True" ItemTemplate="{TemplateBinding ItemTemplate}" ItemContainerStyle="{TemplateBinding ItemContainerStyle}" ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}" MaxHeight="{ThemeResource AutoSuggestListMaxHeight}" Margin="{ThemeResource AutoSuggestListMargin}">
                                        <ListView.ItemContainerTransitions>
                                            <TransitionCollection/>
                                        </ListView.ItemContainerTransitions>
                                    </ListView>
                                </Border>
                            </Popup>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>

那麼AutoSuggestBox的外觀,基本就是由其中的TextBox決定的,其中對應的就是一個AutoSuggestBoxTextBoxStyle:

        <Style x:Key="AutoSuggestBoxTextBoxStyle" TargetType="TextBox">
            <Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}"/>
            <Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}"/>
            <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
            <Setter Property="Background" Value="#ffdddddd"/>
            <Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundChromeDisabledLowBrush}"/>
            <Setter Property="SelectionHighlightColor" Value="{ThemeResource SystemControlHighlightAccentBrush}"/>
            <Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}"/>
            <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
            <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
            <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">
………
        </Style>

有了這個,咱們就能夠輕鬆定義AutoSuggestBox的外觀啦!咱們再看這個AutoSuggestBoxTextBoxStyle,它主要由如下幾個部分構成:ContentElement,它是一個ScrollViewer,就是咱們TextBox中的輸入部分;PlacehoderTextContentPresenter,經過它的名字,咱們能夠知道它就是顯示PlacehoderText的部分;還有DeleteButton和QueryButton兩個按鈕,分別對應控件中的刪除按鈕和查詢按鈕:

    <Border x:Name="BackgroundElement" Background="{TemplateBinding Background}" Grid.ColumnSpan="3" Margin="{TemplateBinding BorderThickness}" Opacity="{ThemeResource TextControlBackgroundRestOpacity}" Grid.Row="1" Grid.RowSpan="1"/>
    <Border x:Name="BorderElement" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.ColumnSpan="3" Grid.Row="1" Grid.RowSpan="1"/>
    <ContentPresenter x:Name="HeaderContentPresenter" Grid.ColumnSpan="3" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}" FontWeight="Normal" Margin="0,0,0,8" Grid.Row="0" TextWrapping="Wrap" Visibility="Collapsed" x:DeferLoadStrategy="Lazy"/>
    <ScrollViewer x:Name="ContentElement" AutomationProperties.AccessibilityView="Raw" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsTabStop="False" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled"/>
    <ContentControl x:Name="PlaceholderTextContentPresenter" Grid.ColumnSpan="3" Content="{TemplateBinding PlaceholderText}" Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}" IsHitTestVisible="False" IsTabStop="False" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1"/>
    <Button x:Name="DeleteButton" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="1" FontSize="{TemplateBinding FontSize}" IsTabStop="False" Margin="{ThemeResource HelperButtonThemePadding}" MinWidth="34" Grid.Row="1" Style="{StaticResource DeleteButtonStyle}" Visibility="Collapsed" VerticalAlignment="Stretch"/>
    <Button x:Name="QueryButton" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="2" FontSize="{TemplateBinding FontSize}" IsTabStop="False" Margin="{ThemeResource HelperButtonThemePadding}" MinWidth="34" Grid.Row="1" Style="{StaticResource QueryButtonStyle}" VerticalAlignment="Stretch"/>

這下就清楚不少了,那麼咱們就開始改造咱們的AutoSuggestBox吧!

首先是Foreground和Background,將AutoSuggestBoxTextBoxStyle裏如下代碼替換爲:

            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Background" Value="#232323"/>

而後,去掉Border:

            <Setter Property="BorderThickness" Value="0"/>


改變DeleteButton的VisualState,主要是PointOver和Pressed兩種狀態:

    <VisualState x:Name="PointerOver">
        <Storyboard>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BorderElement">
                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}"/>
            </ObjectAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="GlyphElement">
                <DiscreteObjectKeyFrame KeyTime="0" Value="Goldenrod"/>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
    </VisualState>
    <VisualState x:Name="Pressed">
        <Storyboard>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BorderElement">
                <DiscreteObjectKeyFrame KeyTime="0" Value="Goldenrod"/>
            </ObjectAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="GlyphElement">
                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltChromeWhiteBrush}"/>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
    </VisualState>

接着去Style咱們的QueryButton,首先是QueryIcon:

        <AutoSuggestBox x:Name="autoSuggestBox"
                        PlaceholderText="anything to search?"
                        BorderThickness="0"
                        Background="#232323"
                        Foreground="White"
                        VerticalContentAlignment="Center"
                        Width="500"
                        Height="50"
                        HorizontalAlignment="Center" Style="{StaticResource AutoSuggestBoxStyle1}">
            <AutoSuggestBox.QueryIcon>
                <BitmapIcon UriSource="ms-appx:Assets/actionbar_searchicon.png"/>
            </AutoSuggestBox.QueryIcon>
        </AutoSuggestBox>

而後是Button的大小,從Style裏面去更改:

  <Button x:Name="QueryButton" Width="50" Height="50" BorderThickness="{TemplateBinding BorderThickness}"  Grid.Column="2" FontSize="{TemplateBinding FontSize}" IsTabStop="False" Margin="{ThemeResource HelperButtonThemePadding}" MinWidth="34" Grid.Row="1" Style="{StaticResource QueryButtonStyle}" VerticalAlignment="Stretch"/>

修改QueryButton的Background,注意這裏並不能直接在Button裏添加Background屬性,而是在QueryButtonStyle裏的ContentPresenter裏面修改,同時把Margin設置爲0,以便於能填充所有背景:

  <ContentPresenter x:Name="ContentPresenter" AutomationProperties.AccessibilityView="Raw"  Background="Goldenrod" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="0" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>

由於調整了QueryButton的高度,因此控件總體高度會變高,爲了讓TextBox裏面的Text可以垂直居中,須要在AutoSuggestBoxTextBoxStyle中,將顯示正文的ContentElement和顯示hint的PlaceholderTextContentPresenter的VerticalAlignment設爲Center,同時設置FontSize:

  <ScrollViewer x:Name="ContentElement" AutomationProperties.AccessibilityView="Raw" FontSize="20" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsTabStop="False" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalAlignment="Center" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled"/>
  <ContentControl x:Name="PlaceholderTextContentPresenter" FontSize="20" Grid.ColumnSpan="3" Content="{TemplateBinding PlaceholderText}" Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}" IsHitTestVisible="False" IsTabStop="False" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalAlignment="Center"/>

程序運行結果以下圖所示:

和目標發現,好像還有什麼不同的……對!是Background!但是咱們已經設置過Background啦……這是爲何呢?經過觀察Style,咱們發現,有許多VisualState的Background都有Transparent的屬性,也就是說,AutoSuggestBox顯示出來的Background和它的父控件式相關的。簡單起見,咱們直接爲AutoSuggestBox添加一個背景色相同的Grid就好:

  <Grid Width="500" Background="#232323" VerticalAlignment="Center" HorizontalAlignment="Center">
      <AutoSuggestBox x:Name="autoSuggestBox"
                  PlaceholderText="anything to search?"
                  Style="{StaticResource AutoSuggestBoxStyle1}">
          <AutoSuggestBox.QueryIcon>
              <BitmapIcon UriSource="ms-appx:Assets/actionbar_searchicon.png"/>
          </AutoSuggestBox.QueryIcon>
      </AutoSuggestBox>
  </Grid>

再執行程序,發現和目標就相同了!!

Consulting

本文歸納地介紹了AutoSuggestBox的使用方法以及簡單自定義Style,但願初次使用該控件的開發者可以從中獲得些許幫助並交流一些開發經驗,謝謝!

相關文章
相關標籤/搜索