I have WPF with MVVM app. ViewModel and View are connected. Controls from View are bound to ViewModel, and ViewModel inherits INotifyChanged. Simple control update in ViewModel works fine.
I'd like to create some info class that contains some info fields. Next I want to create my own thread that maps fields from Info class to fields in ViewModel that updates View. Object of Info class will be used as arg of function called in ViewModel.
private int someControl;
public SomeControl {
get{return someControl;}
set
{
someControl = value;
OnPropChanged("SomeControl");
}
private InfoClass info = new InfoClass();
Thread thread = null;
public ViewModel()
{
Thread thread = new Thread(new ThreadStart(update));
thread.IsBackground = true;
thread.start();
someLongFunction(info);
}
private void update()
{
SomeControl = info.someField;
thread.sleep(1000);
update();
}
What should I add or change to get update periodically? Now, update is only when someLongFunction ends its job.
If what you actually want is continuous view updates then you don't have to create a thread for that because then you will have to make those updates on the Dispatcher thread (UI Thread). Instead you can use the DispatcherTimer class which was built exactly for this kind of situation where you can provide an interval and the DispatcherTimer will call your method periodically at that interval and will do that automatically on the Dispatcher Thread.
Related
I have WinForms with WPF UserControl.
public partial class DynamicMediator : Form
{
public DynamicMediator()
{
InitializeComponent();
lmDynamicMediator = new LMDynamicMediator.MediatorMainWindow();
this.elementHost1.Child = this.lmDynamicMediator;
}
public MainWindowViewModel GetEditFormViewModel()
{
return lmDynamicMediator.Resources["ViewModel"] as MainWindowViewModel;
}
}
I start a new process in my ViewModel
after that I need to update my observableCollection in ViewModel
I use
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => HasListMessages.Add(item)));
but I have exception like this
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread
if I use code like this
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => HasListMessages.Add(item)));
I have exception like this
System.Windows.Application.Current.get returned null
What I do wrong?
How I can get System.Windows.Application.Current in my ViwModel
Calling Dispatcher.CurrentDispatcher in a thread that is not yet associated with a Dispatcher will create a new Dispatcher, which is not the Dispatcher of the UI thread.
Use the Dispatcher of the thread in which the view model instance is created. Keep it in a field of the view model class
public class MainWindowViewModel
{
private readonly Dispatcher dispatcher;
public MainWindowViewModel()
{
dispatcher = Dispatcher.CurrentDispatcher;
}
...
}
and later use that field:
dispatcher.BeginInvoke(() => HasListMessages.Add(item));
I am trying to update GUI when dictionary value changes on WPF, MVVM. Basically, I got following dictionary on separate thread and library/project:
public static Dictionary<string, string> ProgressStageDictionary = new Dictionary<string, string>
{
{"Data Initiation", ""},
{"Data Import", ""}
};
A child library/project is a stand-alone application that doesn't know about GUI and won't have one. I try to update GUI from it I would have a project reference issue as GUI project reference to child project, not vice versa. That's why I cannot call GUI from that library using, for example, DispatcherHelper.CheckBeginInvokeOnUI.
GUI however as the main thread should know about child threads. So to achieve that I created an INotifyPropertyChanged Event:
public class ViewModelBase: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string caller = null)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
I connected it with my MVVM GUI variable
private List<StepItem> _stepItems { get; set; }
public List<StepItem> StepItems
{
get => _stepItems;
set
{
_stepItems = value;
OnPropertyChanged(nameof(Child.ProgressStageDictionary));
Thread.Sleep(250);
}
}
My GUI should update whenever Child.ProgressStageDictionary is update. However, my GUI is not catching that. What do I need to change to be able to watch variables updates on child threads?
I found an answer how to do it. I couldn't work out why my Observers does not catch update but I manage to do it using delegators.
On the main calculation thread, I create a method that dispatch update to GUI thread:
public void SuperStepProgressMethod(Dictionary<string, string> stepDictionary)
{
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
// Set step item list
StepItems = StepProgressHandler.UpdateStepList(stepDictionary);
});
}
After that when I am calling Project method I delegate MainViewModel method as an action to it.
public static void Run(
Action<Dictionary<string, string>> superStepProgressMethod)
{
ProgressStageDictionary["Data Initiation"] = Initiation();
superStepProgressMethod.Invoke(ProgressStageDictionary);#
}
Using this solution behind the scene threads are updating/running at following order:
GUI Thread -> Calculation Thread -> Separate Project Thread -> GUI Thread
After I tried lots and lots of solutions I couldn't solve this problem by any means so I started to believe that there is no solution for this problem.
I have an object that contains complex attributes. E.g: List<SomeComplexObject>. I am running a method from this class on a worker thread to keep the GUI running until the worker thread finishes. When it finishes execution, I want to use the attributes of these objects to update GUI let's say I want to use List<SomeComplexObject> looping through this list and update the GUI. But each time I try to access this list the debugger throws an InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
I tried to make all attributes of this class volatile but with no hope I also used Lazy<T> class approach to solve but the same problem occurs.
Class that contain the worker function:
public class MainModules
{
#region Attributes
public VIDEO video;
public string VideoPath
{
get;
set;
}
LowLevelModule lowLevelOutput;
//this list that I want to use to Update GUI
public volatile List<FaceRecognitionModule> faceModuleOutput;
//worker function running on different thread
public void RunMainModules()
{
//some complex work to set the class attributes
}
}
Thread creation in GUI class
private void RunMainModules_BtnClick(object sender, RoutedEventArgs e)
{
// MainModule = new MainModules(mainModuleObj, Inpath, lif, keyframefolderpath, trdbpath, labelspath, rrankspath, alignmatpath, 11, 10);
this.LazyMainModule = new Lazy<MainModules>(this.InitLazyMainModule);
MainModuleThread = new Thread(this.RunMainModules);
MainModuleThread.Start(MainModule);
}
public MainModules InitLazyMainModule()
{
return new MainModules(mainModuleObj, Inpath, lif, keyframefolderpath, trdbpath, labelspath, rrankspath, alignmatpath, 11, 10);
}
public void RunMainModules(Object obj)
{
//MainModules mm = obj as MainModules;
MainModules mm = LazyMainModule.Value;
mm.RunMainModules();
this.Dispatcher.Invoke((Action)(() =>
{
this.InitSpeechRec_Btn.IsEnabled = true;
}));
}
When I try to access faceModuleOutput in class MainModules from GUI I got InvalidOperationException.
Image img = new Image();
//InvalidOperationException occurs here
img.Source = LazyMainModule.Value.faceModuleOutput[0].keyframes[1].keyframe;
To brief this post:
I want to access an object instantiated by a background thread from main thread but it throws
InvalidOperationException : The calling thread cannot access this object because a different thread owns it.
A UI control needs to be created/modified from the GUI Thread. Doing otherwise is illegal.
It seems that the MainModuleThread is (at least) creating and modifying an Image . This should be done in the GUI Thread (the one that called RunMainModules_BtnClick)
You cannot modify or even access pretty much anything that relates to the UI thread from another thread. This can get pretty extreme/annoying sometimes because you can't even get the value in a textbox or check if a checkbox is checked or not. If you want to perform an action on an object owned by the UI thread you need to invoke the UI thread to do it.
UIObject.Dispatcher.Invoke(() => {
//[Perform your action in here]
});
Finally I found the solution ... Class BitmapImage is thread-affine so it can't be accessed by multiple threads you need first to make it opened for reading only closed for writing so the compiler can guarantee that no threads will modify it's content
So the solution ... :
//keyframe here is a BitmapImage so on creation we must call keyframe.Freeze()
LazyMainModule.Value.faceModuleOutput[0].keyframes[1].keyframe;
class KeyFrame:
public class KeyFrame
{
public volatile BitmapImage keyframe;
public volatile List<string> personsNames;
public volatile List<string> categories;
public KeyFrame(BitmapImage keyframe, List<string> personsNames, List<string> categories)
{
this.keyframe = keyframe;
//here we call Freeze funcition on creation to make it modifiable
this.keyframe.Freeze();
this.personsNames = personsNames;
this.categories = categories;
}
}
I have few controls on my MainForm in Winforms application. For example control that updates progress of each operation. These operations are classes which in run in different Thread.
How can i properly update those controls ?
in your main form you can add a function like this one
private delegate void doSomethingWithTheControlsDelegate(object obj);
public void doSomethingWithTheControls(object obj) {
if (this.InvokeRequired) {
this.BeginInvoke(new doSomethingWithTheControlsDelegate(this.doSomethingWithTheControls), obj);
} else {
// do something
}
}
the best way is to do that by events.
the easier way is to change them directly.
ensure that they are public and you overgive them to the class and then you can change it
Invoke(new MethodInvoker(delegate { frmMain.label1.Text = "bla"; }));
I would suggest using a model class which would contain the data which is displayed to the user. Databind your UI controls to the properties of the model, and update the model values from the worker threads (using appropriate invokations to ensure the update occurs on the UI thread so that you don't get the cross thread exception)
I am trying to understand some code. It is a small program that prints out log data. It is done by creating a form with a DataGridView that is filled by a DataTable. The form class also has a refresh function (RefreshPresentation). The BusinessLogic class does the actual work of updating the DataTable and calling the refresh function in the form. So I pretty much understand the functionality, but not why the program is structured the way it is.
Why is businessLogic.DoWork run as a
thread instead of just a normal method call?
Can someone explain the
RefreshPresentation function for me?
(BeginInvoke and the delegate)
Is it a good idea/practice to pass the MainForm as a parameter to BusinessLogic?
This is the main entry point for the application.
public class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
This is the relevant part of the form.
public partial class MainForm : Form
{
private BusinessLogic businessLogic;
private DataTable viewDataTable;
public MainForm()
{
InitializeComponent();
businessLogic = new BusinessLogic(this);
Thread t = new Thread(new ThreadStart(businessLogic.DoWork));
t.Start();
}
public delegate void RefreshPresentationDelegate(DataTable dataTable);
public void RefreshPresentation(DataTable dataTable)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new RefreshPresentationDelegate(RefreshPresentation), new object[] { dataTable });
return;
}
...
This is the business logic.
internal class BusinessLogic
{
private MainForm form;
private Logging.DAL.Logger loggerDAL;
private int lastId;
internal DataTable DataTable { get; private set; }
internal bool IsRunning { get; set; }
public BusinessLogic(MainForm form)
{
this.form = form;
this.loggerDAL = new Logging.DAL.Logger();
this.IsRunning = true;
DataTable = new DataTable();
}
public void DoWork()
{
while (this.IsRunning)
{
// Get new log messages.
if (DataTable.Rows.Count > 0)
this.lastId = (int)DataTable.Rows[DataTable.Rows.Count - 1]["Id"];
this.DataTable = loggerDAL.GetLogMessagesSinceLastQuery(lastId);
// Callback to GUI for update.
form.RefreshPresentation(this.DataTable);
// Wait for next refresh.
Thread.Sleep(1000);
}
}
}
Q1.Why is businessLogic.DoWork run as a thread instead of just a normal method call?
A1. DoWork needs to be on a separate thread then the main GUI thread, since the main GUI thread needs to be free to pump the message queue (which allows it to redraw itself, handle different GUI events, etc.) Try to create simple GUI program that has a while(true) in the main thread and see that the GUI gets stuck and doesn't redraw itself.
Q2.Can someone explain the RefreshPresentation function for me? (BeginInvoke and the delegate)
A2. Though the DoWork needs to be done on another thread so it doesn't block the GUI thread, updating the GUI needs to always be done from a GUI thread. In order to make this happen, you can call BeginInvoke, which posts a message to the message queue and causes your delegate to be executed on the GUI thread.
Q3.Is it a good idea/practice to pass the MainForm as a parameter to BusinessLogic?
A3. No. The MainForm can know about the business logic, but the business logic should not be aware of the GUI. Google "MVC" for more information on separating the GUI from the business logic.
1) Looks like BusinessLogic is doing some lengthy work. To keep the UI responsive during this processing, it is executed in a different thread.
2) RefreshPresentation() is a method responsible for updating/refreshing UI while background thread is processing to keep UI up to date. Since, UI cannot be changed from a thread besides the UI thread itself, you need to use Invoke()/BeginInvoke() methods to dispatch that code to be executed on UI thread.
3) I personally believe it is a bad idea and instead an event should be exposed by BusinessLogic class to notify data change.