Equivalent of editable ComboBox in WinRT?

痴心易碎 提交于 2019-12-05 12:08:07

thanks to edward.ho.tpe's answer I wrote myself a little EditableComboBox by using an TextBox inside a ComboBoxItem. If you want to use it multiple times it might be better to create a UserControl.

However this is how I did it:

The style:

<SolidColorBrush x:Key="TransparentBrush" Color="Transparent"/>
<Style x:Key="ComboBoxItemTextBox" TargetType="TextBox">
    <Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}"/>
    <Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}"/>
    <Setter Property="Foreground" Value="{ThemeResource TextBoxForegroundThemeBrush}"/>
    <Setter Property="Background" Value="{StaticResource TransparentBrush}"/>
    <Setter Property="BorderBrush" Value="{StaticResource TransparentBrush}"/>
    <Setter Property="SelectionHighlightColor" Value="{ThemeResource TextSelectionHighlightColorThemeBrush}"/>
    <Setter Property="BorderThickness" Value="0"/>
    <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="Margin" Value="-10,0,0,0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TextBox">
                <Grid>
                    <Grid.Resources>
                        <Style x:Name="DeleteButtonStyle" TargetType="Button">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="Button">
                                        <Grid>
                                            <VisualStateManager.VisualStateGroups>
                                                <VisualStateGroup x:Name="CommonStates">
                                                    <VisualState x:Name="Normal"/>
                                                    <VisualState x:Name="PointerOver">
                                                        <Storyboard>
                                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundElement">
                                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}"/>
                                                            </ObjectAnimationUsingKeyFrames>
                                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
                                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}"/>
                                                            </ObjectAnimationUsingKeyFrames>
                                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="GlyphElement">
                                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}"/>
                                                            </ObjectAnimationUsingKeyFrames>
                                                        </Storyboard>
                                                    </VisualState>
                                                    <VisualState x:Name="Pressed">
                                                        <Storyboard>
                                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundElement">
                                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}"/>
                                                            </ObjectAnimationUsingKeyFrames>
                                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
                                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}"/>
                                                            </ObjectAnimationUsingKeyFrames>
                                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="GlyphElement">
                                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}"/>
                                                            </ObjectAnimationUsingKeyFrames>
                                                        </Storyboard>
                                                    </VisualState>
                                                    <VisualState x:Name="Disabled">
                                                        <Storyboard>
                                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundElement"/>
                                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BorderElement"/>
                                                        </Storyboard>
                                                    </VisualState>
                                                </VisualStateGroup>
                                            </VisualStateManager.VisualStateGroups>
                                            <Border x:Name="BorderElement" BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
                                            <Border x:Name="BackgroundElement" Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}" Margin="{TemplateBinding BorderThickness}">
                                                <TextBlock x:Name="GlyphElement" AutomationProperties.AccessibilityView="Raw" Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}" FontStyle="Normal" FontFamily="{ThemeResource SymbolThemeFontFamily}" HorizontalAlignment="Center" Text="&#xE0A4;" VerticalAlignment="Center"/>
                                            </Border>
                                        </Grid>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Grid.Resources>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundElement">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBackgroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBorderThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentElement">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextContentPresenter">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Normal">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="{ThemeResource TextControlBackgroundThemeOpacity}" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundElement"/>
                                    <DoubleAnimation Duration="0" To="{ThemeResource TextControlBorderThemeOpacity}" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BorderElement"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="{ThemeResource TextControlPointerOverBackgroundThemeOpacity}" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundElement"/>
                                    <DoubleAnimation Duration="0" To="{ThemeResource TextControlPointerOverBorderThemeOpacity}" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BorderElement"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Focused"/>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="ButtonStates">
                            <VisualState x:Name="ButtonVisible"/>
                            <VisualState x:Name="ButtonCollapsed"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="BackgroundElement" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" Margin="{TemplateBinding BorderThickness}" Grid.Row="1" Grid.RowSpan="1"/>
                    <Border x:Name="BorderElement" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.ColumnSpan="2" Grid.Row="1" Grid.RowSpan="1"/>
                    <ContentPresenter x:Name="HeaderContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource TextBoxForegroundHeaderThemeBrush}" FontWeight="Semilight" Margin="0,4,0,4" Grid.Row="0"/>
                    <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="2" Content="{TemplateBinding PlaceholderText}" Foreground="{ThemeResource TextBoxPlaceholderTextThemeBrush}" 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" Grid.Row="1" Style="{StaticResource DeleteButtonStyle}" Visibility="Collapsed" VerticalAlignment="Stretch"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The ComboBox:

<ComboBox SelectionChanged="ComboBox_SelectionChanged">
    <ComboBoxItem IsSelected="True">
        <TextBox x:Name="tbComboBox" Style="{StaticResource ComboBoxItemTextBox}" KeyDown="tbComboBox_KeyDown"/>
    </ComboBoxItem>
    <ComboBoxItem>Item 1</ComboBoxItem>
    <ComboBoxItem>Item 2</ComboBoxItem>
    <ComboBoxItem>Item 3</ComboBoxItem>
    <ComboBoxItem>Item 4</ComboBoxItem>
</ComboBox>

The event handlers:

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (e.AddedItems.Count == 1 && e.AddedItems[0] != (sender as ComboBox).Items[0])
    {
        (sender as ComboBox).SelectedIndex = 0;
        tbComboBox.Text = (e.AddedItems[0] as ComboBoxItem).Content as String;
    }
}

private void tbComboBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
    if (e.Key == Windows.System.VirtualKey.Space)
    {
        if (tbComboBox.SelectionLength > 0)
        {
            tbComboBox.Text = tbComboBox.Text.Remove(tbComboBox.SelectionStart, tbComboBox.SelectionLength);
            tbComboBox.SelectionLength = 0;
        }
        int pos = tbComboBox.SelectionStart;
        tbComboBox.Text = tbComboBox.Text.Insert(pos, " ");
        tbComboBox.SelectionStart = pos + 1;
        e.Handled = true;
    }
}

So basically what this is:

As edward.ho.tpe suggested a TextBox as the content of the first item of a ComboBox. The TextBox has a custom style that removes the background, the border, moves it a little to the left (optional - but i like it since it provides more space to write), and changes the delete button to stay collapsed. The SelectionChanged event handler makes sure that the TextBox stays the SelectedItem and only changes the Text based on the content of the selected item and the KeyDown event handler handles the space keydown because otherwise that would trigger the Dropdown of the ComboBox.

Syncfusion Controls for WinRT includes a ComboBox too. But it is not free.

Since combobox item in WinRT can be anything, you can :

<ComboBoxItem>
<TextBox Text="something"/>
</ComboBoxItem>

When user clicks that item, he/she can edit its content.

It's up to you whether to have the edit result change that item's editbox or just adding a new combobox item to that combobox. Process this in the LostFocus event.

You are not missing anything. There is no editable combobox in the out-of-the-box toolbox.

You might have tried the winrt xaml toolkit @ http://winrtxamltoolkit.codeplex.com/ But that does not have one either. Too bad because it is free.

You might have looked at ComponentOne @ http://www.componentone.com/SuperProducts/StudioWinRTXAML/ But that does not have one either.

You might have looked at Infragistics @ http://www.infragistics.com/products/technology-previews/windows8/ But that toolkit is in "preview" still.

You might have looked at Telerik @ http://www.telerik.com/products/windows-8/overview.aspx They have a combobox http://www.telerik.com/products/windows-8/controls/combo-box.aspx

But you might want something free. There is no Microsoft XAML Toolkit at this time. So you need to build your own or find an open source project. I could not find such a project at this time.

But what is an editable combobox really? A standard combobox and a textbox, right?

Elavarasan M

By setting

combobox.ComboBoxMode = ComboBoxModes.Editable 

in syncfusion SfComboBox, you can edit the combobox.

Regards,

Elavarasan M

I know it's an old question but for new searchers like me I found the property ComboBox.IsEditable is fine

Try The UIControl called AutoSuggestBox

<AutoSuggestBox PlaceholderText="Company">
     <AutoSuggestBox.QueryIcon>
         <SymbolIcon Symbol="find"/>
     </AutoSuggestBox.QueryIcon>
</AutoSuggestBox>

I have searched everywhere for a Searchable Combobox and have resolved to writing my own. It is not quite like your editable combobox, but will filter the options in a combobox when typing. Maybe this could help someone.

You would define a new control like this:

<control:SearchableComboBox Margin='5'
          x:Name='ComboBoxCountry'
          ItemsSourceList='{Binding CountryList}'
          ItemsSourceListBase='{Binding CountryListBase}'
          PlaceholderText='Select Country'/>

Unfortunately you still need to provide a duplicate list, CountryListBase. If you have suggestions on improving this, let me know!

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;

namespace eMoyoConnect.Controls
{
    public class SearchableComboBox : ComboBox
    {

        public ObservableCollection<string> ItemsSourceList
        {
            get { return (ObservableCollection<string>)GetValue(ItemsSourceListProperty); }
            set
            {
                SetValue(ItemsSourceListProperty, value);
                this.ItemsSource = ItemsSourceList;
            }
        }

        public static readonly DependencyProperty ItemsSourceListProperty =
            DependencyProperty.Register("ItemsSourceList", typeof(ObservableCollection<string>), typeof(SearchableComboBox),
            new PropertyMetadata(null, ItemsSourceListChanged));

        private static void ItemsSourceListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var plv = d as SearchableComboBox;
            plv.ItemsSource = e.NewValue;
        }

        public Collection<string> ItemsSourceListBase
        {
            get { return (Collection<string>)GetValue(ItemsSourceListBaseProperty); }
            set
            {
                SetValue(ItemsSourceListBaseProperty, value);
                this.ItemsSource = ItemsSourceListBase;
            }
        }

        public static readonly DependencyProperty ItemsSourceListBaseProperty =
            DependencyProperty.Register("ItemsSourceListBase", typeof(Collection<string>), typeof(SearchableComboBox),
            new PropertyMetadata(null, ItemsSourceListBaseChanged));

        private static void ItemsSourceListBaseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var plv = d as SearchableComboBox;
            plv.ItemsSource = e.NewValue;
        }

        string FilterString = "";
        protected override void OnKeyDown(KeyRoutedEventArgs e)
        {
            if (IsLetterOrSpace(e.Key))
            {
                if (e.Key == VirtualKey.Space)
                    FilterString += " ";
                else
                    FilterString += e.Key.ToString();
                FilterList(FilterString);
                this.ItemsSource = ItemsSourceList;
                if (ItemsSourceList.Count > 0)
                    this.SelectedIndex = 0;
            }
            else if (e.Key == VirtualKey.Back)
            {
                if (FilterString.Length > 0)
                {
                    FilterString = FilterString.Substring(0, FilterString.Length - 1);
                    FilterList(FilterString);
                    this.ItemsSource = ItemsSourceList;
                    if (ItemsSourceList.Count > 0)
                        this.SelectedIndex = 0;
                }
            }

            if (e.Key != VirtualKey.Space)
            {
                base.OnKeyDown(e);
            }
        }

        protected override void OnGotFocus(RoutedEventArgs e)
        {
            string selectedValue = "";
            if (this.SelectedValue != null)
                selectedValue = (string)this.SelectedValue;
            FilterString = "";
            FilterList(FilterString);
            if (!string.IsNullOrEmpty(selectedValue))
                this.SelectedValue = selectedValue;
        }

        internal void FilterList(string FilterString)
        {
            ItemsSourceList.Clear();
            IEnumerable<string> list;
            if (!string.IsNullOrEmpty(FilterString))
                list = ItemsSourceListBase.Where(x => x.StartsWith(FilterString));
            else
                list = ItemsSourceListBase;
            foreach (var item in list)
                ItemsSourceList.Add(item);
        }

        private bool IsLetterOrSpace(VirtualKey key)
        {
            return (key == VirtualKey.A
                || key == VirtualKey.B
                || key == VirtualKey.C
                || key == VirtualKey.D
                || key == VirtualKey.E
                || key == VirtualKey.F
                || key == VirtualKey.G
                || key == VirtualKey.H
                || key == VirtualKey.I
                || key == VirtualKey.J
                || key == VirtualKey.K
                || key == VirtualKey.L
                || key == VirtualKey.M
                || key == VirtualKey.N
                || key == VirtualKey.O
                || key == VirtualKey.P
                || key == VirtualKey.Q
                || key == VirtualKey.R
                || key == VirtualKey.S
                || key == VirtualKey.T
                || key == VirtualKey.U
                || key == VirtualKey.V
                || key == VirtualKey.W
                || key == VirtualKey.X
                || key == VirtualKey.Y
                || key == VirtualKey.Z
                || key == VirtualKey.Space);
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!