65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 2D 图像轮播

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (31投票s)

2011年3月15日

CPOL

3分钟阅读

viewsIcon

127123

downloadIcon

8783

WPF 中的 2D 轮播用户控件

ScreenshotMain.png

引言

这篇文章描述了我如何创建 2D 图片轮播图。创建它的原因...我只是想看看我是否能做到。我实际上是在看一个黑莓 Playbook 的 视频,我想知道我是否可以创建一个类似于视频中轮播图功能的控件。Playbook 的虽然更优雅,我可能会羡慕不已。我的轮播图 UserControl,恰如其分地命名为 ImageCarousel,只处理图片,而且只有五张图片。虽然这可能很可悲,但你希望从这篇文章中获得一些有用的东西,所以如果你仍然感兴趣,请继续阅读。

要求

要运行从上面的下载链接提供的项目,你需要以下之一

  • Visual Studio 2010
  • Expression Blend

注意:如果你正在使用 Visual Studio 的 Express 版本,请确保使用 Visual Basic Express 打开解决方案。

ImageCarousel

工作原理

要移动图片,请按住鼠标左键并向左或向右拖动。当你松开鼠标左键时,图片将按预定方向移动。

Screenshot2.png

Screenshot3.png

设计和布局

我在 Expression Blend 中把所有东西放在一起。下图显示了一些元素在 UserControl 中的布局

Layout1.png

在上面的图像中,有五个主要的 Grid 控件可见,另外还有五个 Grid 控件位于主 ImageGrid 的左侧和右侧。下图显示了位于主 Grid 左侧的 Grid

Layout2.png

位于主 Grid 左侧和右侧的 Grid 控件实际上是主 Grid 的副本。我稍后会解释为什么我们需要它们

注意:CarouselCanvasClipToBounds 属性设置为 True

ClipToBounds.png

代码

ImageCarouselInitialized 事件处理程序中,我们执行以下操作

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

在上面的方法中,调用了一个名为 ChangeImagesOpacity 的方法。此方法将 UserControl 中所有图片的透明度更改为 60%,除了中间的 Grid 控件之外。

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

当用户按下鼠标左键,开始移动图片的过程时,将调用 UserControlCarouselCanvas MouseLeftButtonDown 事件处理程序。

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

当用户释放鼠标左键时,会发生以下情况

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

如果用户在水平方向移动鼠标,则启动 CarouselTimer,在其 Tick 事件期间,我们执行以下操作

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

上面调用的 MoveImageGrids 方法会导致 Grid 的最终移动

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

注意:将一个 Grid 移动到下一个最终位置需要 900 毫秒。

RightCheckOriginals 方法检查主 Grid 中最后一个 Grid 的 x 坐标是否到达了 CarouselCanvas 的右边缘,并相应地放置 Grid

' 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

RghtCheckCopy1sRghtCheckCopy2s 方法也执行类似的操作,但这次是针对分别位于主 Grid 左侧和右侧的 Grid

' 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

还记得我之前说过我会解释为什么我们需要额外的 Grid 吗?好吧,额外的 Grid 存在是为了创建一种错觉,即当 Grid 移动时,最后一张或第一张图片会逐渐出现在 UserControl 的另一端。例如,如果用户将图片向右拖动,最后一张图片似乎会从左端滑出。

Screenshot2.png

ImageIllusion.png

ScaleUpGrid 方法(由 MoveImageGrids 调用)会增加 UserControl 中间 Grid 的缩放比例。

' 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

在 Expression Blend 中,如果你选择 UserControl 并查看属性面板的杂项部分,你将注意到有五个属性用于设置 UserControl 的图片。

DependencyProperties.png

以下是用于创建其中一个 DependencyProperty 以使之成为可能的代码

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

结论

希望你喜欢阅读这篇文章,并获得了一些有用的东西。这只是我使用 Expression Blend 和 WPF 的一些尝试,希望你也能这样做。干杯!

历史

  • 2011年3月15日:首次发布
© . All rights reserved.