Tipp-Upload: VB.NET 0361: ThreeStateTreeview
von Spatzenkanonier
Über den Tipp
Dieser Tippvorschlag wird übernommen.
Der Vorschlag ist in den folgenden Kategorien zu finden:
- Algorithmen
- Grafik
- Steuerelemente
Dem Tippvorschlag wurden folgende Schlüsselwörter zugeordnet:
Checkstate,threestate,treeview
Der Vorschlag wurde erstellt am: 01.04.2009 05:16.
Die letzte Aktualisierung erfolgte am 22.03.2014 16:32.
Beschreibung
Die (optionalen) Checkboxen der Treenodes unterstützen nur eine 2-wertige Logik. Dabei ist es das naheliegendste, einen Treenode auf Checkstate.Indeterminate zu setzen, wenn seine Children nicht einheitlich gecheckt sind.
Es gibt sogar eine StateImageList-Property der Treeview, mit der man die für die Checkboxen zu verwendenden Images angeben kann.
Allerdings gibt es auch den Bug, daß es keinerlei Auswirkung auf die Anzeige hat, wenn man den StateImageIndex eines Treenodes setzt.
Hier also eine Treeview, die ihre Checkboxen selber zeichnet.
Schwierigkeitsgrad |
Verwendete API-Aufrufe: |
Download: |
' Dieser Source stammt von http://www.activevb.de ' und kann frei verwendet werden. Für eventuelle Schäden ' wird nicht gehaftet. ' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum. ' Ansonsten viel Spaß und Erfolg mit diesem Source! ' ' Beachten Sie, das vom Designer generierter Code hier ausgeblendet wird. ' In den Zip-Dateien ist er jedoch zu finden. ' ------- Anfang Projektgruppe ThreestateTreeview.sln ------- ' ------ Anfang Projektdatei ThreestateTreeview.vbproj ------ ' ---------- Anfang Datei frmThreestateTreeview.vb ---------- Public Class frmThreestateTreeview Private Sub frmThreestateTreeview_Load(ByVal sender As Object, ByVal e As EventArgs) _ Handles Me.Load TreeView1.ExpandAll() ThreeStateTreeview1.ExpandAll() End Sub Private Sub ThreeStateTreeview1_AfterCheckOrSelect(ByVal sender As Object, ByVal e As _ TreeViewEventArgs) Handles ThreeStateTreeview1.AfterCheck, _ ThreeStateTreeview1.AfterSelect Dim tv As ThreeStateTreeview = DirectCast(sender, ThreeStateTreeview) Dim nd As TreeNode = tv.SelectedNode If nd Is Nothing Then Return lbSelectedNode.Text = String.Concat(nd.Text, ": Checkstate.", tv.NodeCheckState(nd), _ ", Checked=", nd.Checked) End Sub Private Sub MenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _ btCheckSelected.Click, btUnCheckSelected.Click Dim nd As TreeNode = ThreeStateTreeview1.SelectedNode If nd Is Nothing Then Return Select Case True Case sender Is btCheckSelected nd.Checked = True Case sender Is btUnCheckSelected nd.Checked = False End Select End Sub End Class ' ----------- Ende Datei frmThreestateTreeview.vb ----------- ' ------------ Anfang Datei ThreeStateTreeview.vb ------------ Imports System.ComponentModel ''' <summary> ''' Treeview with 3-state Checkboxes. ''' Get the Treenodes 3-state Checkstate by calling ThreeStateTreeview.NodeCheckstate(Treenode) ''' </summary> <DesignerCategory("Code")> Public Class ThreeStateTreeview Inherits TreeView Private _indeterminateds As New List(Of TreeNode) Private _graphics As Graphics Private _imgIndeterminate As Image Private _skipCheckEvents As Boolean = False Public Sub New() InitializeComponent() MyBase.DrawMode = TreeViewDrawMode.OwnerDrawAll MyBase.CheckBoxes = True _imgIndeterminate = ImageList1.Images(2) MyBase.StateImageList = ImageList1 End Sub Protected Overrides Sub Dispose(ByVal disposing As Boolean) If disposing AndAlso _graphics IsNot Nothing Then _graphics.Dispose() _graphics = Nothing If components IsNot Nothing Then components.Dispose() End If MyBase.Dispose(disposing) End Sub Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs) MyBase.OnHandleCreated(e) _graphics = Me.CreateGraphics End Sub Protected Overrides Sub OnSizeChanged(ByVal e As EventArgs) MyBase.OnSizeChanged(e) If _graphics IsNot Nothing Then _graphics.Dispose() _graphics = Me.CreateGraphics End If End Sub Protected Overrides Sub OnBeforeCheck(ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) If _skipCheckEvents Then Return MyBase.OnBeforeCheck(e) End Sub Protected Overrides Sub OnAfterCheck(ByVal e As TreeViewEventArgs) ' Logic: All children of an (un)checked Node inherit its Checkstate ' Parents recompute their state: if all children of a parent have same state, that ' one will be taken over as parents state ' otherwise take Indeterminate ' changing any Treenodes .Checked-Property will raise another Before- and ' After-Check. Skip'em If _skipCheckEvents Then Return _skipCheckEvents = True Try Dim nd As TreeNode = e.Node ' newState is toggled from Checked to Unchecked, and from both Unchecked or ' Indeterminated to Checked Dim newState = If(NodeCheckState(nd) = CheckState.Checked, CheckState.Unchecked, _ CheckState.Checked) If (newState = CheckState.Checked) <> nd.Checked Then Return ' suppress redundant event Dim newStateToChildNodes As Action(Of TreeNode) = Sub(nd0) AssignState(nd0, newState) For Each ndChild As TreeNode In nd0.Nodes newStateToChildNodes(ndChild) Next End Sub newStateToChildNodes(nd) ' call recursive anonyme Sub ' Parents recompute their state nd = nd.Parent Do Until nd Is Nothing If newState <> CheckState.Indeterminate AndAlso nd.Nodes.Cast(Of _ TreeNode).Any(Function(nd2) NodeCheckState(nd2) <> newState) Then newState = CheckState.Indeterminate End If AssignState(nd, newState) nd = nd.Parent Loop MyBase.OnAfterCheck(e) Finally _skipCheckEvents = False End Try End Sub Private Sub AssignState(ByVal nd As TreeNode, ByVal state As CheckState) Dim ck As Boolean = state = checkstate.Checked Dim stateInvalid As Boolean = NodeCheckState(nd) <> state If stateInvalid Then NodeCheckState(nd) = state If nd.Checked <> ck Then nd.Checked = ck ' changing .Checked-Property raises Invalidating internally ElseIf stateInvalid Then ' in general: the less and small the invalidated area, the less flickering ' so avoid calling Invalidate() if possible - just call, if really needed. Me.Invalidate(GetCheckRect(nd)) End If End Sub Public Property NodeCheckState(ByVal nd As TreeNode) As System.Windows.Forms.CheckState Get Return DirectCast(Math.Max(0, nd.StateImageIndex), CheckState) End Get Private Set(value As System.Windows.Forms.CheckState) nd.StateImageIndex = value End Set End Property Protected Overrides Sub OnDrawNode(ByVal e As DrawTreeNodeEventArgs) ' here nothing is drawn. Only collect Indeterminated Nodes, to draw them later (in ' WndProc()) ' because drawing Treenodes properly (Text, Icon(s) Focus, Selection...) is very complicated If e.Node.StateImageIndex = checkstate.Indeterminate Then _indeterminateds.Add(e.Node) e.DrawDefault = True MyBase.OnDrawNode(e) End Sub Protected Overrides Sub WndProc(ByRef m As Message) Const WM_LBUTTONDBLCLK As Integer = &H203 If m.Msg = WM_LBUTTONDBLCLK Then ' fix the bug, when doubleclick on a StateImage Dim pt As Point = Me.PointToClient(Control.MousePosition) If Me.HitTest(pt).Location = TreeViewHitTestLocations.StateImage Then Return End If MyBase.WndProc(m) Const WM_Paint As Integer = 15 If m.Msg = WM_Paint Then ' at that point the built-in drawing is completed - and I quickly paint over the ' indeterminated Checkboxes For Each nd As TreeNode In _indeterminateds _graphics.DrawImage(_imgIndeterminate, GetCheckRect(nd).Location) Next _indeterminateds.Clear() End If End Sub Private Function GetCheckRect(ByVal nd As TreeNode) As Rectangle With nd.Bounds If Me.ImageList Is Nothing Then Return New Rectangle(.X - 16, .Y, 16, 16) Else Return New Rectangle(.X - 35, .Y, 16, 16) End If End With End Function ' since ThreeStateTreeview comes along with its own StateImageList, prevent assigning ' the StateImageList- Property from outside. Shadow and re-attribute original property <EditorBrowsable(EditorBrowsableState.Never)> <DesignerSerializationVisibility( _ DesignerSerializationVisibility.Hidden)> Public Shadows Property StateImageList() As _ ImageList Get Return MyBase.StateImageList End Get Set(ByVal value As ImageList) End Set End Property #Region "Designergenerated" Friend WithEvents ImageList1 As ImageList Private components As IContainer Private Sub InitializeComponent() Me.components = New Container Dim resources As ComponentResourceManager = New ComponentResourceManager(GetType( _ ThreeStateTreeview)) Me.ImageList1 = New ImageList(Me.components) Me.SuspendLayout() ' ' ImageList1 ' Me.ImageList1.ImageStream = CType(resources.GetObject("ImageList1.ImageStream"), _ ImageListStreamer) Me.ImageList1.TransparentColor = Color.Transparent Me.ImageList1.Images.SetKeyName(0, "UnChecked.ico") Me.ImageList1.Images.SetKeyName(1, "Checked.ico") Me.ImageList1.Images.SetKeyName(2, "Indeterminated.ico") Me.ResumeLayout(False) End Sub #End Region ' Designergenerated End Class ' ------------- Ende Datei ThreeStateTreeview.vb ------------- ' ------- Ende Projektdatei ThreestateTreeview.vbproj ------- ' -------- Ende Projektgruppe ThreestateTreeview.sln --------
Diskussion
Diese Funktion ermöglicht es, Fragen, die die Veröffentlichung des Tipps betreffen, zu klären, oder Anregungen und Verbesserungsvorschläge einzubringen. Nach der Veröffentlichung des Tipps werden diese Beiträge nicht weiter verlinkt. Allgemeine Fragen zum Inhalt sollten daher hier nicht geklärt werden.
Um eine Diskussion eröffnen zu können, müssen sie angemeldet sein.