Click here to Skip to main content
11,708,312 members (75,636 online)
Click here to Skip to main content

WPF 2D Image Carousel

, 18 Mar 2011 CPOL 70K 6.4K 74
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Meshack Musundi
Software Developer
Kenya Kenya
Meshack is an avid programmer with a bias towards WPF and VB.NET. He has about 5 years of programming experience initially starting off with Java before shifting to .NET, thanks to the allure of WPF. He currently resides in a small town in Kiambu county, Kenya.

Awards;
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • Best VB.NET article of January 2015
  • Best VB.NET article of August 2013
  • Best VB.NET article of February 2013
  • Best VB.NET article of October 2012
  • Best VB.NET article of July 2012
  • Best VB.NET article of February 2012
  • Best VB.NET article of January 2012
  • Best VB.NET article of November 2011
  • Best VB.NET article of June 2011
  • Best VB.NET article of May 2011
  • Best VB.NET article of March 2011
  • Best VB.NET article of February 2011
  • Best VB.NET article of January 2011
  • Best VB.NET article of December 2010
  • Best VB.NET article of November 2010

You may also be interested in...

Comments and Discussions

 
QuestionMy Vote Of 5 Pin
Manoj K Bhoir27-Jun-12 21:30
memberManoj K Bhoir27-Jun-12 21:30 
GeneralMy vote of 5 Pin
manoj kumar choubey19-Mar-12 20:00
membermanoj kumar choubey19-Mar-12 20:00 
GeneralRe: My vote of 5 Pin
Meshack Musundi20-Apr-12 2:31
mvpMeshack Musundi20-Apr-12 2:31 
GeneralCongratulations! Pin
Oslec5-May-11 19:24
memberOslec5-May-11 19:24 
GeneralRe: Congratulations! Pin
Meshack Musundi6-May-11 0:49
memberMeshack Musundi6-May-11 0:49 
GeneralMy vote of 5 Pin
Oslec5-May-11 19:22
memberOslec5-May-11 19:22 
GeneralRe: My vote of 5 Pin
Meshack Musundi6-May-11 0:46
memberMeshack Musundi6-May-11 0:46 
GeneralMy vote of 5 Pin
Marcelo Ricardo de Oliveira21-Apr-11 4:59
mvpMarcelo Ricardo de Oliveira21-Apr-11 4:59 
GeneralRe: My vote of 5 Pin
Meshack Musundi21-Apr-11 5:42
memberMeshack Musundi21-Apr-11 5:42 
GeneralRe: My vote of 5 Pin
Marcelo Ricardo de Oliveira2-May-11 15:30
mvpMarcelo Ricardo de Oliveira2-May-11 15:30 
GeneralRe: My vote of 5 Pin
Meshack Musundi2-May-11 20:05
memberMeshack Musundi2-May-11 20:05 
GeneralMy +5 Pin
Toniyo Jackson10-Apr-11 23:37
memberToniyo Jackson10-Apr-11 23:37 
GeneralRe: My +5 Pin
Meshack Musundi11-Apr-11 0:21
memberMeshack Musundi11-Apr-11 0:21 
GeneralMy vote of 5 Pin
Sunasara Imdadhusen8-Apr-11 20:46
memberSunasara Imdadhusen8-Apr-11 20:46 
GeneralRe: My vote of 5 Pin
Meshack Musundi9-Apr-11 3:23
memberMeshack Musundi9-Apr-11 3:23 
GeneralI just found a control called PathListBox Pin
Patrick Miron23-Mar-11 10:25
memberPatrick Miron23-Mar-11 10:25 
GeneralRe: I just found a control called PathListBox Pin
Meshack Musundi23-Mar-11 20:26
memberMeshack Musundi23-Mar-11 20:26 
GeneralI think it should move with the mouse not after release Pin
Sacha Barber19-Mar-11 0:11
mvpSacha Barber19-Mar-11 0:11 
GeneralRe: I think it should move with the mouse not after release Pin
Meshack Musundi19-Mar-11 1:22
memberMeshack Musundi19-Mar-11 1:22 
GeneralRe: I think it should move with the mouse not after release Pin
Meshack Musundi19-Mar-11 1:47
memberMeshack Musundi19-Mar-11 1:47 
GeneralMy vote of 5 Pin
maq_rohit16-Mar-11 3:58
membermaq_rohit16-Mar-11 3:58 
GeneralRe: My vote of 5 Pin
Meshack Musundi16-Mar-11 4:26
memberMeshack Musundi16-Mar-11 4:26 
GeneralRe: My vote of 5 Pin
Patrick Miron16-Mar-11 12:32
memberPatrick Miron16-Mar-11 12:32 
GeneralRe: My vote of 5 Pin
Meshack Musundi16-Mar-11 21:07
memberMeshack Musundi16-Mar-11 21:07 
GeneralRe: My vote of 5 Pin
maq_rohit18-Mar-11 6:33
membermaq_rohit18-Mar-11 6:33 
AnswerRe: My vote of 5 Pin
thatraja26-Jan-12 21:18
mvpthatraja26-Jan-12 21:18 
GeneralThanks! Pin
Nishant Sivakumar16-Mar-11 3:12
mvpNishant Sivakumar16-Mar-11 3:12 
GeneralRe: Thanks! Pin
Meshack Musundi16-Mar-11 4:16
memberMeshack Musundi16-Mar-11 4:16 
GeneralMy vote of 5 Pin
Nishant Sivakumar16-Mar-11 3:09
mvpNishant Sivakumar16-Mar-11 3:09 
GeneralRe: My vote of 5 Pin
Meshack Musundi16-Mar-11 4:10
memberMeshack Musundi16-Mar-11 4:10 
GeneralRe: My vote of 5 Pin
Nishant Sivakumar16-Mar-11 4:11
mvpNishant Sivakumar16-Mar-11 4:11 
GeneralMy vote of 5 Pin
karukutimothy15-Mar-11 21:34
memberkarukutimothy15-Mar-11 21:34 
GeneralRe: My vote of 5 Pin
Meshack Musundi15-Mar-11 22:02
memberMeshack Musundi15-Mar-11 22:02 
GeneralMy vote of 5 Pin
Patrick Kalkman15-Mar-11 20:32
memberPatrick Kalkman15-Mar-11 20:32 
GeneralRe: My vote of 5 Pin
Meshack Musundi15-Mar-11 21:34
memberMeshack Musundi15-Mar-11 21:34 
GeneralMy vote of 5 Pin
Patrick Miron15-Mar-11 4:37
memberPatrick Miron15-Mar-11 4:37 
GeneralRe: My vote of 5 Pin
Meshack Musundi15-Mar-11 6:08
memberMeshack Musundi15-Mar-11 6:08 
GeneralMy vote of 5 Pin
Jack_Sparow15-Mar-11 4:27
memberJack_Sparow15-Mar-11 4:27 
GeneralRe: My vote of 5 Pin
Meshack Musundi15-Mar-11 6:04
memberMeshack Musundi15-Mar-11 6:04 
GeneralMy vote of 5 Pin
Slacker00715-Mar-11 4:23
memberSlacker00715-Mar-11 4:23 
GeneralRe: My vote of 5 Pin
Meshack Musundi15-Mar-11 6:04
memberMeshack Musundi15-Mar-11 6:04 
GeneralMy vote of 5 Pin
JF201515-Mar-11 2:36
memberJF201515-Mar-11 2:36 
GeneralRe: My vote of 5 Pin
Meshack Musundi15-Mar-11 3:28
memberMeshack Musundi15-Mar-11 3:28 
GeneralMy vote of 5 Pin
Pravin Patil, Mumbai15-Mar-11 2:22
memberPravin Patil, Mumbai15-Mar-11 2:22 
GeneralRe: My vote of 5 Pin
Meshack Musundi15-Mar-11 3:25
memberMeshack Musundi15-Mar-11 3:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150819.1 | Last Updated 18 Mar 2011
Article Copyright 2011 by Meshack Musundi
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid