Use ObservableCollection(of T) instead of List(of T).
ObservableCollection already implements the ability to notify the View of the addition or deletion of elements.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
I have a DataGrid that should display a new item when it is added to the ItemsSource but that is not happening and I can't see the error.
The XAML:
<DataGrid
x:Name="dgTimes"
ItemsSource="{Binding Path=Walks}">
<DataGrid.Columns>
....
</DataGrid>
The ViewModel:
Private CurrentWalk As New Walk
Private _Walks As List(Of Walk)
Public Property Walks As List(Of Walk)
Get
Return _Walks
End Get
Set(value As List(Of Walk))
_Walks = value
End Set
End Property
Private ReadOnly Property _Save As New RelayCommand(Of Integer)(AddressOf DoSave, AddressOf CanSave)
Public ReadOnly Property Save As RelayCommand(Of Integer)
Get
Return _Save()
End Get
End Property
Private Function CanSave(obj As Object) As Boolean
Return CurrentWalk.IsDirty
End Function
Private Sub DoSave(obj As Object)
<<Assign all properties to CurrentWalk>>
CurrentWalk.Add() 'Adds CurrentWalk to a database
Walks.Add(CurrentWalk)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(Walks)))
End Sub
Shouldn't that RaiseEvent cause the DataGrid to be updated?
Use ObservableCollection(of T) instead of List(of T).
ObservableCollection already implements the ability to notify the View of the addition or deletion of elements.
Hi,@ RogerSchlueter-7899. Welcome to Microsoft Q&A .
You could iterate through the WalksObservableCollection and update the corresponding items in the Walks list.
I added a method called UpdateWalksList
that clears the Walks
list and adds each item from the WalksObservableCollection
. This method is called whenever the WalksObservableCollection
is updated. Now, both Walks
and WalksObservableCollection
should stay in sync.
Whenever the data of dgTimes (WalksObservableCollection) is modified, the data of dgTimes1 (Walks) is also updated immediately.
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<StackPanel>
<DataGrid x:Name="dgTimes" ItemsSource="{Binding WalksObservableCollection}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{ Binding Id,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Name" Binding="{ Binding Name,UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
<DataGrid x:Name="dgTimes1" ItemsSource="{Binding Walks}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{ Binding Id,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Name" Binding="{ Binding Name,UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
codebehind:
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Public Class ViewModel
Implements INotifyPropertyChanged
Public Sub New()
PopulateData()
WalksObservableCollection = New ObservableCollection(Of Walk)(Walks)
End Sub
Private Sub PopulateData()
Walks.Add(New Walk() With {
.Id = 1,
.Name = "user1"
})
Walks.Add(New Walk() With {
.Id = 2,
.Name = "user2"
})
Walks.Add(New Walk() With {
.Id = 3,
.Name = "user3"
})
Walks.Add(New Walk() With {
.Id = 5,
.Name = "user4"
})
Walks.Add(New Walk() With {
.Id = 6,
.Name = "user5"
})
End Sub
Private _walksObservableCollection As ObservableCollection(Of Walk)
Public Property WalksObservableCollection As ObservableCollection(Of Walk)
Get
Return _walksObservableCollection
End Get
Set(value As ObservableCollection(Of Walk))
_walksObservableCollection = value
OnPropertyChanged(NameOf(WalksObservableCollection))
UpdateWalksList()
End Set
End Property
Private _Walks As List(Of Walk) = New List(Of Walk)()
Public Property Walks As List(Of Walk)
Get
Return _Walks
End Get
Set(value As List(Of Walk))
_Walks = value
OnPropertyChanged(NameOf(Walks))
UpdateObservableCollection()
End Set
End Property
Private Sub UpdateObservableCollection()
WalksObservableCollection.Clear()
For Each walk In Walks
WalksObservableCollection.Add(walk)
Next
End Sub
Private Sub UpdateWalksList()
Walks.Clear()
For Each walk In WalksObservableCollection
Walks.Add(walk)
Next
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Public Class Walk
Implements INotifyPropertyChanged
Private Property _Id As Integer
Public Property Id As Integer
Get
Return _Id
End Get
Set(ByVal value As Integer)
_Id = value
OnPropertyChanged(NameOf(Id))
End Set
End Property
Private _Name As String
Public Property Name As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
OnPropertyChanged(NameOf(Name))
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Update:
You could bind the UpdateCommand
to the CellEditEnding
event using the EventToCommand
behavior in XAML.Here is an example using the System.Windows.Interactivity
library, which is commonly used with MVVM scenarios.
Install the System.Windows.Interactivity NuGet package in your project.
<Window x:Class="MainWindow"
...
xmlns:local="clr-namespace:DataGridUpdate" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
>
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<StackPanel>
<DataGrid x:Name="dgTimes" ItemsSource="{Binding Walks}" SelectedItem="{Binding SelectedItem}" AutoGenerateColumns="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding">
<i:InvokeCommandAction Command="{Binding EditCellCommand}"
CommandParameter="{Binding SelectedItem, ElementName=dgTimes}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{ Binding Id,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Name" Binding="{ Binding Name,UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
codebedhind:
Public Class ViewModel
Implements INotifyPropertyChanged
Public Sub New()
PopulateData()
End Sub
Private Sub PopulateData()
Walks.Add(New Walk() With {
.Id = 1,
.Name = "user1"
})
Walks.Add(New Walk() With {
.Id = 2,
.Name = "user2"
})
Walks.Add(New Walk() With {
.Id = 3,
.Name = "user3"
})
Walks.Add(New Walk() With {
.Id = 5,
.Name = "user4"
})
Walks.Add(New Walk() With {
.Id = 6,
.Name = "user5"
})
End Sub
Private _Walks As List(Of Walk) = New List(Of Walk)()
Public Property Walks As List(Of Walk)
Get
Return _Walks
End Get
Set(value As List(Of Walk))
_Walks = value
OnPropertyChanged(NameOf(Walks))
End Set
End Property
Private _SelectedItem As Walk
Public Property SelectedItem As Walk
Get
Return _SelectedItem
End Get
Set(value As Walk)
_SelectedItem = value
OnPropertyChanged(NameOf(SelectedItem))
End Set
End Property
Private _editCellCommand As ICommand
Public ReadOnly Property EditCellCommand As ICommand
Get
If _editCellCommand Is Nothing Then
_editCellCommand = New RelayCommand(AddressOf EditCell)
End If
Return _editCellCommand
End Get
End Property
' Implement the logic for updating List(Of Walk)
Private Sub EditCell(parameter As Object)
Dim e As DataGridCellEditEndingEventArgs = TryCast(parameter, DataGridCellEditEndingEventArgs)
Dim editedItem As Walk = TryCast(e.Row.Item, Walk)
If editedItem IsNot Nothing Then
Dim index As Integer = Walks.IndexOf(editedItem)
If index <> -1 Then
Dim editedColumn As DataGridColumn = e.Column
Dim propertyName As String = editedColumn.SortMemberPath
Dim editedValue As Object = (TryCast(e.EditingElement, TextBox)).Text
If propertyName = "Id" Then
editedItem.Id = Convert.ToInt32(editedValue)
ElseIf propertyName = "Name" Then
editedItem.Name = editedValue.ToString()
End If
Walks(index) = editedItem
End If
End If
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Public Class Walk
Implements INotifyPropertyChanged
Private Property _Id As Integer
Public Property Id As Integer
Get
Return _Id
End Get
Set(ByVal value As Integer)
_Id = value
OnPropertyChanged(NameOf(Id))
End Set
End Property
Private _Name As String
Public Property Name As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
OnPropertyChanged(NameOf(Name))
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Public Class RelayCommand
Implements ICommand
Private ReadOnly _execute As Action(Of Object)
Private ReadOnly _canExecute As Func(Of Object, Boolean)
Public Sub New(execute As Action(Of Object), Optional canExecute As Func(Of Object, Boolean) = Nothing)
_execute = execute
_canExecute = canExecute
End Sub
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
AddHandler(ByVal value As EventHandler)
If _canExecute IsNot Nothing Then
AddHandler CommandManager.RequerySuggested, value
End If
End AddHandler
RemoveHandler(ByVal value As EventHandler)
If _canExecute IsNot Nothing Then
RemoveHandler CommandManager.RequerySuggested, value
End If
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
If _canExecute IsNot Nothing Then
CommandManager.InvalidateRequerySuggested()
End If
End RaiseEvent
End Event
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
If _canExecute Is Nothing Then
Return True
Else
Return _canExecute(parameter)
End If
End Function
Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
_execute(parameter)
End Sub
End Class
If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.