views:

1251

answers:

2

I have a WPF ListBox bound to a data object. Inside the listbox are a series of images with text. It is layed out in a horizontal fashion, and mousing over the left or right sides of the box scroll the items left or right respectively.

let's say there are 20 items in the listbox. I'm trying to figure out how when I hit position 19 item (0 based) that I can cycle the box and start the collection over, so that it goes 1 -19 and so on. It would need to also cycle the other way, so that if you were on item 0, and scrolled left, you would get 19.

I've tried the KeyboardNavigation.DirectionalNavigation="Cycle" but that doesnt' seem to do anything for me, and I was grasping at straws since this has nothing to do with the keyboard, it's all mouse based.

        <ListBox ItemsSource="{Binding Source={StaticResource WPFApparelCollection}}" Margin="24,-7,39,-19" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden" SelectionMode="Single" x:Name="list1" MouseLeave="List1_MouseLeave" MouseMove="List1_MouseMove" Style="{DynamicResource ListBoxStyle1}" Background="Transparent" BorderThickness="0">
            <ListBox.Resources>
                <!-- override the system brushes so that selected items are transparent whether the ListBox has focus or not -->
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
            </ListBox.Resources>
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="Background" Value="Transparent" />
                    <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
                    <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
                    <Setter Property="Padding" Value="20,10,20,10" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <Border x:Name="Bd" SnapsToDevicePixels="true" Background="Transparent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                                    <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                </Border>
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsSelected" Value="true">
                                        <Setter Property="Background" TargetName="Bd" Value="Transparent" />
                                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
                                    </Trigger>
                                    <MultiTrigger>
                                        <MultiTrigger.Conditions>
                                            <Condition Property="IsSelected" Value="true" />
                                            <Condition Property="Selector.IsSelectionActive" Value="false" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
                                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
                                    </MultiTrigger>
                                    <Trigger Property="IsEnabled" Value="false">
                                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel Orientation="Horizontal" IsItemsHost="True" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <Image Source="{Binding Image}" MouseLeave="Image_MouseLeave" MouseEnter="Image_MouseEnter" Cursor="Hand" Tag="{Binding Link}" MouseLeftButtonDown="Image_MouseLeftButtonDown" VerticalAlignment="Top" HorizontalAlignment="Left"></Image>
                        <Label Content="{Binding Name}" Cursor="Hand" Tag="{Binding Link}" MouseLeftButtonDown="Label_MouseLeftButtonDown" VerticalAlignment="Bottom" Foreground="White" Style="{StaticResource Gotham-Medium}" FontSize="8pt" HorizontalAlignment="Center" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
A: 

There are a number of free and commercial WPF carousel implementations that do this. Take a look at this roundup http://mdavey.wordpress.com/2007/04/03/wpf-carousel/

Scott Weinstein
I think those are all pretty lame. Been there done that... saw that post a while ago. The good ones are bucks. And they aren't quite what I'm trying to achieve. Thanks.
John Batdorf
+1  A: 

The issue here is not really with the ListBox, but with the ScrollViewer inside its control template; therefore to make the items cycle you'll need to change the ScrollViewer in some way. I have written a control that derives from ScrollViewer that cycles in a vertical direction... but it should be easy to see how to make it work horizontally too.

public class CyclicScrollViewer : ScrollViewer
{
    public CyclicScrollViewer()
    {
        this.CommandBindings.Add(new CommandBinding(ScrollBar.LineUpCommand, LineCommandExecuted));
        this.CommandBindings.Add(new CommandBinding(ScrollBar.LineDownCommand, LineCommandExecuted));
    }

    private void LineCommandExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        if (e.Command == ScrollBar.LineUpCommand)
        {
            if (this.VerticalOffset == 0)
                this.ScrollToEnd();
            else
                this.LineUp();
        }

        if (e.Command == ScrollBar.LineDownCommand)
        {
            if (this.VerticalOffset == this.ScrollableHeight)
                this.ScrollToTop();
            else
                this.LineDown();
        }
    }
}

The ScrollTo... and Line... methods already exist on the ScrollViewer making the coding pretty simple. All I'm doing here is checking the current offset against the bounds of the viewer before scrolling.

The next step is to insert the new ScrollViewer into the control template for the target control, in this case the ListBox. Here's a XAML snippet to demonstrate this.

        <ControlTemplate x:Key="{x:Type ListBox}" TargetType="ListBox">
            ...
                <l:CyclicScrollViewer 
                    Padding="{TemplateBinding Control.Padding}" 
                    Focusable="False">
                    <ItemsPresenter ... />
                </l:CyclicScrollViewer>
            ...
        </ControlTemplate>

I've posted a sample application that uses this code here. This sample does not have keyboard support, but that can be simply added by overriding the OnKeyDown method and executing the appropriate Line... commands. I hope this helps.

PaulJ
This kind of answered my question, my problem is I'm not scrolling through the list quite the way you are, I don't think I can do what I'd like, but I'll mark it answered, because I think it's a creative solution. :)
John Batdorf