Wpf Observable Collection and DataGrid Do Not Change Changes

I have an observable collection in a view model that implements a Bindable Base as follows. Please take a look at the MoveUp and MoveDown methods, where they are bound to two buttons in the view. When I press the up button, I want the selected row in the datagrid to move one step up according to the sequence column in the database and down one step down. Both methods work EXCELLENT. The problem is that changes are displayed in the data file only when the entire view is updated. My requirement is when the button is pressed. I want the view to automatically update. I apologize for such a long code. Please help !!!!. I also have cs code for both the up and down buttons below the viewmodel. The only pointers in the code to emphasize are the ObservableCollection JobEntities, MoveUp, and MoveDown commands.

ViewModel.cs:

public class JobConfigurationViewModel : BindableBase { public JobConfigurationLogic JobConfigurationLogic = new JobConfigurationLogic(new JobConfigurationResultsRepository()); public SrcDestConfigurationLogic SrcDestConfigurationLogic = new SrcDestConfigurationLogic(new SrcDestCofigurationRepository()); private string _enterprise; public string Enterprise { get { return _enterprise; } set { SetProperty(ref _enterprise, value); } } private int currentJobID; private int currentSequence; private int previousJobID; private int previousSequence; private string _site; public string Site { get { return _site; } set { SetProperty(ref _site, value); } } private int _siteID; public int SiteID { get { return _siteID; } set { SetProperty(ref _siteID, value); } } private ObservableCollection<JobConfigurationResults> _jobEntities; public ObservableCollection<JobConfigurationResults> JobEntities { get { return _jobEntities; } set { SetProperty(ref _jobEntities, value); this.OnPropertyChanged("JobEntities"); } } //Source System List for Job private List<SourceSiteSystem> _lstJobSrcSystems; public List<SourceSiteSystem> LstJobSrcSystems { get { return _lstJobSrcSystems; } set { //Using bindable base setproperty method instead of older inotify prop changed method SetProperty(ref _lstJobSrcSystems, value); } } //Deestination System List for Job private List<DestinationSiteSystem> _lstJobDestSystems; public List<DestinationSiteSystem> LstJobDestSystems { get { return _lstJobDestSystems; } set { //Using bindable base setproperty method instead of older inotify prop changed method SetProperty(ref _lstJobDestSystems, value); } } //the Selected Source Site system ID private int _selectedSrcSiteSystemId = 0; public int SelectedSrcSiteSystemId { get { return _selectedSrcSiteSystemId; } set { //Using bindable base setproperty method instead of older inotify prop changed method SetProperty(ref _selectedSrcSiteSystemId, value); } } //the Selected Source Site system from the dropdown private SourceSiteSystem _selectedSrcSiteSystem; public SourceSiteSystem SelectedSrcSiteSystem { get { return _selectedSrcSiteSystem; } set { //Using bindable base setproperty method instead of older inotify prop changed method if (value != null) { SetProperty(ref _selectedSrcSiteSystem, value); SelectedSrcSiteSystemId = SelectedSrcSiteSystem.SiteSystemId; } } } //the Selected Destination Site system ID private int _selectedDestSiteSystemId = 0; public int SelectedDestSiteSystemId { get { return _selectedDestSiteSystemId; } set { //Using bindable base setproperty method instead of older inotify prop changed method SetProperty(ref _selectedDestSiteSystemId, value); } } //the Selected Destination Site system from the dropdown private DestinationSiteSystem _selectedDestSiteSystem; public DestinationSiteSystem SelectedDestSiteSystem { get { return _selectedDestSiteSystem; } set { //Using bindable base setproperty method instead of older inotify prop changed method if (value != null) { SetProperty(ref _selectedDestSiteSystem, value); SelectedDestSiteSystemId = SelectedDestSiteSystem.SiteSystemId; } } } private JobConfigurationResults _jeJobConfigurationResults; public JobConfigurationResults JEJobConfigurationResults { get { return _jeJobConfigurationResults; } set { _jeJobConfigurationResults = value; } } private List<JobTaskConfiguration> _taskSelectionList = new List<JobTaskConfiguration>(); private CancellationTokenSource _source; private RelayCommand<object> _commandSaveInstance; private RelayCommand<object> _hyperlinkInstance; private RelayCommand<object> _commandRunJob; private RelayCommand<object> _upCommand; private RelayCommand<object> _downCommand; private IEventAggregator _aggregator; /// <summary> /// This is a Subscriber to the Event published by EnterpriseViewModel /// </summary> /// <param name="agg"></param> public JobConfigurationViewModel(IEventAggregator agg) { _aggregator = agg; PubSubEvent<Message> evt = _aggregator.GetEvent<PubSubEvent<Message>>(); evt.Subscribe(message => Enterprise = message.Enterprise.ToString(), ThreadOption.BackgroundThread); evt.Subscribe(message => Site = message.Site.ToString(), ThreadOption.BackgroundThread); evt.Subscribe(message => SiteID = message.SiteID, ThreadOption.BackgroundThread); //evt.Unsubscribe(); StartPopulate(); } private async void StartPopulate() { await TaskPopulate(); } //This is to ensure that the publisher has published the data that is needed for display in this workspace private bool TaskProc() { Thread.Sleep(500); PubSubEvent<Message> evt = _aggregator.GetEvent<PubSubEvent<Message>>(); evt.Subscribe(message => Enterprise = message.Enterprise.ToString(), ThreadOption.BackgroundThread); evt.Subscribe(message => Site = message.Site.ToString(), ThreadOption.BackgroundThread); evt.Subscribe(message => SiteID = message.SiteID, ThreadOption.BackgroundThread); return DoPopulate(); } private Task<bool> TaskPopulate() { _source = new CancellationTokenSource(); return Task.Factory.StartNew<bool>(TaskProc, _source.Token); } /// <summary> /// This method handles the populating of the Source and Destination Dropdowns and the Job entity and Task Datagrid /// This is mainly driven by the Site selected in the previous workspace /// </summary> /// <returns></returns> private bool DoPopulate() { PopulateSourceDestinations(this.SiteID); return true; } /// <summary> /// this method displays all entities and tasks for the site. /// This is done async so that the Publisher thread is not held up /// </summary> public void GetJobConfigurationResults() { if (SelectedSrcSiteSystem == null) { SelectedSrcSiteSystem = LstJobSrcSystems[0]; } if (SelectedDestSiteSystem == null) { SelectedDestSiteSystem = LstJobDestSystems[0]; } SelectedSrcSiteSystemId = SelectedSrcSiteSystem.SiteSystemId; SelectedDestSiteSystemId = SelectedDestSiteSystem.SiteSystemId; var jobConfigurationResults = new JobConfigurationResults { SourceId = SelectedSrcSiteSystemId, DestinationId = SelectedDestSiteSystemId }; JobEntities = new ObservableCollection<JobConfigurationResults>(); JobEntities = JobConfigurationLogic.GetResults(jobConfigurationResults.SourceId, jobConfigurationResults.DestinationId); _taskSelectionList = new List<JobTaskConfiguration>(JobEntities.Count * 3); } /// <summary> /// //Adding a method to pupulate the Source and Destination dropdown lists. /// This is done async so that the Publisher thread is not held up /// </summary> /// /// public async void PopulateSourceDestinations(int siteId) { this.LstJobSrcSystems = SrcDestConfigurationLogic.LoadSourceSiteSystems(siteId); this.LstJobDestSystems = SrcDestConfigurationLogic.LoadDestinationSystems(siteId); GetJobConfigurationResults(); } public ICommand HyperlinkCommand { get { if (_hyperlinkInstance == null) _hyperlinkInstance = new RelayCommand<object>(openDialog); return _hyperlinkInstance; } } private void openDialog(object obj) { JobConfigurationResults results = obj as JobConfigurationResults; JEJobConfigurationResults = JobEntities.SingleOrDefault(x => x.JobEntityId == results.JobEntityId); } public ICommand CommandSave { get { if (_commandSaveInstance == null) _commandSaveInstance = new RelayCommand<object>(saveJobConfigurationChanges); return _commandSaveInstance; } } public ICommand CommandRunJob { get { return _commandRunJob ?? (_commandRunJob = new RelayCommand<object>(RunJob)); } } /// <summary> /// this saves all the changes in the selection made by the user /// </summary> /// <param name="ob"></param> public void saveJobConfigurationChanges(object ob) { foreach (var job in JobEntities) { int jobEntityId = job.JobEntityId; foreach (var task in job.TaskDetails) { int id = task.JobTask_ID; bool isSelected = task.IsSelected; _taskSelectionList.Add(task); } } JobConfigurationLogic.UpdateTaskSelection(_taskSelectionList); } public ICommand UpCommand { get { if (_upCommand == null) _upCommand = new RelayCommand<object>(MoveUp); return _upCommand; } } private void MoveUp(object obj) { if (obj != null) { JobConfigurationResults results = obj as JobConfigurationResults; currentJobID = results.JobEntityId; currentSequence = results.SequenceOrder - 1; try { JobConfigurationResults res = _jobEntities.SingleOrDefault(n => n.SequenceOrder == currentSequence); previousJobID = res.JobEntityId; previousSequence = res.SequenceOrder + 1; // JobConfigurationLogic.UpdateSequence(currentJobID, previousSequence, previousJobID, currentSequence); JobConfigurationLogic.UpdateSequence(currentSequence, currentJobID, previousSequence, previousJobID); OnPropertyChanged("JobEntities"); } catch (NullReferenceException) { MessageBox.Show("Can't move the top record"); } } else { MessageBox.Show("Please Select a row that you want to sort"); } } public ICommand DownCommand { get { if (_downCommand == null) _downCommand = new RelayCommand<object>(MoveDown); return _downCommand; } } private void MoveDown(object obj) { if (obj != null) { JobConfigurationResults results = obj as JobConfigurationResults; currentJobID = results.JobEntityId; currentSequence = results.SequenceOrder + 1; try { JobConfigurationResults res = _jobEntities.SingleOrDefault(a => a.SequenceOrder == currentSequence); previousJobID = res.JobEntityId; previousSequence = res.SequenceOrder - 1; JobConfigurationLogic.UpdateSequence(currentSequence, currentJobID, previousSequence, previousJobID); OnPropertyChanged("JobEntities"); } catch (NullReferenceException) { MessageBox.Show("You have reached the end"); } } else { MessageBox.Show("Please Select a row that you want to sort"); } } /// <summary> /// Execute an etl job using the current job id /// </summary> private void RunJob(object obj) { JobEngine jobEngine = new JobEngine(); var jobId = JobEntities[0].JobId; jobEngine.ProcessJob(jobId); } } 

CS CODE:

 private void btnup_Click(object sender, RoutedEventArgs e) { dgEntities.Items.Refresh(); //dgEntities.GetBindingExpression(DataGrid.ItemsSourceProperty).UpdateTarget(); } private void btndown_Click(object sender, RoutedEventArgs e) { dgEntities.GetBindingExpression(DataGrid.ItemsSourceProperty).UpdateTarget(); } 
+6
source share
1 answer

An ObservableCollection will notify you of a change. There is no reason to do this manually, so you can delete all OnPropertyChanged("JobEntities"); . This will lead you to a cleaner solution.

MSDN

WPF provides the ObservableCollection class, which is a built-in data collection implementation that implements the INotifyCollectionChanged Interface.

The next part is that the ObservableCollection will notify you of changes to the collection itself (add / remove). Any changes to the item in the list will not be submitted. For this, the simplest method is to implement the INotifyPropertyChanged elements used in the Observable Collection.

I am using PRISM 5 in this example, so it should be very similar to what you are doing. There are a couple of major design changes you code. First, I use the direct property for my Observable Collection. We know that the structure will handle any add / remove operations to this collection. Then, to notify me when I change a property inside an object in an observable collection, I used the notify property in the TestEntity class TestEntity .

 public class MainWindowViewModel : BindableBase { //Notice no OnPropertyChange, just a property public ObservableCollection<TestEntity> TestEntities { get; set; } public MainWindowViewModel() : base() { this.TestEntities = new ObservableCollection<TestEntity> { new TestEntity { Name = "Test", Count=0}, new TestEntity { Name = "Test1", Count=1}, new TestEntity { Name = "Test2", Count=2}, new TestEntity { Name = "Test3", Count=3} }; this.UpCommand = new DelegateCommand(this.MoveUp); } public ICommand UpCommand { get; private set; } private void MoveUp() { //Here is a dummy edit to show the modification of a element within the observable collection var i = this.TestEntities.FirstOrDefault(); i.Count = 5; } } 

Here is my essence, pay attention to BindableBase and the fact that I am notifying about the change. This allows the DataGrid or whatever you use to be notified of a property change.

 public class TestEntity : BindableBase { private String _name; public String Name { get { return _name; } set { SetProperty(ref _name, value); } } //Notice I've implemented the OnPropertyNotify (Prism uses SetProperty, but it the same thing) private Int32 _count; public Int32 Count { get { return _count; } set { SetProperty(ref _count, value); } } } 

Now actually all TestEntity should have implemented INotifyPropertyChanged for this, but I am using PRISM BindableBase as an example.

EDIT

I found a similar question about SO. I think yours is a little different, but they overlap with concepts. This may help to view it.

Observed collection Notify when a property is changed in MVVM

EDIT

If the datagrid is sorted, the previous method will not update the grid. To deal with this, you need to update the grid view, but you cannot access it directly using MVVM. Therefore, to handle this, you will want to use CollectionViewSource .

 public class MainWindowViewModel : BindableBase { //This will bind to the DataGrid instead of the TestEntities public CollectionViewSource ViewSource { get; set; } //Notice no OnPropertyChange, just a property public ObservableCollection<TestEntity> TestEntities { get; set; } public MainWindowViewModel() : base() { this.TestEntities = new ObservableCollection<TestEntity> { new TestEntity { Name = "Test", Count=0}, new TestEntity { Name = "Test1", Count=1}, new TestEntity { Name = "Test2", Count=2}, new TestEntity { Name = "Test3", Count=3} }; this.UpCommand = new DelegateCommand(this.MoveUp); //Initialize the view source and set the source to your observable collection this.ViewSource = new CollectionViewSource(); ViewSource.Source = this.TestEntities; } public ICommand UpCommand { get; private set; } private void MoveUp() { //Here is a dummy edit to show the modification of a element within the observable collection var i = this.TestEntities.FirstOrDefault(); i.Count = 5; //Now anytime you want the datagrid to refresh you can call this. ViewSource.View.Refresh(); } } 

The TestEntity class TestEntity not change, but here again the class:

 public class TestEntity : BindableBase { private String _name; public String Name { get { return _name; } set { SetProperty(ref _name, value); } } //Notice I've implemented the OnPropertyNotify (Prism uses SetProperty, but it the same thing) private Int32 _count; public Int32 Count { get { return _count; } set { SetProperty(ref _count, value); } } } 

For clarification, here my XAML shows the binding to the new CollectionViewSource .

 <DataGrid Grid.Row="1" ItemsSource="{Binding ViewSource.View}"></DataGrid> 

For further reading, you can refer to the MSDN article.

Here's another topical question / answer - Re-sort WPF DataGrid after limited data has changed

+10
source

Source: https://habr.com/ru/post/978170/


All Articles