Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF 2D Image Carousel

0.00/5 (No votes)
18 Mar 2011 1  
A 2D carousel like UserControl in WPF
ScreenshotMain.png

Introduction

This article describes how I went about creating a 2D image carousel. The reason for creating it... I just wanted to see if I could. I was actually watching a Blackberry Playbook video and I wondered whether I could create a carousel like control that replicated the carousel features in the video. The Playbook's though is more elegant and I may all but envy. My carousel UserControl, aptly named ImageCarousel, only deals with images and five images at that. Sad as that may be, you'll hopefully pick up something useful from this article so read on if you're still interested.

Requirements

To run the project provided from the download link above, you require either of the following:

  • Visual Studio 2010
  • Expression Blend

NB: If you're using the Express edition of Visual Studio, ensure that you open the solution using Visual Basic Express.

ImageCarousel

How It Works

To move the images, hold down the left mouse button and drag either to the left or to the right. When you let go of the left mouse button, the images will move in the intended direction.

Screenshot2.png

Screenshot3.png

Design and Layout

I put everything together in Expression Blend. The following image shows how some of the elements are laid out in the UserControl:

Layout1.png

There are five main Grid controls which are visible in the image above, and an extra five Grid controls to the left and also to the right of the main ImageGrids. The following image shows the Grids that are to the left of the main Grids.

Layout2.png

The Grid controls to the left and to the right of the main Grids are actually copies of the main Grids. I will explain why we need them as you read on.

NB: The CarouselCanvas' ClipToBounds property is set to True.

ClipToBounds.png

The Code

In ImageCarousel's Initialized event handler, we do the following:

Private Sub ImageCarousel_Initialized(ByVal sender As Object, _
                         ByVal e As System.EventArgs) Handles Me.Initialized

    AddHandler CarouselTimer.Tick, AddressOf CarouselTimer_Tick
    CarouselTimer.Interval = New TimeSpan(0, 0, 0, 0, 100)

    ImageGrid_1.RenderTransform = GridScaleTr_1
    ImageGrid_2.RenderTransform = GridScaleTr_2
    ImageGrid_3.RenderTransform = GridScaleTr_3
    ImageGrid_4.RenderTransform = GridScaleTr_4
    ImageGrid_5.RenderTransform = GridScaleTr_5

    ' Increase the scale of the Grid in the middle
    ' of the UserControl.
    Dim y As Double = Canvas.GetTop(ImageGrid_3)
    MidCanvasX = CarouselCanvas.ActualWidth / 2
    GridScaleTr_3.ScaleX = scale
    GridScaleTr_3.ScaleY = scale
    GridScaleTr_3.CenterX = MidCanvasX + (ImgGridWidth / 2)
    GridScaleTr_3.CenterY = y + (ImgGridHeight / 2)

    ChangeImagesOpacity()
End Sub

In the method above, a method named ChangeImagesOpacity is called. This method changes the opacity of all images in the UserControl to 60%, except those of the Grid control in the middle.

Private Sub ChangeImagesOpacity()
    For Each ImgGrid As Grid In CarouselCanvas.Children
        If Canvas.GetLeft(ImgGrid) <> 270 Then
            For Each el As UIElement In ImgGrid.Children
                If TypeOf (el) Is Image Then
                    el.Opacity = 0.6
                End If
            Next
        ElseIf Canvas.GetLeft(ImgGrid) = 270 Then
            For Each el As UIElement In ImgGrid.Children
                If TypeOf (el) Is Image Then
                    el.Opacity = 1
                End If
            Next
        End If
    Next
End Sub

When the user presses the left mouse button, to start off the process of moving the images, the UserControl's CarouselCanvas MouseLeftButtonDown event handler is called.

Private Sub CarouselCanvas_MouseLeftButtonDown(ByVal sender As Object, _
                           ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                           Handles CarouselCanvas.MouseLeftButtonDown
    If IsCarouseling = False Then
        InitMouseX = e.GetPosition(CarouselCanvas).X
        InitMouseY = e.GetPosition(CarouselCanvas).Y
    End If
End Sub

When the user releases the left mouse button, the following takes place:

Private Sub CarouselCanvas_MouseLeftButtonUp(ByVal sender As Object, _
                                 ByVal e As System.Windows.Input.MouseButtonEventArgs) _
                                 Handles CarouselCanvas.MouseLeftButtonUp
    If IsCarouseling = False Then
        IsCarouseling = True

        FinalMouseX = e.GetPosition(CarouselCanvas).X
        FinalMouseY = e.GetPosition(CarouselCanvas).Y

        DiffX = FinalMouseX - InitMouseX
        DiffY = FinalMouseY - InitMouseY

        ' Check whether swipe is horizontal.
        If Math.Abs(DiffX) > Math.Abs(DiffY) Then
            CarouselTimer.Start()
        Else
            IsCarouseling = False
        End If
    End If
End Sub

If the user moved the mouse in a horizontal direction, the CarouselTimer is started and during its Tick event, we do the following:

Private Sub CarouselTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
    If move <> 135 Then
        MoveImageGrids()
        move += shift
        ChangeImagesOpacity()
    Else
        CarouselTimer.Stop()
        move = 0
        IsCarouseling = False
    End If
    End Sub

The MoveImageGrids method that is called above causes the eventual movement of the Grids:

Private Sub MoveImageGrids()
    ' Right swipe.
    If DiffX > 0 Then
        For Each ImgGrid As Grid In CarouselCanvas.Children
            Dim x As Double = Canvas.GetLeft(ImgGrid)
            Canvas.SetLeft(ImgGrid, (x + shift))
            RightCheckOriginals(ImgGrid)
            RghtCheckCopy1s(ImgGrid)
            RghtCheckCopy2s(ImgGrid)
            ScaleUpGrid(ImgGrid)
            ScaleGridToNormal(ImgGrid)
        Next
    Else
        ' Left swipe.
        For Each ImgGrid As Grid In CarouselCanvas.Children
            Dim x As Double = Canvas.GetLeft(ImgGrid)
            Canvas.SetLeft(ImgGrid, (x - shift))
            LeftCheckOriginals(ImgGrid)
            LeftCheckCopy1s(ImgGrid)
            LeftCheckCopy2s(ImgGrid)
            ScaleUpGrid(ImgGrid)
            ScaleGridToNormal(ImgGrid)
        Next
    End If
End Sub

NB: It takes 900 milliseconds to move a Grid to the next final position.

The RightCheckOriginals method checks whether the last Grid, among the main Grids, x-position has reached the right edge of CarouselCanvas and places the Grid accordingly.

' Set the location of main ImageGrids to zero
' when their x-position reaches right-edge of canvas.
Private Sub RightCheckOriginals(ByVal ImgGrid As Grid)
    If (ImgGrid Is ImageGrid_1) Or (ImgGrid Is ImageGrid_2) _
    Or (ImgGrid Is ImageGrid_3) Or (ImgGrid Is ImageGrid_4) _
    Or (ImgGrid Is ImageGrid_5) Then
        If Canvas.GetLeft(ImgGrid) >= 675 Then
            Canvas.SetLeft(ImgGrid, 0)
        End If
    End If
End Sub

The RghtCheckCopy1s and RghtCheckCopy2s methods also do something similar, but this time for the Grids located to the left and to the right of the main Grids respectively.

' Set the location of ImageGrids on left side
' of the main ImageGrids to -675 when their x-position
' reaches zero.
Private Sub RghtCheckCopy1s(ByVal ImgGrid As Grid)
    If (ImgGrid Is ImageGrid_1_Copy1) Or (ImgGrid Is ImageGrid_2_Copy1) _
    Or (ImgGrid Is ImageGrid_3_Copy1) Or (ImgGrid Is ImageGrid_4_Copy1) _
    Or (ImgGrid Is ImageGrid_5_Copy1) Then
        If Canvas.GetLeft(ImgGrid) >= 0 Then
            Canvas.SetLeft(ImgGrid, -675)
        End If
    End If
End Sub

' Place the ImageGrids on the right of the main
' grids to the appropriate location when one of
' those Grids exceeds a certain limit.
Private Sub RghtCheckCopy2s(ByVal ImgGrid As Grid)
    If (ImgGrid Is ImageGrid_1_Copy2) Or (ImgGrid Is ImageGrid_2_Copy2) _
    Or (ImgGrid Is ImageGrid_3_Copy2) Or (ImgGrid Is ImageGrid_4_Copy2) _
    Or (ImgGrid Is ImageGrid_5_Copy2) Then
        If Canvas.GetLeft(ImgGrid) >= 1350 Then
            Canvas.SetLeft(ImgGrid, 675)
        End If
    End If
End Sub

Remember earlier I said that I would explain why we needed the extra Grids? Well, the extra Grids are there to create the illusion that the last or first image is gradually showing up on the opposite end of the UserControl when the Grids are moving. E.g. if the user dragged the images to the right, the last image would seem to be slipping through on the left end.

Screenshot2.png

ImageIllusion.png

The ScaleUpGrid method, that is called by MoveImageGrids, increases the scale of the Grid in the middle of the UserControl.

' Increase the scale of the Grid in the middle.
Private Sub ScaleUpGrid(ByVal ImgGrid As Grid)
    Dim x As Double = Canvas.GetLeft(ImgGrid)
    Dim y As Double = Canvas.GetTop(ImgGrid)
    If (ImgGrid Is ImageGrid_1) And x = 270 Then
        GridScaleTr_1.ScaleX = scale
        GridScaleTr_1.ScaleY = scale
        GridScaleTr_1.CenterX = MidCanvasX + (ImgGridWidth / 2)
        GridScaleTr_1.CenterY = y + (ImgGridHeight / 2)
    ElseIf (ImgGrid Is ImageGrid_2) And x = 270 Then
        GridScaleTr_2.ScaleX = scale
        GridScaleTr_2.ScaleY = scale
        GridScaleTr_2.CenterX = MidCanvasX + (ImgGridWidth / 2)
        GridScaleTr_2.CenterY = y + (ImgGridHeight / 2)
    ElseIf (ImgGrid Is ImageGrid_3) And x = 270 Then
        GridScaleTr_3.ScaleX = scale
        GridScaleTr_3.ScaleY = scale
        GridScaleTr_3.CenterX = MidCanvasX + (ImgGridWidth / 2)
        GridScaleTr_3.CenterY = y + (ImgGridHeight / 2)
    ElseIf (ImgGrid Is ImageGrid_4) And x = 270 Then
        GridScaleTr_4.ScaleX = scale
        GridScaleTr_4.ScaleY = scale
        GridScaleTr_4.CenterX = MidCanvasX + (ImgGridWidth / 2)
        GridScaleTr_4.CenterY = y + (ImgGridHeight / 2)
    ElseIf (ImgGrid Is ImageGrid_5) And x = 270 Then
        GridScaleTr_5.ScaleX = scale
        GridScaleTr_5.ScaleY = scale
        GridScaleTr_5.CenterX = MidCanvasX + (ImgGridWidth / 2)
        GridScaleTr_5.CenterY = y + (ImgGridHeight / 2)
    End If
End Sub

In Expression Blend, if you select the UserControl and look at the Miscellaneous section of the Properties panel, you will notice five properties for setting the images of the UserControl.

DependencyProperties.png

The following is the code for creating one of the DependencyPropertys that makes this possible:

Public Property Image1Source() As ImageSource
    Get
      Return CType(GetValue(Image1Property), ImageSource)
    End Get
    Set(ByVal value As ImageSource)
        SetValue(Image1Property, value)
    End Set
End Property

Public Shared Image1Property As DependencyProperty = _
    DependencyProperty.Register("Image1Source", _
                                GetType(ImageSource), _
                                GetType(ImageCarousel), _
                                New FrameworkPropertyMetadata( _
                                New PropertyChangedCallback(AddressOf ChangeSource1)))

Private Shared Sub ChangeSource1(ByVal source As DependencyObject, _
                                 ByVal e As DependencyPropertyChangedEventArgs)
    CType(source, ImageCarousel).Image_1.Source = CType(e.NewValue, ImageSource)
    CType(source, ImageCarousel).Image_1_Copy1.Source = CType(e.NewValue, ImageSource)
    CType(source, ImageCarousel).Image_1_Copy2.Source = CType(e.NewValue, ImageSource)
End Sub

Conclusion

I hope you enjoyed reading the article and you picked up something useful. This was me just playing around with Expression Blend and WPF, hope you do the same. Cheers!

History

  • 15th March, 2011: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here