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.
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
:
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 ImageGrid
s. The following image shows the Grid
s that are to the left of the main Grid
s.
The Grid
controls to the left and to the right of the main Grid
s are actually copies of the main Grid
s. I will explain why we need them as you read on.
NB: The CarouselCanvas
' ClipToBounds
property is set to True.
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
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
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 Grid
s:
Private Sub MoveImageGrids()
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
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 Grid
s, x-position has reached the right edge of CarouselCanvas
and places the Grid
accordingly.
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 Grid
s located to the left and to the right of the main Grid
s respectively.
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
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 Grid
s? Well, the extra Grid
s 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 Grid
s 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.
The ScaleUpGrid
method, that is called by MoveImageGrids
, increases the scale of the Grid
in the middle of the UserControl
.
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
.
The following is the code for creating one of the DependencyProperty
s 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