Files
EgtWPFLib5/EgtTabControl.vb
Dario Sassi 0b44f5a723 EgtWPFLib5 1.8j1 :
- piccole migliorie.
2017-11-02 07:35:30 +00:00

250 lines
9.4 KiB
VB.net

' Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
'
' Step 1a) Using this custom control in a XAML file that exists in the current project.
' Add this XmlNamespace attribute to the root element of the markup file where it is
' to be used:
'
' xmlns:MyNamespace="clr-namespace:EgtWPFLib5"
'
'
' Step 1b) Using this custom control in a XAML file that exists in a different project.
' Add this XmlNamespace attribute to the root element of the markup file where it is
' to be used:
'
' xmlns:MyNamespace="clr-namespace:EgtWPFLib5;assembly=EgtWPFLib5"
'
' You will also need to add a project reference from the project where the XAML file lives
' to this project and Rebuild to avoid compilation errors:
'
' Right click on the target project in the Solution Explorer and
' "Add Reference"->"Projects"->[Browse to and select this project]
'
'
' Step 2)
' Go ahead and use your control in the XAML file. Note that Intellisense in the
' XML editor does not currently work on custom controls and its child elements.
'
' <MyNamespace:EgtTabControl/>
'
Imports System.Windows.Controls.Primitives
Imports System.Collections.Specialized
Imports System.Windows.Threading
' Extended TabControl which saves the displayed item so you don't get the performance hit of
' unloading and reloading the VisualTree when switching tabs
' Made some modifications so it reuses a TabItem's ContentPresenter when doing drag/drop operations
<TemplatePart(Name:="PART_ItemsHolder", Type:=GetType(Panel))> _
Public Class EgtTabControl
Inherits System.Windows.Controls.TabControl
' Holds all items, but only marks the current tab's item as visible
Private _itemsHolder As Panel = Nothing
' Temporaily holds deleted item in case this was a drag/drop operation
Private _deletedObject As Object = Nothing
Public Sub New()
MyBase.New()
' this is necessary so that we get the initial databound selected item
AddHandler Me.ItemContainerGenerator.StatusChanged, AddressOf ItemContainerGenerator_StatusChanged
End Sub
''' <summary>
''' if containers are done, generate the selected item
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub ItemContainerGenerator_StatusChanged(sender As Object, e As EventArgs)
If Me.ItemContainerGenerator.Status = GeneratorStatus.ContainersGenerated Then
RemoveHandler Me.ItemContainerGenerator.StatusChanged, AddressOf ItemContainerGenerator_StatusChanged
UpdateSelectedItem()
End If
End Sub
''' <summary>
''' get the ItemsHolder and generate any children
''' </summary>
Public Overrides Sub OnApplyTemplate()
MyBase.OnApplyTemplate()
_itemsHolder = TryCast(MyBase.GetTemplateChild("PART_ItemsHolder"), Panel)
UpdateSelectedItem()
End Sub
''' <summary>
''' when the items change we remove any generated panel children and add any new ones as necessary
''' </summary>
''' <param name="e"></param>
Protected Overrides Sub OnItemsChanged(e As NotifyCollectionChangedEventArgs)
MyBase.OnItemsChanged(e)
If _itemsHolder Is Nothing Then
Return
End If
Select Case e.Action
Case NotifyCollectionChangedAction.Reset
_itemsHolder.Children.Clear()
If MyBase.Items.Count > 0 Then
MyBase.SelectedItem = MyBase.Items(0)
UpdateSelectedItem()
End If
Exit Select
Case NotifyCollectionChangedAction.Add, NotifyCollectionChangedAction.Remove
' Search for recently deleted items caused by a Drag/Drop operation
If e.NewItems IsNot Nothing AndAlso _deletedObject IsNot Nothing Then
For Each item As Object In e.NewItems
If _deletedObject Is item Then
' If the new item is the same as the recently deleted one (i.e. a drag/drop event)
' then cancel the deletion and reuse the ContentPresenter so it doesn't have to be
' redrawn. We do need to link the presenter to the new item though (using the Tag)
Dim cp As ContentPresenter = FindChildContentPresenter(_deletedObject)
If cp IsNot Nothing Then
Dim index As Integer = _itemsHolder.Children.IndexOf(cp)
TryCast(_itemsHolder.Children(index), ContentPresenter).Tag = If((TypeOf item Is TabItem), item, (Me.ItemContainerGenerator.ContainerFromItem(item)))
End If
_deletedObject = Nothing
End If
Next
End If
If e.OldItems IsNot Nothing Then
For Each item As Object In e.OldItems
_deletedObject = item
' We want to run this at a slightly later priority in case this
' is a drag/drop operation so that we can reuse the template
Me.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, (Sub()
If _deletedObject IsNot Nothing Then
Dim cp As ContentPresenter = FindChildContentPresenter(_deletedObject)
If cp IsNot Nothing Then
Me._itemsHolder.Children.Remove(cp)
End If
End If
End Sub))
Next
End If
UpdateSelectedItem()
Exit Select
Case NotifyCollectionChangedAction.Replace
Throw New NotImplementedException("Replace not implemented yet")
End Select
End Sub
''' <summary>
''' update the visible child in the ItemsHolder
''' </summary>
''' <param name="e"></param>
Protected Overrides Sub OnSelectionChanged(e As SelectionChangedEventArgs)
MyBase.OnSelectionChanged(e)
UpdateSelectedItem()
End Sub
''' <summary>
''' generate a ContentPresenter for the selected item
''' </summary>
Private Sub UpdateSelectedItem()
If _itemsHolder Is Nothing Then
Return
End If
' generate a ContentPresenter if necessary
Dim item As TabItem = GetSelectedTabItem()
If item IsNot Nothing Then
CreateChildContentPresenter(item)
End If
' show the right child
For Each child As ContentPresenter In _itemsHolder.Children
child.Visibility = If((TryCast(child.Tag, TabItem).IsSelected), Visibility.Visible, Visibility.Collapsed)
Next
End Sub
''' <summary>
''' create the child ContentPresenter for the given item (could be data or a TabItem)
''' </summary>
''' <param name="item"></param>
''' <returns></returns>
Private Function CreateChildContentPresenter(item As Object) As ContentPresenter
If item Is Nothing Then
Return Nothing
End If
Dim cp As ContentPresenter = FindChildContentPresenter(item)
If cp IsNot Nothing Then
Return cp
End If
' the actual child to be added. cp.Tag is a reference to the TabItem
cp = New ContentPresenter() With {
.Content = If((TypeOf item Is TabItem), TryCast(item, TabItem).Content, item),
.ContentTemplate = Me.SelectedContentTemplate,
.ContentTemplateSelector = Me.SelectedContentTemplateSelector,
.ContentStringFormat = Me.SelectedContentStringFormat,
.Visibility = Visibility.Collapsed,
.Tag = If((TypeOf item Is TabItem), item, (Me.ItemContainerGenerator.ContainerFromItem(item)))
}
_itemsHolder.Children.Add(cp)
Return cp
End Function
''' <summary>
''' Find the CP for the given object. data could be a TabItem or a piece of data
''' </summary>
''' <param name="data"></param>
''' <returns></returns>
Private Function FindChildContentPresenter(data As Object) As ContentPresenter
If TypeOf data Is TabItem Then
data = TryCast(data, TabItem).Content
End If
If data Is Nothing Then
Return Nothing
End If
If _itemsHolder Is Nothing Then
Return Nothing
End If
For Each cp As ContentPresenter In _itemsHolder.Children
If cp.Content Is data Then
Return cp
End If
Next
Return Nothing
End Function
''' <summary>
''' copied from TabControl; wish it were protected in that class instead of private
''' </summary>
''' <returns></returns>
Protected Function GetSelectedTabItem() As TabItem
Dim selectedItem As Object = MyBase.SelectedItem
If selectedItem Is Nothing Then
Return Nothing
End If
If _deletedObject Is selectedItem Then
End If
Dim item As TabItem = TryCast(selectedItem, TabItem)
If item Is Nothing Then
item = TryCast(MyBase.ItemContainerGenerator.ContainerFromIndex(MyBase.SelectedIndex), TabItem)
End If
Return item
End Function
End Class