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 off 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's 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 ImageGrid's 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 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
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"