Files
OmagPHOTO/Utility/VirtualizingTilePanel.vb
T
Emmanuele Sassi 53c8d6d934 OmagPHOTO :
- Aggiunta lista lastre che scorre e gestisce migliaia di lastre.
- Miglioramenti e correzioni varie.
2018-01-15 10:14:32 +00:00

296 lines
11 KiB
VB.net

Imports System.Windows.Controls.Primitives
Imports System.Windows.Controls
Imports System.Windows
Imports System.Windows.Media
Imports System
Imports System.Diagnostics
Imports System.Collections.Specialized
Imports System.Windows.Data
Public Class VirtualizingTilePanel
Inherits VirtualizingPanel
Implements IScrollInfo
Public Sub New()
' For use in the IScrollInfo implementation
Me.RenderTransform = _trans
End Sub
Public Shared ReadOnly ChildSizeProperty As DependencyProperty = DependencyProperty.RegisterAttached("ChildSize", GetType(Double), GetType(VirtualizingTilePanel), New FrameworkPropertyMetadata(200.0, FrameworkPropertyMetadataOptions.AffectsMeasure Or FrameworkPropertyMetadataOptions.AffectsArrange))
Public Property ChildSize As Double
Get
Return CDbl(GetValue(ChildSizeProperty))
End Get
Set(ByVal value As Double)
SetValue(ChildSizeProperty, value)
End Set
End Property
Protected Overrides Function MeasureOverride(ByVal availableSize As Size) As Size
UpdateScrollInfo(availableSize)
Dim firstVisibleItemIndex, lastVisibleItemIndex As Integer
GetVisibleRange(firstVisibleItemIndex, lastVisibleItemIndex)
Dim children As UIElementCollection = Me.InternalChildren
Dim generator As IItemContainerGenerator = Me.ItemContainerGenerator
Dim startPos As GeneratorPosition = generator.GeneratorPositionFromIndex(firstVisibleItemIndex)
Dim childIndex As Integer = If((startPos.Offset = 0), startPos.Index, startPos.Index + 1)
Using generator.StartAt(startPos, GeneratorDirection.Forward, True)
Dim itemIndex As Integer = firstVisibleItemIndex
While itemIndex <= lastVisibleItemIndex
Dim newlyRealized As Boolean
Dim child As UIElement = TryCast(generator.GenerateNext(newlyRealized), UIElement)
If newlyRealized Then
If childIndex >= children.Count Then
MyBase.AddInternalChild(child)
Else
MyBase.InsertInternalChild(childIndex, child)
End If
generator.PrepareItemContainer(child)
Else
Debug.Assert(child Is children(childIndex), "Wrong child was generated")
End If
child.Measure(GetChildSize())
System.Threading.Interlocked.Increment(itemIndex)
System.Threading.Interlocked.Increment(childIndex)
End While
End Using
CleanUpItems(firstVisibleItemIndex, lastVisibleItemIndex)
Return availableSize
End Function
Protected Overrides Function ArrangeOverride(ByVal finalSize As Size) As Size
Dim generator As IItemContainerGenerator = Me.ItemContainerGenerator
UpdateScrollInfo(finalSize)
For i As Integer = 0 To Me.Children.Count - 1
Dim child As UIElement = Me.Children(i)
Dim itemIndex As Integer = generator.IndexFromGeneratorPosition(New GeneratorPosition(i, 0))
ArrangeChild(itemIndex, child, finalSize)
Next
Return finalSize
End Function
Private Sub CleanUpItems(ByVal minDesiredGenerated As Integer, ByVal maxDesiredGenerated As Integer)
Dim children As UIElementCollection = Me.InternalChildren
Dim generator As IItemContainerGenerator = Me.ItemContainerGenerator
Dim i As Integer = children.Count - 1
While i >= 0
Dim childGeneratorPos As GeneratorPosition = New GeneratorPosition(i, 0)
Dim itemIndex As Integer = generator.IndexFromGeneratorPosition(childGeneratorPos)
If itemIndex < minDesiredGenerated OrElse itemIndex > maxDesiredGenerated Then
generator.Remove(childGeneratorPos, 1)
RemoveInternalChildRange(i, 1)
End If
i -= 1
End While
End Sub
Protected Overrides Sub OnItemsChanged(ByVal sender As Object, ByVal args As ItemsChangedEventArgs)
Select Case args.Action
Case NotifyCollectionChangedAction.Remove, NotifyCollectionChangedAction.Replace, NotifyCollectionChangedAction.Move
RemoveInternalChildRange(args.Position.Index, args.ItemUICount)
End Select
End Sub
Private Function CalculateExtent(ByVal availableSize As Size, ByVal itemCount As Integer) As Size
Dim childrenPerRow As Integer = CalculateChildrenPerRow(availableSize)
Return New Size(childrenPerRow * Me.ChildSize, Me.ChildSize * Math.Ceiling(CDbl(itemCount) / childrenPerRow))
End Function
Private Sub GetVisibleRange(ByRef firstVisibleItemIndex As Integer, ByRef lastVisibleItemIndex As Integer)
Dim childrenPerRow As Integer = CalculateChildrenPerRow(_extent)
firstVisibleItemIndex = CInt(Math.Floor(_offset.Y / Me.ChildSize)) * childrenPerRow
lastVisibleItemIndex = CInt(Math.Ceiling((_offset.Y + _viewport.Height) / Me.ChildSize)) * childrenPerRow - 1
Dim itemsControl As ItemsControl = itemsControl.GetItemsOwner(Me)
Dim itemCount As Integer = If(itemsControl.HasItems, itemsControl.Items.Count, 0)
If lastVisibleItemIndex >= itemCount Then lastVisibleItemIndex = itemCount - 1
End Sub
Private Function GetChildSize() As Size
Return New Size(Me.ChildSize, Me.ChildSize)
End Function
Private Sub ArrangeChild(ByVal itemIndex As Integer, ByVal child As UIElement, ByVal finalSize As Size)
Dim childrenPerRow As Integer = CalculateChildrenPerRow(finalSize)
Dim row As Integer = itemIndex \ childrenPerRow ' IMPORTANTE L'operatore è di divisione intera, quindi \ NON / che invece arrotonda all'intero pari più vicino!!!!
Dim column As Integer = itemIndex Mod childrenPerRow
child.Arrange(New Rect(column * Me.ChildSize, row * Me.ChildSize, Me.ChildSize, Me.ChildSize))
End Sub
Private Function CalculateChildrenPerRow(ByVal availableSize As Size) As Integer
Dim childrenPerRow As Integer
If availableSize.Width = Double.PositiveInfinity Then childrenPerRow = Me.Children.Count Else childrenPerRow = Math.Max(1, CInt(Math.Floor(availableSize.Width / Me.ChildSize)))
Return childrenPerRow
End Function
Private Sub UpdateScrollInfo(ByVal availableSize As Size)
Dim itemsControl As ItemsControl = itemsControl.GetItemsOwner(Me)
Dim itemCount As Integer = If(itemsControl.HasItems, itemsControl.Items.Count, 0)
Dim extent As Size = CalculateExtent(availableSize, itemCount)
If extent <> _extent Then
_extent = extent
If _owner IsNot Nothing Then _owner.InvalidateScrollInfo()
End If
If availableSize <> _viewport Then
_viewport = availableSize
If _owner IsNot Nothing Then _owner.InvalidateScrollInfo()
End If
End Sub
Public Property ScrollOwner As ScrollViewer Implements IScrollInfo.ScrollOwner
Get
Return _owner
End Get
Set(ByVal value As ScrollViewer)
_owner = value
End Set
End Property
Public Property CanHorizontallyScroll As Boolean Implements IScrollInfo.CanHorizontallyScroll
Get
Return _canHScroll
End Get
Set(ByVal value As Boolean)
_canHScroll = value
End Set
End Property
Public Property CanVerticallyScroll As Boolean Implements IScrollInfo.CanVerticallyScroll
Get
Return _canVScroll
End Get
Set(ByVal value As Boolean)
_canVScroll = value
End Set
End Property
Public ReadOnly Property HorizontalOffset As Double Implements IScrollInfo.HorizontalOffset
Get
Return _offset.X
End Get
End Property
Public ReadOnly Property VerticalOffset As Double Implements IScrollInfo.VerticalOffset
Get
Return _offset.Y
End Get
End Property
Public ReadOnly Property ExtentHeight As Double Implements IScrollInfo.ExtentHeight
Get
Return _extent.Height
End Get
End Property
Public ReadOnly Property ExtentWidth As Double Implements IScrollInfo.ExtentWidth
Get
Return _extent.Width
End Get
End Property
Public ReadOnly Property ViewportHeight As Double Implements IScrollInfo.ViewportHeight
Get
Return _viewport.Height
End Get
End Property
Public ReadOnly Property ViewportWidth As Double Implements IScrollInfo.ViewportWidth
Get
Return _viewport.Width
End Get
End Property
Public Sub LineUp() Implements IScrollInfo.LineUp
SetVerticalOffset(Me.VerticalOffset - 10)
End Sub
Public Sub LineDown() Implements IScrollInfo.LineDown
SetVerticalOffset(Me.VerticalOffset + 10)
End Sub
Public Sub PageUp() Implements IScrollInfo.PageUp
SetVerticalOffset(Me.VerticalOffset - _viewport.Height)
End Sub
Public Sub PageDown() Implements IScrollInfo.PageDown
SetVerticalOffset(Me.VerticalOffset + _viewport.Height)
End Sub
Public Sub MouseWheelUp() Implements IScrollInfo.MouseWheelUp
SetVerticalOffset(Me.VerticalOffset - 50)
End Sub
Public Sub MouseWheelDown() Implements IScrollInfo.MouseWheelDown
SetVerticalOffset(Me.VerticalOffset + 50)
End Sub
Public Sub LineLeft() Implements IScrollInfo.LineLeft
Throw New InvalidOperationException()
End Sub
Public Sub LineRight() Implements IScrollInfo.LineRight
Throw New InvalidOperationException()
End Sub
Public Function MakeVisible(ByVal visual As Visual, ByVal rectangle As Rect) As Rect Implements IScrollInfo.MakeVisible
Return New Rect()
End Function
Public Sub MouseWheelLeft() Implements IScrollInfo.MouseWheelLeft
Throw New InvalidOperationException()
End Sub
Public Sub MouseWheelRight() Implements IScrollInfo.MouseWheelRight
Throw New InvalidOperationException()
End Sub
Public Sub PageLeft() Implements IScrollInfo.PageLeft
Throw New InvalidOperationException()
End Sub
Public Sub PageRight() Implements IScrollInfo.PageRight
Throw New InvalidOperationException()
End Sub
Public Sub SetHorizontalOffset(ByVal offset As Double) Implements IScrollInfo.SetHorizontalOffset
Throw New InvalidOperationException()
End Sub
Public Sub SetVerticalOffset(ByVal offset As Double) Implements IScrollInfo.SetVerticalOffset
If offset < 0 OrElse _viewport.Height >= _extent.Height Then
offset = 0
Else
If offset + _viewport.Height >= _extent.Height Then
offset = _extent.Height - _viewport.Height
End If
End If
_offset.Y = offset
If _owner IsNot Nothing Then _owner.InvalidateScrollInfo()
_trans.Y = -offset
InvalidateMeasure()
End Sub
Private _trans As TranslateTransform = New TranslateTransform()
Private _owner As ScrollViewer
Private _canHScroll As Boolean = False
Private _canVScroll As Boolean = False
Private _extent As Size = New Size(0, 0)
Private _viewport As Size = New Size(0, 0)
Private _offset As Point
End Class