C
C#•3mo ago
Dingus

Updating datagrid gui from other threads

In wpf, I have an application that has a button and datagrid. The button over the datagrid adds an object to the datagrid called barProgress. The class barProgress contains one public int value called progress and is a default of 0 when initialized. The datagrid itself has two columns. Column one contains a progress bar for each object and Column two has a button that tells the progress of object to go up by one every second. I understand creating/adding/binding objects to the datagrid and their values. I added background workers (running async of course) per object to update the object.progress by one per second. Here's the problem. I do not understand how to update the gui other than calling a method to refresh the ENTIRE datagrid. If I refresh the datagrid have issues with the buttons associated with each object being unable to be used the split second the datagrid refreshes. Is there a concept I am missing? I do not have an observable collection or notify changes property for the class of bar progress. I believe that is what I need but I also cannot find a good example of how those work and I wonder that even if I get those to work does that mean the button per object is still going to refresh and be disabled for a split second? This is my code. This is just a demo for this specific issue that pertains to a much bigger project I have where refreshes are much more rapid and button access really matters.
GitHub
GitHub - Dingus115/multithreaded-Datagrid
Contribute to Dingus115/multithreaded-Datagrid development by creating an account on GitHub.
No description
15 Replies
ACiDCA7
ACiDCA7•3mo ago
i noticed that you named your project mvvm, but what i see that is no mvvm backgroundworker ha a event oncompleted that will be called on the ui thread when the backgroundworker finishes.. making manual dispatching unneccessary im not too familiar with datagrid, tbh i would have expected that when you add item that it will be shown directly
Dingus
DingusOP•3mo ago
I am trying to learn mvvm right now to see if maybe that was the aproach that I needed. I'm just having a lot of trouble wrapping my head around the concepts of the viewmodel that I see make into a INotifyClass. If you know/have any super simple examples of a mvvm approach with just like a single variable to change that would be greatly appreaciated! I believe that even if I got mvvm approach working though that the datagrid would still need to refresh to show the objects updated progress through its progress bar unless there is some magical sorcery I dont know that allows a single cell to refresh rather than the row.
ACiDCA7
ACiDCA7•3mo ago
what you are missing in your case is just the notify propertychanged in class1 then you can skip the refreshstuff because it will just update the progress and nothing else
public class ProgressbarProgress : INotifyPropertyChanged
{
private decimal progress1 = 0;

public decimal progress
{
get => progress1; set
{
progress1 = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class ProgressbarProgress : INotifyPropertyChanged
{
private decimal progress1 = 0;

public decimal progress
{
get => progress1; set
{
progress1 = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
i was playing around with it a bit
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml.Serialization;

namespace mvvm
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private BackgroundWorker? worker;
public ProgressbarProgress? temp;
public MainWindow()
{
InitializeComponent();
}

private void addObject(object sender, RoutedEventArgs e)//add class progressbarprogress to datagrid
{
progressBarDataGrid.Items.Add(temp = new ProgressbarProgress { progress = 0 });
}

private void Button_Click(object sender, RoutedEventArgs e)//button to start thread work per object
{
ProgressbarProgress fill = (ProgressbarProgress)progressBarDataGrid.CurrentItem;


fill.progress += 10;
}

private void StartIncreaseCount(object sender, DoWorkEventArgs e)//start method for worker
{
increaseCount((ProgressbarProgress)e.Argument);
}

private void increaseCount(ProgressbarProgress fill)//change calue for progress
{
while (fill.progress < 100)
{
Thread.Sleep(100);
this.Dispatcher.Invoke(new Action(() => { fill.progress++; }));

//refreshGridFunction();
}
}
}
}
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml.Serialization;

namespace mvvm
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private BackgroundWorker? worker;
public ProgressbarProgress? temp;
public MainWindow()
{
InitializeComponent();
}

private void addObject(object sender, RoutedEventArgs e)//add class progressbarprogress to datagrid
{
progressBarDataGrid.Items.Add(temp = new ProgressbarProgress { progress = 0 });
}

private void Button_Click(object sender, RoutedEventArgs e)//button to start thread work per object
{
ProgressbarProgress fill = (ProgressbarProgress)progressBarDataGrid.CurrentItem;


fill.progress += 10;
}

private void StartIncreaseCount(object sender, DoWorkEventArgs e)//start method for worker
{
increaseCount((ProgressbarProgress)e.Argument);
}

private void increaseCount(ProgressbarProgress fill)//change calue for progress
{
while (fill.progress < 100)
{
Thread.Sleep(100);
this.Dispatcher.Invoke(new Action(() => { fill.progress++; }));

//refreshGridFunction();
}
}
}
}
and removed the refreshbutton from xaml when you click on reset it will just add 10 to progress and update
Dingus
DingusOP•3mo ago
you're an actual godsend. unfortunately I still cant wrap my head around the onpropertychanged function. I will do research to break that function down so I understand what the definition of each part means!
ACiDCA7
ACiDCA7•3mo ago
now to the explanation why it this works or more why you needed the refresh before: you created a a binding to a property that had didnt notify for changes, so the ui didnt know when to update... that was the reason you had to invoke refresh so the datagrid can pull the new data now that notify property changed is implemented the ui gets an event when the progress has been changed and can update it directly thats what inotifypropertychanged is for there is also icollectionchanged that updates the ui if a collection got longer/shorter bindings in the xaml are basicly the way to tell wpf to listen to these events sorry but i dont want to get to much into detail about mvvm.. its late and i would need to rite a book..xD
Dingus
DingusOP•3mo ago
youre totally fine. I really appreciate the help! I just want to make sure I understand what I am doing wrong and understand the concepts behind what I need rather than just copy paste. again thank you so much!
ACiDCA7
ACiDCA7•3mo ago
if you have specific questions i might answer them but explaing mvvm from scratch is a nono for me today
Dingus
DingusOP•3mo ago
'protected void OnPropertyChanged([CallerMemberName] string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); }' Mainly what does "[CallerMemberName] string name = null" even define. Is this the object, the binding name, or the property of object (and if property does that mean that then the function uses this method to update other columns of different values that change as well?)
ACiDCA7
ACiDCA7•3mo ago
[CallerMemberName] is a special attribute provided by dotnet that puts the nameof the member that calls that method into that string wihtout having to do it manually so i this case progress calls OnPropertyChanged so name will be "progress" when invoking PropertyChanged, wpf needs to know what to update in this case progress string name = null is for the case if you want to update another property then you have to provide the name yourself in OnPropertyChanged(); eg OnPropertyChanged("someotherprop");
Dingus
DingusOP•3mo ago
sweet. If another column and property was made called "RandomInt", then would that mean that 1. I would need to set up another "public int RandomInt" with get/set where I would also need to pass the onpropertyChanged event handler to 2. that "string? name =" would equal "RandomInt" as the value?
ACiDCA7
ACiDCA7•3mo ago
asuming i understood you correctly, yes
Dingus
DingusOP•3mo ago
this is amazing 😀 thank you so much for the help. I'll need to implement this into a much larger project next week and thanks to you now it won't be nearly as painful now that I understand it.
ACiDCA7
ACiDCA7•3mo ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.TextFormatting;
using System.Xml.Linq;

namespace mvvm
{
public class ProgressbarProgress : INotifyPropertyChanged
{
private decimal progress1 = 0;

public decimal progress
{
get => progress1;
set
{
progress1 = value;
OnPropertyChanged();
}
}

private int randomInt = 0;

public int RandomInt
{
get => randomInt;
set
{
randomInt = value;
OnPropertyChanged();
}
}


public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.TextFormatting;
using System.Xml.Linq;

namespace mvvm
{
public class ProgressbarProgress : INotifyPropertyChanged
{
private decimal progress1 = 0;

public decimal progress
{
get => progress1;
set
{
progress1 = value;
OnPropertyChanged();
}
}

private int randomInt = 0;

public int RandomInt
{
get => randomInt;
set
{
randomInt = value;
OnPropertyChanged();
}
}


public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
private void Button_Click(object sender, RoutedEventArgs e)//button to start thread work per object
{
ProgressbarProgress fill = (ProgressbarProgress)progressBarDataGrid.CurrentItem;
fill.progress += 10;
fill.RandomInt = Random.Shared.Next(0, 100);
}
private void Button_Click(object sender, RoutedEventArgs e)//button to start thread work per object
{
ProgressbarProgress fill = (ProgressbarProgress)progressBarDataGrid.CurrentItem;
fill.progress += 10;
fill.RandomInt = Random.Shared.Next(0, 100);
}
<DataGridTemplateColumn Width="*" Header="randomint">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Width="50" Margin="2" Text="{Binding RandomInt}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="randomint">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Width="50" Margin="2" Text="{Binding RandomInt}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
i have quickly thrown something together so that we are talking about the same stuff
Dingus
DingusOP•3mo ago
I really appreciate your time! That was exactly what I was talking about
ACiDCA7
ACiDCA7•3mo ago
no problem.. you showed genuine interested, and knew how to ask a question.. thats when i go the extramile ;)
Want results from more Discord servers?
Add your server