0b44f5a723
- piccole migliorie.
250 lines
9.4 KiB
VB.net
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 |