Search code examples
wpfxamlcarousel

WPF carousel / rotating elements


I am developing a software which takes automated photos using a camera. I want the user to be able to set the photo interval. I would like that to look and work like some kind of carousel.

This is what it should look like:

when I swipe (or drag with the mouse) it should move in the given direction:

the selected item should always be in the middle and if it's the last one, the first should rotate to the right (or vice versa).

I have already added the basic swipe/drag functionality, it recognizes whenever an action has been taken and in which direction. Hovewer, I have no idea on how to actually move the items.

Currently, the items are defined like this:

<StackPanel x:Name="countdownContainer" Background="{StaticResource GuiSideBarBackgroundColor}" Orientation="Horizontal" HorizontalAlignment="Center">
    <TextBlock Style="{StaticResource CountdownElement}" Text="3s"/>
    <TextBlock Style="{StaticResource CountdownElementSelected}" Text="5s"/>
    <TextBlock Style="{StaticResource CountdownElement}" Text="10s"/>
</StackPanel>

I know some basics about animation, but I don't know how to animate them like I need here. Would it be better to use some other controls instead of a StackPanel and TextBlock elements?


Solution

  • I base myself on the styles you have declared above, but not included in the code. You can do this in pure xaml and a viewmodel. This will be a very simple and crude example. Alternativly you could implement a custom control that inherhits a class that has the Selector class somewhere down the hierarchy, rather than control which is the default provided by the default cust control template in vs. I recommend you have a look at the MVVM pattern and Galasoft MVVM Light. In this example I've excluded touch gestures, they are easy to implement and using EventToCommand you can use them directly in your vm or custcontrol*.

    Resources

    <ItemsPanelTemplate x:Key="YourItemsPanelTemplate">
       <VirtualizingStackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>
    
    <DataTemplate x:Key="YourDataTemplate">
        <TextBlock Style="{StaticResource CountdownElement}" Text="{Binding .}" x:Name="PART_TextBlock"/>
        <DataTemplate.Triggers>
            <DataTrigger
            Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= {x:Type ListViewItem}},Path=IsSelected}" Value="True"> 
                <!-- Here I'm just changing your fontsize, do whatever you want here :) -->                                       
                <Setter Property="FontSize" Value="34" TargetName="PART_TextBlock"/>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
    
    <Style x:Key="YourContainerStyle" TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment" Value="Center" />
        <Setter Property="VerticalContentAlignment" Value="Bottom" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Margin" Value="10,0,10,0" />
        <Setter Property="Padding" Value="0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListViewItem}">
                    <ContentPresenter x:Name="PART_ContentPresenter"
                                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                    Margin="{TemplateBinding Padding}"
                                    Content="{TemplateBinding Content}"
                                    ContentTemplate="{StaticResource YourDataTemplate}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <Style x:Key="YourListBoxStyle" TargetType="ListBox">
        <Setter Property="ItemContainerStyle" Value="{StaticResource YourContainerStyle}"/>
        <Setter Property="ItemTemplate" Value="{StaticResource YourDataTemplate}"/>
        <Setter Property="ItemsPanel" Value="{StaticResource YourItemsPanelTemplate}"/>
    </Style>
    

    There are your styles, now for the xaml code, note that I bind your items here, and are using the styles described above.

    XAML

    <Grid>
        <ListView Background="{StaticResource  GuiSideBarBackgroundColor}" 
            Style="{StaticResource YourListBoxStyle}"
            ItemsSource="{Binding CountDownElements}"
            SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
    <Grid>
    

    The ViewModel Remember to set this as datacontext for your view or copy the code to your code behind.

    public class YourViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<string> countDownElements = new ObservableCollection<string> { "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s", "10s" };
        private string selectedItem;
    
        public ObservableCollection<string> CountDownElements
        {
            get { return countDownElements; }
            set
            {
                if (Equals(value, countDownElements)) return;
                countDownElements = value;
                OnPropertyChanged();
            }
        }
    
        public string SelectedItem
        {
            get { return selectedItem; }
            set
            {
                if (value == selectedItem) return;
                selectedItem = value;
                OnPropertyChanged();
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator] // remove if you don't have R#
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    Output

    Very simple stuff

    Hope it helps or at least kicks you in the right direction! :)

    Cheers

    Stian