Home > Enterprise >  DataGrid update not working when changed source
DataGrid update not working when changed source

Time:12-17

This is my DataGrid testing code. I set some data on the DataGrid source with ObservableCollection and bound this. And I modify the value where the ObservableCollection member's property. In this case, my UI has to show that value is changed. However, my DataGrid only interact when I selected the cell.

Binding

public ObservableCollection<MyClass> griddata { get; set; } = new ObservableCollection<MyClass>();

My Class

public class MyClass
{
    public int num { get; set; }
    public int idxnumber { get; set; }
}

XAML

<Grid>
    <DataGrid ItemsSource="{Binding griddata, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
    </DataGrid>
</Grid>

Function

//... griddata.add(someOfData);
public void ViewModel()
{
    int i = 0;
    while(i < 30)
    {
        testing(0);
        i  ;
    }
}

private void testing(int idxnum)
{
    var test = griddata.Where(z => z.idxnumber == idxnum).FirstOrDefalut();
    test.num  = 1;
}

Result
Cell value shows that 0 and I selected value is changed to 30, immediately.

Expect result
Cell value shows 0 to 30 continuously.

EDIT :
This is my whole code:

XAML

<Window x:Class="TestSol3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestSol3"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>
        <DataGrid ItemsSource="{Binding griddata}">
            
        </DataGrid>
    </Grid>
</Window>

Model.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestSol3
{
    public class Model
    {
        public int num { get; set; }
        public int idxnumber { get; set; }
    }
}

ViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestSol3
{
    public class ViewModel
    {
        public ObservableCollection<Model> griddata { get; set; } = new ObservableCollection<Model>();

        public ViewModel()
        {
            griddata.Add(new Model() { num = 0, idxnumber = 0 });
            griddata.Add(new Model() { num = 1, idxnumber = 1 });

            Load(0);
        }

        private void Load(int idxnum)
        {
            int i = 0;
            while(i < 30)
            {
                i  ;
                griddata[idxnum].num  ;
                //Thread.Sleep(200);
            }
        }
    }
}

CodePudding user response:

Try implementing INotifyPropertyChanged in your model

namespace TestSol3
{
    public class ViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<Model> _griddata = new ObservableCollection<Model>();
        public ObservableCollection<Model> griddata 
        { 
            get => _griddata;
            set
            {
                _griddata= value;
                OnChangedProperty("griddata");
            }
        }

        public void OnChangedProperty(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
}

CodePudding user response:

[Solution]

In WPF, you can get dispatcher from Application.Current.Dispatcher

How to pass the UI Dispatcher to the ViewModel

And we need to change items in ObservableCollection to notify value change events.

The document of ObservableCollection says:

Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.

https://docs.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=net-6.0

private void Load(int idxnum)
{
    Task.Run(() =>
    {
        int i = 0;
        while (i < 30)
        {
            i  ;
            var model = griddata[idxnum];
            model.num  ;

            var dispatcher = Application.Current.Dispatcher;
            dispatcher.Invoke(() =>
            {
                griddata.RemoveAt(idxnum);
                griddata.Insert(idxnum, model);
            });

            Thread.Sleep(500);
        }
    });
}

[Simple Demo]

I wrote a demo project to show how to update UI value continuously as below.

It use Task to start another thread to update UI value, hope it helps.

MainWindow.xaml

<Window x:Class="TestWpfApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Title="MainWindow" Height="450" Width="800">        

    <StackPanel>
        <TextBlock Name="MyTextBlock" FontSize="20"></TextBlock>
        <Button Click="Button_Click">Start</Button>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace TestWpfApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private Task? CurrentTask { get; set; }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // only one task is supported simultaneously
            if (this.CurrentTask != null
                && !this.CurrentTask.IsCompleted)
            {
                return;
            }

            // start a task to calculate value continuously
            this.CurrentTask = Task.Run(() =>
            {
                int i = 0;
                while (i < 30)
                {
                      i;
                    Thread.Sleep(500);

                    // update value in the UI thread
                    this.Dispatcher.Invoke(() =>
                    {
                        this.MyTextBlock.Text = i.ToString();
                    });
                }
            });
        }
    }
}
  • Related