views:

56

answers:

3

Because of the changes I have done to my post I have thinked to open another thread. In the new thread I have posted my (provvisory) solution. You can find it here

Hi! I have a problem with my TreeView in a WPF application (Framework 3.5 SP1). It's a TreeVIew with 2 Levels of Data. I expand / collapse the items of the first level in a particular way (with a single mouse-click on the TreeViewItem). Again when I expand a first-level TreeViewItem, I add some second-level TreeViewItems to the group (it's an important detail, infact if I don't add the items the problem doesn't occur). All works good until the TreeView loses focus. If, for example, I expand the TreeViewItem at the first position, adding at the same time one element to the second-level, then I click on a button (to let the TreeView lose the focus), and then I click again on the TreeViewItem at the third position to expand it, the TreeViewItem that results from the hit-test with the mouse position is not the "real" TreeViewItem (in this case the third), but a TreeViewItem which is in an higher position than the one clicked (in this case the second). I have tried to use the UpdateLayout method on the TreeView-LostFocus event, but without results. Probably I need a method that does the opposite: starting from the UI, refresh the object that contains the position of the TreeViewItems. Can you, please, help me? Thank you! Pileggi

This is the code:

   ' in this way I tried to put remedy at the problem, but it doesn't work.
    Private Sub tvArt_LostFocus(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles tvArt.LostFocus
        Me.tvArt.UpdateLayout()

        e.Handled = True
    End Sub

    ' here I expand / collapse the items of the first level of my TreeView
    Private Sub tvArt_PreviewMouseUp(ByVal sender As System.Object, ByVal e As MouseButtonEventArgs) Handles tvArt.PreviewMouseUp
        Dim p As Point = Nothing
        Dim tvi As TreeViewItem = getItemFromMousePosition(Of TreeViewItem)(p, e.OriginalSource, Me.tvArt)
        If tvi Is Nothing = False Then
            If tvi.HasItems Then
                Dim be As BindingExpression = BindingOperations.GetBindingExpression(tvi, TreeViewItem.ItemsSourceProperty)
                Dim ri As P_RicambiItem = DirectCast(be.DataItem, P_RicambiItem)
                If ri.isExpanded = False then
                    ' here I add items to the second level collection
                End If
                ri.isExpanded = Not ri.isExpanded
            End If
        End If

        e.Handled = True
    End Sub

    Private Function getItemFromMousePosition(Of childItem As DependencyObject)(ByRef p As Point, ByVal sender As UIElement, _
        ByVal _item As UIElement) As childItem

        p = sender.TranslatePoint(New Point(0, 0), _item)
        Dim obj As DependencyObject = DirectCast(_item.InputHitTest(p), DependencyObject)
        While obj Is Nothing = False AndAlso TypeOf obj Is childItem = False
            obj = VisualTreeHelper.GetParent(obj)
        End While
        Return DirectCast(obj, childItem)
    End Function
A: 

Your hit test code seems a little odd. You ignore the mouse position given by the MouseButtonEventArgs object, and then do a hit test in the TreeView against the upper-left corner of the control that was clicked. This will normally give you back the same control again, and I suspect your weird behavior is in the cases where it doesn't. Instead of doing TranslatePoint and InputHitTest, just use the sender directly. Your helper function reduces to:

Private Function getParentOfType(Of childItem As DependencyObject)(ByVal sender As UIElement) As childItem
    Dim obj As DependencyObject = sender
    While obj Is Nothing = False AndAlso TypeOf obj Is childItem = False
        obj = VisualTreeHelper.GetParent(obj)
    End While
    Return DirectCast(obj, childItem)
End Function

You can actually make it simpler again by taking advantage of the fact that MouseUp is a routed event and letting it find the TreeViewItem parent for you. Instead of adding the event handler to the TreeView itself, add a MouseUp handler to the TreeViewItem, and it will always be called with a sender of the TreeViewItem.

You should also set your binding on IsExpanded to be two-way if it is not already. That way you can update IsExpanded on the TreeViewItem and the value will be pushed to the binding source.

In XAML:

<TreeView.ItemContainerStyle>
    <Style TargetType="{x:Type TreeViewItem}">
        <Setter Property="IsExpanded" Value="{Binding isExpanded, Mode=TwoWay}" />
        <EventSetter Event="Mouse.MouseUp" Handler="tvi_MouseUp"/>
    </Style>
</TreeView.ItemContainerStyle>

Then in code:

Private Sub tvi_MouseUp(ByVal sender As System.Object, ByVal e As MouseButtonEventArgs)
    Dim tvi As TreeViewItem = DirectCast(sender, TreeViewItem)
    If tvi.HasItems Then
        tvi.IsExpanded = Not tvi.IsExpanded
    End If
    e.Handled = True
End Sub
Quartermeister
A: 

Thank you, you are very kind. But unfortunately the problem is the same with your solution. I omitted an important detail (sorry): when I expand a first-level TreeViewItem I add some second-level TreeviewItems. This causes the problem, if I don't add the items all works good. I have edited my question, to make it more comprehensible. Maybe now the solution is more easy (I hope). Thanks, Pileggi

pileggi
A: 

Because of the changes I have done to my post I have thinked to open another thread. In the new thread I have posted my (provvisory) solution. You can find it here

pileggi