2

Hello I have the following setup for a treeview:

<local:BuddyManager x:Key="bmBuddyManager" />

<CollectionViewSource x:Key="cvsBuddyManager"
                      Source="{Binding Source={StaticResource bmBuddyManager}, Path=Buddies}">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="State" />
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

<DataTemplate x:Key="dtBuddyTemplate" DataType="{x:Type local:Buddy}">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Nick}" FontSize="12" FontWeight="Bold" />
        <TextBlock Text="{Binding GameHost}" FontSize="12" FontWeight="Bold"
                   Foreground="Purple" Margin="10,0,0,0" />
    </StackPanel>
</DataTemplate>

<HierarchicalDataTemplate x:Key="hdtBuddyCategoryTemplate" ItemsSource="{Binding Path=Items}"
                          ItemTemplate="{StaticResource dtBuddyTemplate}">
    <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Foreground="Gold" FontSize="15" />
</HierarchicalDataTemplate>

        <TreeView ItemsSource="{Binding Source={StaticResource cvsBuddyManager}, Path=Groups}"
                  ItemTemplate="{StaticResource hdtBuddyCategoryTemplate}"
                  ContextMenuOpening="tvBuddies_ContextMenuOpening"
                  ContextMenuClosing="tvBuddies_ContextMenuClosing"
                  Background="Transparent" Margin="2,0,3,3">
        </TreeView>

Code Behind:

<System.Runtime.InteropServices.ComVisible(False)> Public Enum BuddyState
    Online
    Offline
    Blocked
End Enum

<System.Runtime.InteropServices.ComVisible(False)> Public Class Buddy
    Implements INotifyPropertyChanged

    Private _Nick As String
    Private _IsInGame As Boolean
    Private _GameHost As String
    Private _State As BuddyState

    Sub New(ByVal xwisNick As String)
        _Nick = xwisNick
        _State = BuddyState.Offline
    End Sub

    Sub New(ByVal xwisNick As String, ByVal state As BuddyState)
        _Nick = xwisNick
        _State = state
    End Sub

    Public Property Nick() As String
        Get
            Return _Nick
        End Get
        Set(ByVal value As String)
            _Nick = value
        End Set
    End Property

    Public Property IsInGame() As Boolean
        Get
            Return _IsInGame
        End Get
        Set(ByVal value As Boolean)
            _IsInGame = value

            If _IsInGame = False Then
                GameHost = Nothing
            End If

            OnPropertyChanged("IsInGame")
        End Set
    End Property

    Public Property GameHost() As String
        Get
            Return _GameHost
        End Get
        Set(ByVal value As String)
            _GameHost = value
            OnPropertyChanged("GameHost")
        End Set
    End Property

    Public Property State() As BuddyState
        Get
            Return _State
        End Get
        Set(ByVal value As BuddyState)
            _State = value

            If value = BuddyState.Online Then
                If _IsInGame Then
                    _IsInGame = False
                    _GameHost = Nothing
                End If
            End If

            OnPropertyChanged("State")
        End Set
    End Property

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    ' Create the OnPropertyChanged method to raise the event
    Protected Sub OnPropertyChanged(ByVal name As String)
        Try
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
        Catch
        End Try
    End Sub
End Class

Public Class BuddyManager
    Implements INotifyPropertyChanged

    Private ocBuddies As ObservableCollection(Of Buddy) = New ObservableCollection(Of Buddy)

    Public ReadOnly Property Buddies As ObservableCollection(Of Buddy)
        Get
            Return ocBuddies
        End Get
    End Property

    Public BuddyCheck As List(Of Buddy) = New List(Of Buddy)
    Public IsCheckingForBuddies As Boolean = False


    Public Function IsBuddy(ByVal XwisNick As String) As Boolean
        Dim nick As String = XwisNick.ToLower

        For Each b As Buddy In ocBuddies
            If b.Nick = nick Then
                Return True
            End If
        Next

        Return False
    End Function

    Public Function IsInGame(ByVal XwisNick As String) As String
        Dim nick As String = XwisNick.ToLower

        For Each b As Buddy In ocBuddies
            If b.Nick = nick Then
                If b.IsInGame Then
                    Return b.GameHost
                Else
                    Return Nothing
                End If
            End If
        Next

        Return Nothing
    End Function


    Public Function AddBuddy(ByVal XwisNick As String) As Boolean
        Dim nick As String = XwisNick.ToLower

        For Each b As Buddy In ocBuddies
            If b.Nick = nick Then
                Return False
            End If
        Next

        ocBuddies.Add(New Buddy(nick))

        OnPropertyChanged("Buddies")

        Return True
    End Function

    Public Function RemoveBuddy(ByVal XwisNick As String) As Boolean
        Dim nick As String = XwisNick.ToLower

        For i As Integer = 0 To ocBuddies.Count - 1
            If ocBuddies(i).Nick = nick Then
                ocBuddies.RemoveAt(i)

                OnPropertyChanged("Buddies")

                Return True
            End If
        Next

        Return False
    End Function

    Public Function BlockBuddy(ByVal XwisNick As String) As Boolean
        Dim nick As String = XwisNick.ToLower

        For i As Integer = 0 To ocBuddies.Count - 1
            If ocBuddies(i).Nick = nick Then
                ocBuddies(i).State = BuddyState.Blocked

                OnPropertyChanged("Buddies")

                Return True
            End If
        Next

        ocBuddies.Add(New Buddy(nick, BuddyState.Blocked))

        OnPropertyChanged("Buddies")

        Return True
    End Function

    Public Function UnblockBuddy(ByVal XwisNick As String) As Boolean
        Dim nick As String = XwisNick.ToLower

        For i As Integer = 0 To ocBuddies.Count - 1
            If ocBuddies(i).Nick = nick Then
                ocBuddies(i).State = BuddyState.Offline

                OnPropertyChanged("Buddies")

                Return True
            End If
        Next

        Return False
    End Function


    Public Sub UpdateOnlineStatus(ByVal XwisNick As String, ByVal online As Boolean)
        Dim nick As String = XwisNick.ToLower

        For i As Integer = 0 To ocBuddies.Count - 1
            If ocBuddies(i).Nick = nick Then
                If online Then
                    ocBuddies(i).State = BuddyState.Online
                Else
                    ocBuddies(i).State = BuddyState.Offline
                End If
                OnPropertyChanged("Buddies")

                Exit For
            End If
        Next

        RaiseEvent BuddyOnlineStatusChanged(nick, online)
    End Sub

    Public Sub UpdateInGameStatus(ByVal XwisNick As String, ByVal gamehost As String)
        Dim nick As String = XwisNick.ToLower

        For i As Integer = 0 To ocBuddies.Count - 1
            If ocBuddies(i).Nick = nick Then
                ocBuddies(i).IsInGame = True
                ocBuddies(i).GameHost = gamehost

                OnPropertyChanged("Buddies")

                RaiseEvent BuddyGameStatusChanged(nick, gamehost)

                Exit For
            End If
        Next
    End Sub


    Public Sub FillBuddyCheck()
        BuddyCheck = ocBuddies.Where(Function(bud) bud.State <> BuddyState.Blocked).ToList
    End Sub

    Public Function GetBuddies() As IEnumerable(Of Buddy)
        Return ocBuddies.Where(Function(bud) bud.State <> BuddyState.Blocked)
    End Function

    Public Sub Sort()
        ocBuddies.OrderBy(Function(bud) bud.Nick)
        OnPropertyChanged("Buddies")
    End Sub

    Public Function Count() As Integer
        Return GetBuddies.Count
    End Function


    Public Event BuddyOnlineStatusChanged(ByVal nick As String, ByVal online As Boolean)
    Public Event BuddyGameStatusChanged(ByVal nick As String, ByVal gamehost As String)


    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    ' Create the OnPropertyChanged method to raise the event
    Protected Sub OnPropertyChanged(ByVal name As String)
        Try
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
        Catch

        End Try
    End Sub
End Class

How I interact with the classes:

    Public Function GetBuddyManager() As BuddyManager
        Try
            Return DirectCast(FindResource("bmBuddyManager"), BuddyManager)
        Catch ex As Exception
            MessageBox.Show("Error getting buddy manager object: " & ex.ToString())
            Application.Current.Shutdown()

            Return Nothing
        End Try
    End Function

GetBuddyManager().UpdateOnlineStatus(GetBuddyManager().BuddyCheck(0).Nick, True)

The Binding and grouping work well, the only issue is when I set a particular 'buddy' to online or blocked the child nodes do not move or change.

I am trying to get this to work like an MSN Treeview where people go offline and online.

Any help is appreciated, I have been working on this problem for a month or so with heavy research and no luck.

Thank you for your time.

tcables
  • 1,231
  • 5
  • 16
  • 36
  • 1
    3 questions: 1st: is there a reason, you are using a treeview and not a ListBox with `ListBox.GroupStyle` set? 2nd:have you tried adding collectionchanged event handlers to either the CollectionViewSource.View or CollectionViewSource.View.Groups[0] to see if they are in fact being changed? 3rd: Does the UI update with an explicit call to CollectionViewSource.View.Refresh? – Markus Hütter Apr 27 '11 at 19:08
  • 3rd: yes and my labels update (since I have the status as a second label in a stackpanel on each item). 2nd: CollectionViewSource.View does not have any events that I can see. 1st: I will try that. thanks! – tcables Apr 27 '11 at 19:11

1 Answers1

0

Looks like you need an OnPropertyChanged event in the State property for VisualColor. The UI does not get a notification that the visual color should be updated. It knows that the State value has changed, but all that means is whatever is bound to the State property gets updated.

I would suggest putting those colors in XAML and writing a DataTrigger on your item, that evaluates the state and changes the color to suit.

Next as for not moving from one status category to another, when you set the state, have you looked at the CollectionViewSource at runtime to see how it is sorting? Are you calling a refresh on the CVS view anywhere?

CodeWarrior
  • 7,388
  • 7
  • 51
  • 78
  • I'm removing that from this example. Since that is totally not what my question or the problem is about, but thank you for at least looking at my issue. – tcables Apr 28 '11 at 03:49
  • You are asking why your items are not changing position in the treeview when you change a property though right? If you change the State property, perform OnPropertyChanged, and refresh the CollectionView, what happens? Nothing? Personally, I have not tried implementing like this. I picked up MVVM before delving into treeviews and CollectionViewSources. I have however gotten this to work with a viewmodel, and it did not take a lot. In that instance I had several observable collections that held objects in certain "States" and I would move items back and forth. Worked like a charm. – CodeWarrior Apr 28 '11 at 04:10
  • "change the State property, perform OnPropertyChanged, and refresh the CollectionView" I have tried that a few days ago and it does work - but causes the tree to flicker, close up, and loose the selected item. Which gets really annoying when doing things with the selected item such as a context menu or click-delete. – tcables Apr 28 '11 at 16:04
  • Your best bet may be to go with either several observable collections that manually move objects between them. As I understand it, refresh causes CVS to reingest the collection of objects, thus the UI repaints them from scratch. You could really just write a single method which would perform moves based on three arguments, item to move, source and destination. – CodeWarrior Apr 28 '11 at 16:10
  • oh well, that is the conclusion I came up with a month ago... I was hoping to do it cleaner with WPF grouping/binding but I guess we can mark this as another WPF limitation - that should be expanded by Microsoft. Thanks! – tcables Apr 28 '11 at 20:48