locked
Revolving ComboBox: how do I make a ComboBox not loop around the items?

    Question

  • I have a simple ComboBox with a bunch of items - when I deploy my app to a touch enabled tablet and I tap on the ComboBox, I can scroll through the list of items.

    The problem is that when I reach the last item, the list of items loops around and starts again from the first item (with a space between them as described in this post about the Carousel - http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/37d44c05-2d3e-46de-bbc2-00ac975df3be )

    How can I stop this behavior and make the ComboBox act like a standard ComboBox?  When it reaches the first or last values it should not loop around

    Tuesday, May 22, 2012 8:18 PM

Answers

  • The XAML ComboBox does not provide an easy way to opt out to its placement logic. You might have to resort to deriving from ComboBox and overriding virtual methods to keep the default placement logic from occurring.

    -mark
    Program Manager
    Microsoft
    This post is provided "as-is"
    • Marked as answer by Emmanuel Huna Wednesday, May 23, 2012 9:48 PM
    Wednesday, May 23, 2012 6:58 PM

All replies

  • You'll just want to pick a different ItemsPanel for that.  Like so:

            <ComboBox Height="20" Width="500">
                <ComboBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel />
                    </ItemsPanelTemplate>
                </ComboBox.ItemsPanel>

    Hope this helps,
    Matt


    SDET : Deployment/Hosting

    Tuesday, May 22, 2012 10:14 PM
  • Matt, thanks - I tried it out but if I just change the <CarouselPanel /> to a <StackPanel />, when I tap or click on the ComboBox, the StackPanel now takes the full width of the screen.  

    I tried limiting its width to 300 for example, but that didn't work - the stackpanel still took the full screen width.

    Using Blend, I did create a copy of the combobox template (see it below) - what could we change to get the following behavior:

    1. Items should not loop.
    2. Width of the ComboBox when expanded should be the same as width when not expanded (DropDownState == Opened)
    3. When expanded, the ComboBox should expand down - currently if I select an item in the middle of the list and expand the combobox, items appear above and below the combobox.

    Help with any of the above is appreciated -

        <Style x:Key="ComboBoxStyleCustom"  TargetType="ComboBox">
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Background" Value="Red" />
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
            <Setter Property="MinWidth" Value="258"/>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
            <Setter Property="BorderBrush" Value="White"/>
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="FontWeight" Value="Light"/>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <StackPanel />
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ComboBox">
                        <Grid>
                            <Grid.Resources>
                                <Style x:Key="DropDownToggleStyle" TargetType="ToggleButton">
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="ToggleButton">
                                                <Rectangle x:Name="LayoutRoot" Fill="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                                    <VisualStateManager.VisualStateGroups>
                                                        <VisualStateGroup x:Name="CommonStates">
                                                            <VisualState x:Name="Normal"/>
                                                            <VisualState x:Name="PointerOver"/>
                                                            <VisualState x:Name="Pressed">
                                                                <Storyboard>
                                                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="LayoutRoot">
                                                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ComboBoxPressedFillBrush}"/>
                                                                    </ObjectAnimationUsingKeyFrames>
                                                                </Storyboard>
                                                            </VisualState>
                                                            <VisualState x:Name="Disabled"/>
                                                        </VisualStateGroup>
                                                    </VisualStateManager.VisualStateGroups>
                                                </Rectangle>
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </Grid.Resources>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="32"/>
                            </Grid.ColumnDefinitions>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="PointerOver">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Background">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Red"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ComboBoxPointerOverBorderBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Highlight">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ComboBoxHighlightHoverFillBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Pressed">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Red"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="White"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Background">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ControlDisabledFillBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ComboBoxDisabledBorderBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ControlDisabledTextBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DropDownGlyph">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ControlDisabledTextBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="FocusStates">
                                    <VisualState x:Name="Focused">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HighlightBackground"/>
                                            <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Highlight"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Unfocused"/>
                                    <VisualState x:Name="PointerFocused"/>
                                    <VisualState x:Name="FocusedDropDown">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="PopupBorder">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="DropDownStates">
                                    <VisualState x:Name="Opened">
                                        <Storyboard>
                                            <SplitOpenThemeAnimation ClosedTargetName="DropDownToggle" ContentTranslationOffset="0" ContentTargetName="ScrollViewer" ClosedLength="{Binding TemplateSettings.DropDownClosedHeight, RelativeSource={RelativeSource Mode=TemplatedParent}}" OffsetFromCenter="{Binding TemplateSettings.DropDownOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}" OpenedTargetName="PopupBorder" OpenedLength="{Binding TemplateSettings.DropDownOpenedHeight, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Closed">
                                        <Storyboard>
                                            <SplitCloseThemeAnimation ClosedTargetName="DropDownToggle" ContentTranslationOffset="40" ContentTranslationDirection="{Binding TemplateSettings.SelectedItemDirection, RelativeSource={RelativeSource Mode=TemplatedParent}}" ContentTargetName="ScrollViewer" ClosedLength="{Binding TemplateSettings.DropDownClosedHeight, RelativeSource={RelativeSource Mode=TemplatedParent}}" OffsetFromCenter="{Binding TemplateSettings.DropDownOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}" OpenedTargetName="PopupBorder" OpenedLength="{Binding TemplateSettings.DropDownOpenedHeight, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Border x:Name="Background" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0" Background="Red" Grid.ColumnSpan="2"/>
                            <Rectangle x:Name="HighlightBackground" Grid.ColumnSpan="2" Opacity="0">
                                <Rectangle.Fill>
                                    <ImageBrush />
                                </Rectangle.Fill>
                            </Rectangle>
                            <Rectangle x:Name="Highlight" Fill="Red" Margin="{TemplateBinding BorderThickness}" Opacity="0"/>
                            <ToggleButton x:Name="DropDownToggle" Grid.ColumnSpan="2" Style="{StaticResource DropDownToggleStyle}"/>
                            <ContentPresenter x:Name="ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            <TextBlock x:Name="DropDownGlyph" Grid.Column="1" Foreground="White" FontWeight="{StaticResource Bold}" FontSize="{StaticResource ComboBoxGlyphFontSize}" FontFamily="{StaticResource SymbolFontFamily}" HorizontalAlignment="Right" IsHitTestVisible="False" Margin="0,0,6,4" Text="?" VerticalAlignment="Center"/>
                            <Popup x:Name="Popup">
                                <Border x:Name="PopupBorder" 
                                        BorderBrush="{StaticResource ComboBoxPopupBorderBrush}" 
                                        BorderThickness="{StaticResource ComboBoxPopupBorderThickness}" 
                                        Background="Red" HorizontalAlignment="Stretch">
                                    <ScrollViewer x:Name="ScrollViewer" 
                                                  Foreground="White" 
                                                  HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" 
                                                  HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" 
                                                  VerticalSnapPointsType="Mandatory" 
                                                  VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" 
                                                  VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" 
                                                  VerticalSnapPointsAlignment="Near" 
                                                  ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
                                        <ItemsPresenter/>
                                    </ScrollViewer>
                                </Border>
                            </Popup>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    

    Tuesday, May 22, 2012 11:26 PM
  • The XAML ComboBox does not provide an easy way to opt out to its placement logic. You might have to resort to deriving from ComboBox and overriding virtual methods to keep the default placement logic from occurring.

    -mark
    Program Manager
    Microsoft
    This post is provided "as-is"
    • Marked as answer by Emmanuel Huna Wednesday, May 23, 2012 9:48 PM
    Wednesday, May 23, 2012 6:58 PM
  • The default behavior of a Metro dropdown seems to work this way - see for example WIN+I (Settings) > More PC Settings (at the bottom) > Share > Frequent > Items in list.  Change it for example to '5'; now tap on it and you'll notice the '5' is selected when you open the dropdown, while values 1 through 4 are above the selection box.

    This is different than an HTML or WPF/Silverlight dropdown - it's a Metro dropdown.  I believe Microsoft has put some thought into what makes it easier for touch: by showing values above and below, the user has to swipe less to select a new value.  The looping was probably done for the same reason.

    Unfortunately, my manager and testers are used to standard HTML and WPF/Silverlight dropdowns, so they are confused by this behavior.

    It would be great if Microsoft published some info on why the ComboBox works in this non-standard way in Metro.  This would give me some ammunition in convincing my manager that it's a bad idea to override these default behaviors.

    I'd be ok with a blog article in http://windowsteamblog.com/windows/b/windowsexperience/ for example.

    Wednesday, May 23, 2012 9:48 PM
  • I'll suggest it as a blog entry! Thanks!

    -mark
    Program Manager
    Microsoft
    This post is provided "as-is"
    Thursday, May 24, 2012 1:08 AM