Reputation: 4491
I have a DataGrid that binds to an ObservableCollection named Programs. On the window there's 2 buttons, one for changing a bit field in the selected row to Activate, the other to change it to Deactivate. Since we're using .EDMX files, so don't have access to the generated C# code for each model class, changing a value of the bit field in one of the rows of Programs doesn't change the value in the DataGrid. I understand that. I looked up here on SO how I might be able to do this. I found a post from almost 8 years ago, titled ObservableCollection not updating View. This looked very promising so I implemented the solution given by aqwert. However, it still is not working. I know the value is getting modified and following aqwert's solution I'm replacing Programs. Doesn't matter, it doesn't update the view.
We're using .NET Framework 4.5.2. We're also using MVVM Light. And we're using FirstFloor Software's ModernUI for WPF.
Here's the XAML for the DataGrid:
<DataGrid
Grid.Row="3"
Grid.Column="2"
AutoGenerateColumns="False"
BorderThickness="1"
CanUserAddRows="False"
ItemsSource="{Binding Programs}"
SelectedItem="{Binding SelectedProgram, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTemplateColumn
Width="Auto"
Header="ID"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
Width="Auto"
Header="Abbrev"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ProgramAbbrev}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
Width="Auto"
Header="Program Name"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ProgramName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
Width="Auto"
Header="Inactive"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Inactive, Converter={StaticResource BoolToYN}}" TextAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
Here's the definition of Programs from the VM:
private ObservableCollection<Program> programs;
public ObservableCollection<Program> Programs
{
get { return programs; }
set
{
if (programs != value)
{
programs = value;
RaisePropertyChanged("Programs");
}
}
}
and lastly, here's the code that I've made, following aqwert's solution:
//make a copy of Programs
var programsCopy = new List<Program>();
foreach (var item in Programs)
{
if (item.ID == SelectedProgram.ID)
{
item.Inactive = inactiveFlag;
item.UpdatedBy = Environment.UserDomainName + "\\" + Environment.UserName;
item.UpdatedOn = rightNow;
}
programsCopy.Add(item);
}
//copy over the top of Programs
Programs = new ObservableCollection<Program>(programsCopy);
Upvotes: 0
Views: 592
Reputation: 4491
I finally discovered my problem. Stupid error on my part. I have 2 routines, once each that are bound to two different ICommands. One for the Activate button, the other for the Deactivate button. I'll just should the Deactivate button's code:
private void ExecuteDeActivateCommand()
{
SetInactiveFlag(true);
GetProgramsSynchronously(SelectedRow.ID); //this call was what was causing me problems
}
Yesterday I showed you some code from the SetInactiveFlag(true) call. That worked fine and was setting the Inactive bit flag to true. However, I'd forgotten that I'd called the GetProgramsSynchronously(SelectedRow.ID) call would go against the database and refresh the in-memory Programs collection, with the Inactive flag set back to false.
Egg on my face. I want you all to learn from my mistake so hopefully you have avoid egg on your face.
Upvotes: 0
Reputation: 169420
An ObservableCollection<T>
doesn't raise change notifications to the UI when properties of individual items in the collection are modified. The way to do this is to implement the INotifyPropertyChanged
event in your Program
class.
If you can't modify Program
for whatever reason, you could create a new view model class that wraps it and change the type of your source collection property from ObservableCollection<Programs>
to IEnumerable<ProgramViewModel>
. You don't need an ObservableCollection
if you reset the Programs
property to a new instance each time you want to change the collection.
You then implement the INotifyPropertyChanged
in the ProgramViewModel
class and bind to properties of this one.
Upvotes: 1