Enable and disable button - wpf

I have a texbox in binding with a property.
<TextBox Name="txtPrice" Grid.Row="0" Grid.Column="2" MaxLength="8" TabIndex="1"
Text="{Binding Price, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True,
StringFormat= '\{0:#,###.##\}', ConverterCulture=fr-FR}" TextWrapping="Wrap"/>
Private Property _Price As Double
Public Property Price As Double
Get
Return Price
End Get
Set(value As Double)
_Price = Double.Parse(value)
OnPropertyChanged("Price")
End Set
End Property
When I type some chars or the textbox is empty, the button Cmd_Insert must not be enabled, but doesn't work.
Why ? (see Function CanCmd_Insert())
Public ReadOnly Property Cmd_Insert As ICommand
Get
If _Cm_Insert Is Nothing Then
_Cm_Insert = New RelayCommand(AddressOf Cmd_InsertExe, AddressOf CanCmd_Insert)
End If
Return _Cm_Insert
End Get
End Property
Private Sub Cmd_InsertExe()
UPDATE_Price()
End Sub
Private Function CanCmd_Insert() As Boolean
If IsNumeric(Price) = False Then
Return False
Else
Return True
End If
End Function

I added TargetNullValue='' and changed your property to nullable. Refer the below code.
<StackPanel>
<TextBox Name="txtPrice" Grid.Row="0" Grid.Column="2" MaxLength="8" TabIndex="1"
Text="{Binding Price, UpdateSourceTrigger=PropertyChanged, TargetNullValue='',
StringFormat= '\{0:#,###.##\}'}" TextWrapping="Wrap" />
<Button Content="Update" Command="{Binding Cmd_Insert }"></Button>
</StackPanel>
Imports GalaSoft.MvvmLight.CommandWpf
Imports System.ComponentModel
Public Class ViewModel
Implements INotifyPropertyChanged
Private Property _Price As Double?
Public Property Price As Double?
Get
Return _Price
End Get
Set(value As Double?)
_Price = value
OnPropertyChanged("Price")
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub OnPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Private Property _Cm_Insert As ICommand
Public ReadOnly Property Cmd_Insert As ICommand
Get
If _Cm_Insert Is Nothing Then
_Cm_Insert = New RelayCommand(AddressOf Cmd_InsertExe, AddressOf CanCmd_Insert)
End If
Return _Cm_Insert
End Get
End Property
Private Sub Cmd_InsertExe()
End Sub
Private Function CanCmd_Insert() As Boolean
If IsNumeric(Price) = False Then
Return False
Else
Return True
End If
End Function
End Class

Related

Attempting to Databind an ObservableCollection to a ListView the MVVM Way

I'm new to WPF/MVVM and have been attempting to bind an observable collection class to a list view, no code behind, just MVVM.
The XAML:
<Window.DataContext>
<local:acLengthCalcVM/>
</Window.DataContext>
<StackPanel>
<GroupBox Header="Section(s) Selected" Margin="3,10,3,10">
<ListView x:Name="lvSections" Margin="0,6,0,2" Height="150" ItemsSource="{Binding SectionsSelected}">
<ListView.View>
<GridView>
<GridViewColumn Header="#" Width="30"/>
<GridViewColumn Header="Reference Tag #" Width="100"/>
<GridViewColumn Header="Section Type" Width="150"/>
<GridViewColumn Header="Section Length" Width="100"/>
</GridView>
</ListView.View>
</ListView>
</GroupBox>
</StackPanel>
The View Model:
Public Class acLengthCalcVM
Implements INotifyPropertyChanged
Dim _sectionsSelected As ObservableCollection(Of acLengthCalcModel) = New ObservableCollection(Of acLengthCalcModel)
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property SectionsSelected As ObservableCollection(Of acLengthCalcModel)
Get
Return _sectionsSelected
End Get
Set(value As ObservableCollection(Of acLengthCalcModel))
_sectionsSelected = value
NotifyPropertyChanged("SectionsSelected")
End Set
End Property
Private Sub NotifyPropertyChanged(Optional propertyName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
The Model:
Public Class acLengthCalcModel
Inherits ObservableCollection(Of acLengthCalcData)
Public Sub New()
Add(New acLengthCalcData(1, "n/a", "VTU Frame", 8))
Add(New acLengthCalcData(2, "1.1.01", "Air Conveyor Straight", 118))
End Sub
End Class
Public Class acLengthCalcData
' Constructor for this Class
Public Sub New(serialNum As Integer, refTagNum As String, sectionType As String, sectionLength As Double)
_refTagNum = refTagNum
_sectionLength = sectionLength
_sectionType = sectionType
_serialNum = serialNum
End Sub
' Reference Tag #
Private _refTagNum As String
Public Property RefTagNum As String
Get
Return _refTagNum
End Get
Set(value As String)
_refTagNum = value
End Set
End Property
' Section Length
Private _sectionLength As Double
Public Property SectionLength As Double
Get
Return _sectionLength
End Get
Set(value As Double)
_sectionLength = value
End Set
End Property
' Section Type
Private _sectionType As String
Public Property SectionType As String
Get
Return _sectionType
End Get
Set(value As String)
_sectionType = value
End Set
End Property
' Serial #
Private _serialNum As Integer
Public Property SerialNum As Integer
Get
Return _serialNum
End Get
Set(value As Integer)
_serialNum = value
End Set
End Property
End Class
When I run the code, the list view is created with only its headers, none of the data I initialized in the model constructor shows up.
Any pointers? Thanks.
You should create and return an instance of a acLengthCalcModel in your view model class:
Public Class acLengthCalcVM
Implements INotifyPropertyChanged
Dim _sectionsSelected = New acLengthCalcModel
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property SectionsSelected As acLengthCalcModel
Get
Return _sectionsSelected
End Get
Set(value As acLengthCalcModel)
_sectionsSelected = value
NotifyPropertyChanged("SectionsSelected")
End Set
End Property
Private Sub NotifyPropertyChanged(Optional propertyName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
You also need to bind each of the columns to a property of the acLengthCalcData class using the DisplayMemberBinding property:
<ListView x:Name="lvSections" Margin="0,6,0,2" Height="150" ItemsSource="{Binding SectionsSelected}">
<ListView.View>
<GridView>
<GridViewColumn Header="#" DisplayMemberBinding="{Binding SerialNum}" Width="30"/>
<GridViewColumn Header="Reference Tag #" DisplayMemberBinding="{Binding RefTagNum}" Width="100"/>
<GridViewColumn Header="Section Type" DisplayMemberBinding="{Binding SectionType}" Width="150"/>
<GridViewColumn Header="Section Length" DisplayMemberBinding="{Binding SectionLength}" Width="100"/>
</GridView>
</ListView.View>
</ListView>

wpf image button in combobox list

I'm trying to get an image displayed in the list of a combobox based on a bound boolean value. When the image is clicked the boolean value, and thus the image should change.
Here's the xaml:
<ComboBox Name="Combo2" Margin="20,79,20,0" ItemsSource="{Binding}" VerticalAlignment="Top" Height="20">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Name="MyBoolImage" Height="12" Width="12" MouseLeftButtonUp="Image_MouseLeftButtonUp"/>
<TextBlock Text="{Binding name}" Margin="5,0,0,0" Width="100" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding LightOn}" Value="False">
<Setter TargetName="MyBoolImage" Property="Source" Value="/Images/Exit.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding LightOn}" Value="True">
<Setter TargetName="MyBoolImage" Property="Source" Value="/Images/Cut.png"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
And the data class:
Class ComboData
Private _LightOn As Boolean
Public Property LightOn As Boolean
Get
Return _LightOn
End Get
Set(value As Boolean)
_LightOn = value
End Set
End Property
Private _name As String
Public Property name As String
Get
Return _name
End Get
Set(value As String)
_name = value
End Set
End Property
Sub New(name_ As String, Light_On As Boolean)
_LightOn = Light_On
_name = name_
End Sub
End Class
loading some test data:
Dim x As New List(Of ComboData)
x.Add(New ComboData("test1a", True))
x.Add(New ComboData("test2a", False))
x.Add(New ComboData("test3a", True))
x.Add(New ComboData("test4a", True))
x.Add(New ComboData("test5a", False))
x.Add(New ComboData("test6a", True))
Combo2.ItemsSource = x
and finally the click event where the magic isn't happening...
Private Sub Image_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs)
Dim SelectedComboData As ComboData = TryCast(CType(sender, Image).DataContext, ComboData)
SelectedComboData.LightOn = Not SelectedComboData.LightOn
e.Handled = True
End Sub
The LightOn value is changed as supposed to, even in the "x" (the list of combodata), the value is changed. But the displayed image is not changing.
What am I missing?
Thanks!
Class comboData should implement INotifyPropertyChanged in order to notify UI about its changes..
Class ComboData
Implements INotifyPropertyChanged
Private _LightOn As Boolean
Public Property LightOn As Boolean
Get
Return _LightOn
End Get
Set(value As Boolean)
_LightOn = value
OnPropertyChanged("LightOn")
End Set
End Property
Private _name As String
Public Property name As String
Get
Return _name
End Get
Set(value As String)
_name = value
OnPropertyChanged("name")
End Set
End Property
Sub New(name_ As String, Light_On As Boolean)
_LightOn = Light_On
_name = name_
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class

WPF ComboBox Binding Selected Value

I am trying to do a binding as with following data formats:
Public Structure ItemBase
Property ID As String
Property Description As String
End Structure
Namespace Classes
Public Class StockEntityClass
Implements INotifyPropertyChanged
#Region "Property Variables"
Property ID As String
Property Namee As String
Property Units As String
Property ContactID As String
Property SetCount As Integer
Property VatOnMargin As Boolean
Property Vat As Double
Property Code As String
Property _ContactNamee As String
#End Region
Public Sub New()
_IDValue = Now.ToString
_NameeValue = ""
_UnitsValue = "Pcs"
_ContactIDValue = ""
_SetCountValue = 0
_VatOnMarginValue = False
_VatValue = 14.5
_CodeValue = ""
_ContactNamee = ""
End Sub
End Class
End Namespace
In my wpf xaml window which is DataContext to Stock Entity variable, I have a combo box that is bound to BindingList(of ItemBase) and the user will select an Item and that ItemID as to get assosiated to StockEntity.ContactID.
Following is the xaml code:
<ComboBox Name="VendorsComboBox" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" DisplayMemberPath="Description" SelectedItem="{Binding Path=CustomerID}"/>
Following is Binding Code:
Dim Stock As Classes.StockEntityClass
VendorsComboBox.ItemsSource = Contacts.DBAccessFunctions.Get_ContactsByType_BaseList(DataSource, "Vendor")
StockEntityStack.DataContext = Stock
Where StockEntityStack contains the UI part which is datacontext bind to stock variable.
Could you please tell me how to write the xaml databound to it.
Try something like that:
MainWindow (XAML file):
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ComboBox ItemsSource="{Binding MySource}"
SelectedValue="{Binding ContactID}"
DisplayMemberPath="Description"
SelectedValuePath="ID"
Width="180" Height="25"
/>
<StackPanel Orientation="Horizontal" Margin="10" >
<TextBlock Text="Selected ID:" />
<TextBlock Text="{Binding ContactID}" />
</StackPanel>
</StackPanel>
</Window>
MainWindow (code-behind file):
Class MainWindow
Public Sub New()
InitializeComponent()
Me.DataContext = New MainViewModel()
End Sub
End Class
MainViewModel file:
Imports System.ComponentModel
Public Class MainViewModel
Implements INotifyPropertyChanged
#Region "Fields"
Private _selectedContactID As String = String.Empty
#End Region
#Region "Property Variables"
Property MySource As List(Of ItemBase) = New List(Of ItemBase)
Public Property ContactID As String
Get
Return _selectedContactID
End Get
Set(ByVal value As String)
_selectedContactID = value
OnPropertyChanged("ContactID")
End Set
End Property
#End Region
Public Sub New()
MySource.Add(New ItemBase(1, "test1"))
MySource.Add(New ItemBase(2, "test2"))
MySource.Add(New ItemBase(3, "test3"))
MySource.Add(New ItemBase(4, "test4"))
MySource.Add(New ItemBase(5, "test5"))
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Public Structure ItemBase
Public Sub New( _
ByVal _id As String,
ByVal _description As String
)
ID = _id
Description = _description
End Sub
Property ID As String
Property Description As String
End Structure

Async Update ItemsControl Inside of a Datagrid (or suggest a better way)

I have a datagrid bound to an observable collection. Each item inside of the grid can have multiple detail lines, which are stored in an observable collection property inside of the main object.
The line details are being fetched from a linked server with a slow connection, so I'd like to put a background worker in to update the line details OC, but I get an error saying that the control can't be updated outside of the thread that made it.
What's the best way to do this. The 2 second lag is a bit much.
datagrid xaml:
<DataGrid AutoGenerateColumns="False" Name="dgROList" ItemsSource="{Binding ElementName=MainWindow, Path=cROInfo}" CanUserDeleteRows="True" CanUserReorderColumns="False" GridLinesVisibility="Horizontal" Margin="0,112,0,0" Grid.ColumnSpan="2" AlternatingRowBackground="#FFFFE776">
<DataGrid.Columns>
<DataGridTextColumn Header="RO Number" Width="Auto" Binding="{Binding RONum}" />
<DataGridTemplateColumn x:Name="roDetails" Header="RO Details" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ItemsControl Name="LineDetails" ItemsSource="{Binding LineInfo}" Width="Auto">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ItemsPresenter />
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Line}" />
<Label Content="{Binding Status}" />
<Label Content="{Binding PaidAmount}" />
<Label Content="{Binding SDate}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="RO Details" Width="*" Binding="{Binding RODetails}" />-->
</DataGrid.Columns>
</DataGrid>
OC class:
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Public Class ocROInformation
Implements INotifyPropertyChanged
Private _RONum As String
Private _LineInfo As ObservableCollection(Of ocROLineInformation)
Private _Changed As Boolean
Private _RONumChanged As Boolean
Private _RODetailsChanged As Boolean
Private WithEvents bgworker As BackgroundWorker
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Public Sub New(ronum As String)
_RONum = ronum
_LineInfo = New ObservableCollection(Of ocROLineInformation)
bgworker = New BackgroundWorker
bgworker.RunWorkerAsync()
' GetData() ' If I call this directly it works, just lags out while the query runs for a little bit.
End Sub
Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
If Not Propertyname.Contains("Changed") Then
Changed = True
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
End Sub
Sub GetData() Handles bgworker.DoWork
Dim DCodes As String = DealerCodes
Dim rSelect As New ADODB.Recordset
Dim sSql As String = "SELECT DISTINCT * FROM IGlobal WHERE RMAJBC = " & _RONum"
Dim line As Integer
Dim status As String = ""
Dim sdate As Date
Dim paidamount As Double
Dim tdate As String
With rSelect
.Open(sSql, MyCn, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockReadOnly)
If .EOF Then
status = "Never Received"
End If
Do While Not .EOF
line = .Fields!LineNum.Value
status = NZ(.Fields!Stat6.Value, "")
paidamount = NZ(.Fields!PaidAmount.Value, 0)
tdate = NZ(.Fields!SDate.Value, "")
If Not tdate = "" And Not tdate = "0" Then
sdate = Date.ParseExact(tdate, "yyyyMMdd", System.Globalization.DateTimeFormatInfo.InvariantInfo)
_LineInfo.Add(New ocROLineInformation(line, status, sdate, paidamount))
Else
_LineInfo.Add(New ocROLineInformation(line, status))
End If
.MoveNext()
Loop
.Close()
End With
OnPropertyChanged("RODetails")
End Sub
Public Property Changed() As Boolean
Get
Return _Changed
End Get
Set(ByVal value As Boolean)
If _Changed <> value Then
_Changed = value
OnPropertyChanged("Changed")
End If
End Set
End Property
Public Property RONum() As String
Get
Return _RONum
End Get
Set(value As String)
If _RONum <> value Then
_RONum = value
RONumChanged = True
OnPropertyChanged("RONum")
GetData()
End If
End Set
End Property
Public ReadOnly Property RODetails As String
Get
Dim output As String = ""
For Each l As ocROLineInformation In _LineInfo
output &= l.Print & " "
Next
Return output '"This is a test: " & _RONum
End Get
End Property
Public ReadOnly Property LineInfo As ObservableCollection(Of ocROLineInformation)
Get
Return _LineInfo
End Get
End Property
Public Property RODetailsChanged As Boolean
Get
Return _RODetailsChanged
End Get
Set(value As Boolean)
If _RODetailsChanged <> value Then
_RODetailsChanged = value
OnPropertyChanged("RODetailsChanged")
End If
End Set
End Property
Public Property RONumChanged() As Boolean
Get
Return _RONumChanged
End Get
Set(value As Boolean)
If _RONumChanged <> value Then
_RONumChanged = value
OnPropertyChanged("RONumChanged")
End If
End Set
End Property
End Class
Public Class ocROLineInformation
Implements INotifyPropertyChanged
Private _Line As Integer
Private _Status As String
Private _SDate As Date
Private _PaidAmount As Double
Private _Changed As Boolean
Private _LineChanged As Boolean
Private _StatusChanged As Boolean
Private _SDateChanged As Boolean
Private _PaidAmountChanged As Boolean
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Public Sub New(line As Integer, status As String, sdate As Date, paidamount As Double)
_Line = line
_Status = status
_SDate = sdate
_PaidAmount = paidamount
End Sub
Public Sub New(line As Integer, status As String)
_Line = line
_Status = status
End Sub
Public ReadOnly Property Print() As String
Get
Return "Line: " & _Line & ", Status: " & _Status & ", Amount: " & _PaidAmount & ", Date: " & _SDate.ToShortDateString
End Get
End Property
Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
If Not Propertyname.Contains("Changed") Then
Changed = True
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
End Sub
Public Property Changed() As Boolean
Get
Return _Changed
End Get
Set(ByVal value As Boolean)
If _Changed <> value Then
_Changed = value
OnPropertyChanged("Changed")
End If
End Set
End Property
Public Property Line() As Integer
Get
Return _Line
End Get
Set(value As Integer)
If _Line <> value Then
_Line = value
LineChanged = True
OnPropertyChanged("Line")
End If
End Set
End Property
Public Property Status() As String
Get
Return _Status
End Get
Set(value As String)
If _Status <> value Then
_Status = value
StatusChanged = True
OnPropertyChanged("Status")
End If
End Set
End Property
Public Property SDate() As Date
Get
Return _SDate
End Get
Set(value As Date)
If _SDate <> value Then
_SDate = value
SDateChanged = True
OnPropertyChanged("SDate")
End If
End Set
End Property
Private Property PaidAmount() As Double
Get
Return _PaidAmount
End Get
Set(value As Double)
If _PaidAmount <> value Then
_PaidAmount = value
PaidAmountChanged = True
OnPropertyChanged("PaidAmount")
End If
End Set
End Property
Public Property LineChanged() As Boolean
Get
Return _LineChanged
End Get
Set(value As Boolean)
If _LineChanged <> value Then
_LineChanged = value
OnPropertyChanged("LineChanged")
End If
End Set
End Property
Public Property StatusChanged() As Boolean
Get
Return _StatusChanged
End Get
Set(value As Boolean)
If _StatusChanged <> value Then
_StatusChanged = value
OnPropertyChanged("StatusChanged")
End If
End Set
End Property
Public Property SDateChanged() As Boolean
Get
Return _SDateChanged
End Get
Set(value As Boolean)
If _SDateChanged <> value Then
_SDateChanged = value
OnPropertyChanged("SDateChanged")
End If
End Set
End Property
Public Property PaidAmountChanged() As Boolean
Get
Return _PaidAmountChanged
End Get
Set(value As Boolean)
If _PaidAmountChanged <> value Then
_PaidAmountChanged = value
OnPropertyChanged("PaidAmountChanged")
End If
End Set
End Property
End Class
You need to update the collection on the UI thread. To do this make sure to pass the data back via the Result property on the DoWorkEventArgs.
In your completed event you can then run through the data via the RunWorkerCompletedEventArgs.Result property and set your ObservableCollection accordingly, all on the UI thread.
If you wanted to avoid the BackgroundWorker you could use the Dispatcher.
ThreadStart start = delegate()
{
// make your calls to the db
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action<object>(UpdateCollection),
new object[] { myData });
};
new Thread(start).Start();
private void UpdateCollection(object data)
{
//iterate your collection and add the data as needed
}
Whatever route you go, the root cause is attempting to access an object created on the UI thread from a differing thread.

Refresh TreeView CollectionViewSource ObservableCollection Item Changed

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.
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?

Resources