Tree Node Проверено поведение TreeView в Compact Framework 3.5, работающем в Windows Mobile 6.5

Я обновлял существующее приложение.NET для Windows Mobile, чтобы использовать версию 3.5 компактной платформы и работать на Windows Mobile 6.5. У меня есть форма с TreeView. Свойство TreeView.Checkboxes имеет значение true, поэтому каждый узел имеет флажок. Это не доставляет проблем во всех предыдущих версиях Windows Mobile.

Тем не менее, в версии 6.5, когда вы нажимаете на флажок, он появляется, чтобы проверить, а затем снимите флажок мгновенно. Но он вызывает событие AfterCheck только один раз. Единственный способ, которым я могу получить чек, - это дважды щелкнуть по нему (что является неправильным поведением).

Кто-нибудь видел такое поведение? Кто-нибудь знает обходной путь для этого?

Я включил простую форму теста. Извлеките эту форму из приложения для смарт-устройств Visual Studio 2008, ориентированного на Windows Mobile 6, чтобы понять, что я имею в виду.

Public Class frmTree
Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "
Public Sub New()
    MyBase.new()
    ' This call is required by the Windows Form Designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.

End Sub

'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing AndAlso components IsNot Nothing Then
        components.Dispose()
    End If
    MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
Friend WithEvents TreeView1 As System.Windows.Forms.TreeView
Private mainMenu1 As System.Windows.Forms.MainMenu

'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.  
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
    Dim TreeNode1 As System.Windows.Forms.TreeNode = New System.Windows.Forms.TreeNode("Node0")
    Dim TreeNode2 As System.Windows.Forms.TreeNode = New System.Windows.Forms.TreeNode("Node2")
    Dim TreeNode3 As System.Windows.Forms.TreeNode = New System.Windows.Forms.TreeNode("Node3")
    Dim TreeNode4 As System.Windows.Forms.TreeNode = New System.Windows.Forms.TreeNode("Node4")
    Dim TreeNode5 As System.Windows.Forms.TreeNode = New System.Windows.Forms.TreeNode("Node1")
    Dim TreeNode6 As System.Windows.Forms.TreeNode = New System.Windows.Forms.TreeNode("Node5")
    Dim TreeNode7 As System.Windows.Forms.TreeNode = New System.Windows.Forms.TreeNode("Node6")
    Dim TreeNode8 As System.Windows.Forms.TreeNode = New System.Windows.Forms.TreeNode("Node7")
    Me.mainMenu1 = New System.Windows.Forms.MainMenu
    Me.TreeView1 = New System.Windows.Forms.TreeView
    Me.SuspendLayout()
    '
    'TreeView1
    '
    Me.TreeView1.CheckBoxes = True
    Me.TreeView1.Location = New System.Drawing.Point(37, 41)
    Me.TreeView1.Name = "TreeView1"
    TreeNode2.Text = "Node2"
    TreeNode3.Text = "Node3"
    TreeNode4.Text = "Node4"
    TreeNode1.Nodes.AddRange(New System.Windows.Forms.TreeNode() {TreeNode2, TreeNode3, TreeNode4})
    TreeNode1.Text = "Node0"
    TreeNode6.Text = "Node5"
    TreeNode7.Text = "Node6"
    TreeNode8.Text = "Node7"
    TreeNode5.Nodes.AddRange(New System.Windows.Forms.TreeNode() {TreeNode6, TreeNode7, TreeNode8})
    TreeNode5.Text = "Node1"
    Me.TreeView1.Nodes.AddRange(New System.Windows.Forms.TreeNode() {TreeNode1, TreeNode5})
    Me.TreeView1.Size = New System.Drawing.Size(171, 179)
    Me.TreeView1.TabIndex = 0
    '
    'frmTree
    '
    Me.AutoScaleDimensions = New System.Drawing.SizeF(96.0!, 96.0!)
    Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi
    Me.AutoScroll = True
    Me.ClientSize = New System.Drawing.Size(240, 268)
    Me.Controls.Add(Me.TreeView1)
    Me.Menu = Me.mainMenu1
    Me.Name = "frmTree"
    Me.Text = "frmTree"
    Me.ResumeLayout(False)

End Sub
#End Region

End Class

4 ответа

Решение

Я смог найти обходной путь для этого, но для этого мне пришлось пойти на крайние меры. Похоже, что наблюдаемое нами поведение происходит из-за того, что событие Click запускается как из событий MouseDown, так и из событий MouseUp (вместо того, чтобы просто поднять мышь вверх, как это было бы в Windows или в предыдущих версиях).

Чтобы показать это, вы можете начать, нажав на флажок, оставив палец на экране и перетащив флажок. Он будет проверен из события MouseDown и останется отмеченным, потому что событие MouseUp не запускается, когда вы поднимаете палец из другой позиции. То же самое работает для снятия флажка и перетаскивания.

Чтобы предотвратить поведение двойного щелчка, вы должны подавить одно из событий MouseDown или MouseUp. Я закончил тем, что создал элемент управления, который унаследовал TreeView и использовал WndProcHooker, чтобы перехватить метод OnMouseDown и пометить его как обработанный, чтобы событие MouseDown фактически никогда не запускалось. Я подумал, что это имеет смысл (вы должны держать палец над флажком, когда поднимаете его).

Вот ссылка на статью MSDN о WndProcHooker. Ниже приведен мой код для моего класса TreeViewInherit. Несмотря на то, что это работает, я все еще удивляюсь, что я должен идти на все, чтобы это заработало. Кроме того, я не с нетерпением жду дня, когда MS исправит это и, таким образом, нарушит мой обходной путь в процессе.

    Imports System.Windows.Forms
Imports Microsoft.WindowsCE.Forms

Public Class TreeViewInherit
    Inherits System.Windows.Forms.TreeView

#Region " Variables "
    Private mBlnHandleMouseDown As Boolean
#End Region

#Region " Methods "

    Public Sub New()
    MyBase.New()

    'Set the Handle Mouse Down based on the OS. if 6.5 and up, then handle it.
    mBlnHandleMouseDown = (System.Environment.OSVersion.Version.Major >= 5 AndAlso System.Environment.OSVersion.Version.Minor >= 2 AndAlso System.Environment.OSVersion.Version.Build >= 21234)
    If mBlnHandleMouseDown Then
        WndProcHooker.HookWndProc(Me, New WndProcHooker.WndProcCallback(AddressOf Me.WM_LButtonDown_Handler), Win32.WM_LBUTTONDOWN)
    End If
    End Sub

    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
    'Don't Call the Base to prevent the extra event from firing
    If Not mBlnHandleMouseDown Then
        MyBase.OnMouseDown(e)
    End If
    End Sub

#End Region

#Region " Events "
    Private Function WM_LButtonDown_Handler(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer, ByRef handled As Boolean) As Integer
    Try
        Me.Capture = False

        Dim lastCursorCoordinates As Win32.POINT = Win32.LParamToPoint(lParam)
        If Me.ClientRectangle.Contains(lastCursorCoordinates.X, lastCursorCoordinates.Y) Then
        OnMouseDown(New MouseEventArgs(MouseButtons.Left, 1, lastCursorCoordinates.X, lastCursorCoordinates.Y, 0))
        End If
        handled = True
        Return 0
    Catch ex As Exception
        Throw
    End Try
    End Function
#End Region

End Class

Удачи!

Чтобы решить проблему отсутствия запуска события AfterCheck, я обнаружил, что могу щелкнуть узел и затем использовать его для вызова AfterCheck, это работает, но потом я обнаружил, что AfterCheck вызывался до того, как состояние флажка было изменено, поэтому вместо этого поднял мое собственное событие и обработал его соответствующим образом.


Imports System.Windows.Forms
Imports Microsoft.WindowsCE.Forms

Public Class TreeViewInherit
    Inherits System.Windows.Forms.TreeView

    'Occurs when the user clicks a TreeNode with the mouse.
    Public Event MouseDownOveride(ByVal node As TreeNode)

#Region " Variables "
    Private mBlnHandleMouseDown As Boolean
#End Region

#Region " Methods "



    Public Sub New()
        MyBase.New()

        'Set the Handle Mouse Down based on the OS. if 6.5 and up, then handle it.
        mBlnHandleMouseDown = (System.Environment.OSVersion.Version.Major >= 5 AndAlso System.Environment.OSVersion.Version.Minor >= 2 AndAlso System.Environment.OSVersion.Version.Build >= 21234)
        If mBlnHandleMouseDown Then
            WndProcHooker.HookWndProc(Me, New WndProcHooker.WndProcCallback(AddressOf Me.WM_LButtonDown_Handler), Win32.WM_LBUTTONDOWN)
        End If
    End Sub

    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
        'Don't Call the Base to prevent the extra event from firing
        If Not mBlnHandleMouseDown Then
            MyBase.OnMouseDown(e)
        End If
    End Sub


    Private Function FindTreeNodeFromHandle(ByVal tnc As TreeNodeCollection, ByVal handle As IntPtr) As TreeNode
        For Each tn As TreeNode In tnc
            If tn.Handle = handle Then
                Return tn
            End If
            ' we couldn't have clicked on a child of this node if this node
            ' is not expanded!
            If tn.IsExpanded Then
                Dim tn2 As TreeNode = FindTreeNodeFromHandle(tn.Nodes, handle)
                If tn2 IsNot Nothing Then
                    Return tn2
                End If
            End If
        Next
        Return Nothing
    End Function


#End Region

#Region " Events "
    Private Function WM_LButtonDown_Handler(ByVal hwnd As IntPtr, ByVal msg As UInteger, ByVal wParam As UInteger, ByVal lParam As Integer, ByRef handled As Boolean) As Integer
        Try
            Me.Capture = False

            Dim lastCursorCoordinates As Win32.POINT = Win32.LParamToWin32POINT(lParam)

            If Me.ClientRectangle.Contains(lastCursorCoordinates.X, lastCursorCoordinates.Y) Then
                OnMouseDown(New MouseEventArgs(MouseButtons.Left, 1, lastCursorCoordinates.X, lastCursorCoordinates.Y, 0))
            End If
            handled = True


            Dim msgPos As Point = Win32.LParamToPoint(CInt(Win32.GetMessagePos()))
            msgPos = Me.PointToClient(msgPos)

            ' check to see if the click was on an item
            Dim hti As New Win32.TVHITTESTINFO()
            hti.pt.X = msgPos.X
            hti.pt.Y = msgPos.Y
            Dim hitem As Integer = Win32.SendMessage(Me.Handle, Win32.TVM_HITTEST, 0, hti)
            Dim htMask As UInteger = (Win32.TVHT_ONITEMICON Or Win32.TVHT_ONITEMLABEL Or Win32.TVHT_ONITEMINDENT Or Win32.TVHT_ONITEMBUTTON Or Win32.TVHT_ONITEMRIGHT Or Win32.TVHT_ONITEMSTATEICON)

            If hti.flags = Win32.TVHT_ONITEMSTATEICON Then
                RaiseEvent MouseDownOveride(FindTreeNodeFromHandle(Me.Nodes, hti.hItem))
            End If


            Return 0
        Catch ex As Exception
            Throw
        End Try
    End Function
#End Region

End Class

Работает нормально

Мы попросили у MS ответа, и они дали нам обходной путь, который включает захват клика и установку или снятие флажка по желанию. Я еще не устал, но если он сделает свою работу, я также опубликую это.

Вы можете использовать эту альтернативу: http://blogs.southworks.net/mconverti/2009/06/22/optional-checkable-tree-view-control-for-net-compact-framework-35/

Гидрослайд: Чтобы ответить на ваш первый вопрос, да, я вижу это поведение. На ваш второй вопрос: нет, я еще не нашел решение.

Я разработал приложение в VS2K8 для CF 3.5 SP1. Как и ваше приложение, мое используется на нескольких поколениях устройств без проблем. Однако я столкнулся с этой проблемой TreeView в Windows Mobile 6.5.

На всех моих телефонах WM 6.5 (HTC Pure (ST6356), HTC Tilt 2 и HTC Imagio), а также в эмуляторе WM 6.5, функция флажка TreeView не работает. Нажатие на флажок почти всегда приводит к тому, что установленный флажок сбрасывается только через миллисекунды (и наоборот). Единственный способ, которым я нашел надежно заставить галочку "застрять", - это дважды нажать на галочку. Звучит знакомо, Гидрослайд?

В дополнение к этому странному поведению, внешний вид этих TreeViews изменен на более новых телефонах HTC, чтобы включать увеличенное пустое пространство между узлами, предположительно, чтобы облегчить манипулирование пальцем или большим пальцем. Сравните: @ http://ftp.agconnections.com/treeviews.png. (Удалите символ @, предшествующий ссылке. Это необходимо, потому что Stackru сначала побуждает меня публиковать "подробные и конкретные вопросы", а затем запрещает мне создавать сообщение, содержащее более одной гиперссылки. Хорошо.) Интересно, что эмулятор WM 6.5 отображает древовидная структура без каких-либо дополнительных пробелов, но все еще демонстрирует проблему проверки / снятия отметки.

Я создал голый проект, содержащий только стандартное древовидное представление и несколько узлов, и его поведение идентично моему производственному проекту: http://ftp.agconnections.com/TreeViewTest.zip. Я установил точку останова в событии AfterCheck и обнаружил - как это сделал Hydroslide - что он срабатывает только один раз при однократном нажатии.

Я удивлен, что никто за пределами нас двоих не жаловался на такое поведение.

Клиенты, ожидающие решения этой проблемы, начинают накапливаться, и некоторые из них не совсем понимают. Любые предложения с благодарностью.

Джейсон Перселл

Другие вопросы по тегам