Manage tasks progressions in one user interface - c#

I have an application in witch the user can start tasks, heavy tasks. And I want to manage the progression of these tasks in one user interface Grid (each row is a task, with a progression bar) the user can show this grid by clicking a button (using the main thread). The problem that I have is a Cross Thread Operation. I know why: whenever the task progression changed (with thread1), the algorithm try to update the grid datasource (with the main thread). But I don't know how to fix it.
The DataSource property of my grid is set to a BindingList<BackgroundOperation>.
The definition of my task (BackgroundOperation)
public class BackgroundOperation
{
public int progression;
public int Progression
{
get { return progression;}
set
{
progression = value;
OnPropertyChanged("Progression");
}
}
public event EventHandler OnRun;
public event EventHandler<ProgressChangedEventArgs> OnProgressChanged;
public event PropertyChangedEventHandler PropertyChanged;
public void Run()
{
var task = new Task(() =>
{
if (OnRun != null)
OnRun(this, null);
});
task.Start();
}
public void ReportProgress(int progression)
{
Progression = progression;
if (OnProgressChanged != null)
OnProgressChanged(this, new ProgressChangedEventArgs { Progression = progression });
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}

You need to run the OnProgressChanged (which BTW should be called just ProgressChanged) on the UI thread. You can do that by saving the current SynchronizationContext when the class is created and then Post()ing the delegate there:
public class BackgroundOperation
{
private readonly SynchronizationContext m_synchronizationContext;
public BackgroundOperation()
{
m_synchronizationContext = SynchronizationContext.Current;
}
…
public void ReportProgress(int progression)
{
Progression = progression;
var handler = OnProgressChanged;
if (handler != null)
m_synchronizationContext.Post(
_ => handler(
this,
new ProgressChangedEventArgs { Progression = progression }),
null);
}
}

Related

c# WinForms DataBinding doesn't update my TextBox.text

I wanna bind a TextBox to a class property, so when this property changes, my TextBox changes automatically too (Windows Forms).
I have a class like this:
class Device : INotifyPropertyChanged
{
private string can_rpm;
public string Can_rpm
{
get { return can_rpm; }
set { can_rpm = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
\\lots of other codes
}
My main form has some code like this (with a textbox called 'tbTest'):
private void Form1_Load(object sender, EventArgs e)
{
Device device= device = new Device();
tbTest.DataBindings.Clear();
tbTest.DataBindings.Add(new Binding("Text",device,"Can_rpm",true,DataSourceUpdateMode.OnPropertyChanged));
\\lots of other stuff
}
My problem: My textBox never updates! A have some other code that updates the 'Can_rpm' property, but nothing shows on my textbox.text. BUT, if I change the empty value of my textbox to something else, my property DOES change too!
So it's working 'one way', but not the other!
I've searched here and googled it, but all I find is examples that does what is already done in my code, but mine doesn't work.
Thanks for helping if you can.
Try with this:
tbTest.DataBindings.Add(nameof(TextBox.Text), device, nameof(Device.Can_rpm));
I've tested the code with your Device class code and this code in the form constructor:
var device = new Device();
this.textBox1.DataBindings.Add(nameof(TextBox.Text), device, nameof(Device.Can_rpm));
device.Can_rpm = "Hello";
After that, my textbox has "Hello" text.
UPDATE
You need update controls always in the thread in which they was created, usually in the main thread. I use a Form extension methods to do that:
public static class FormExtends
{
public static void RunInMyThread(this Form form, Action operation)
{
if (form.InvokeRequired)
{
form.BeginInvoke(operation);
}
else
{
operation();
}
}
}
With the previous extension, you can do (in your Form code) your updates in this way:
this.RunInMyThread(() => device.Can_rpm = "Hello");
Another way to do that:
public class Device : INotifyPropertyChanged
{
private static SynchronizationContext GuiContext = SynchronizationContext.Current;
private string can_rpm;
public string Can_rpm
{
get { return can_rpm; }
set { can_rpm = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
GuiContext.Post(
s => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)),
null);
}
}
}
GuiContext is initialized in the main thread so it runs the code in that thread. If you change your PropertyChanged event to throw in the Post of that context, you don't need take care about where your device properties are changed because the notiy always run in the main thread.

The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

In my windows phone 8.1 application I have a singleton service DataService which should once in a while be downloading some data. Meanwhile on UI I should be displaying the amount of data received. DataService.StartGettingData() gets called when user logs into the application:
void StartGettingData()
{
if (getDataTaskCancellationTokenSource != null)
getDataTaskCancellationTokenSource.Cancel();
getDataTaskCancellationTokenSource = new CancellationTokenSource();
var token = getDataTaskCancellationTokenSource.Token;
Task.Factory.StartNew(async () => await ExecuteCycleAsync(token), token);
}
async Task ExecuteCycleAsync(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
await LoadDataAsync(cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(timeTillNextDownload, cancellationToken);
}
}
This task will be cancelled when user logs out with the help of
if (getDataTaskCancellationTokenSource != null)
getDataTaskCancellationTokenSource.Cancel();
The property containing the result of download looks like this:
List<DataType> Data = new List<DataType>();
public IEnumerable<DataType> Data
{
get { return Data; }
set
{
Data = value.ToList();
OnDataUpdated();
}
}
void OnDataUpdated()
{
var handler = DataUpdated;
if (handler != null)
handler(this, EventArgs.Empty);
}
This part seemed to be working until I had to display the amount of data on the screen.
My MainViewModel gets instance of DataService injected with Ninject.
readonly IDataService DataService;
public MainViewModel(IDataService dataService)
{
DataService = dataService;
DataService.DataUpdated += DataService_DataUpdated;
UpdateDataCount();
}
void DataService_DataUpdated(object sender, EventArgs e)
{
UpdateDataCount();
}
void UpdateDataCount()
{
DataCount = DataService.Data.Count();
}
In xaml I've got TextBlock binded to DataCount property of MainViewModel
int DataCount;
public int DataCount
{
get { return DataCount; }
set
{
DataCount = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And here is where problem appears: OnPropertyChanged fails with
"The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))" which get's caught in DataService.LoadDataAsync().
I understand the runtime is trying to tell me I am accessing UI element from non ui thread. But am I? I thought OnPropertyChanged is the magic place which disconnects UI from the rest of background tasks.
Of course, the problem can be solved implementing OnPropertyChanged this way:
public CoreDispatcher Dispatcher { get; set; }
protected async void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
handler(this, new PropertyChangedEventArgs(propertyName));
});
}
}
But should it really be implemented this way? Or am I missing something in DataService.ExecuteCycleAsync()?
Not trying to dig any deeper, I believe your problem is this:
Task.Factory.StartNew(async () => await ExecuteCycleAsync(token), token);
Change it to simply this, see if it works as expected:
ExecuteCycleAsync(token);
Otherwise, the code inside ExecuteCycleAsync starts execution on a thread without synchronization context, which can lead to all different kinds of problems, depending on what's inside LoadDataAsync.
Note that calling ExecuteCycleAsync(token) like this is still a fire-and-forget call which may not be observing any exceptions (more here). You may want to store the Task object it returns, to be able to observe it later.

bind listbox to index C#

I dont know if it possible but what I want is something like that
In WinForm listbox1 has a list of lines(copied from file)
In another Thread and class I run on a List that contains the same lines each line I parse and DoSomething
once I finish with that line I want the index in the listbox to change
from my basic and limited understanding my approach was with an Event to fire in form and than maybe using Invoke (for not to cross thread )
Is there is a way to somehow bind to index of the listbox somehow with my for/foreach loop ?
class form
{
listBoxScript.SetSelected(ScriptCounter, true);<--bind the ScriptCounter?
}
in another Class
class RunScript
{
//..
public void RunScriptList()
{
ScriptCounter = 0 ;
foreach ( var cell in ScriptList)
{
ScriptCounter ++;
//DoSomething
}
}
}
Make sure you implement INotifyPropertyChanged in RunScript class. Here's a complete sample:
class RunScript : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int scriptCounter;
private ISynchronizeInvoke invoker;
public RunScript(ISynchronizeInvoke invoker)
{
if (invoker == null) throw new ArgumentNullException("invoker");
this.invoker = invoker;
}
public async void RunScriptList()
{
ScriptCounter = 0;
foreach (var cell in Enumerable.Range(1, 15))
{
ScriptCounter++;
await Task.Delay(1000);
//DoSomething
}
}
public int ScriptCounter
{
get { return scriptCounter; }
set
{
scriptCounter = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
Action action = () => handler(this, new PropertyChangedEventArgs(propertyName));
invoker.Invoke(action, null);
}
}
}
private RunScript rs;
public Form1()
{
InitializeComponent();
rs = new RunScript(this)
var binding = new Binding("SelectedIndex", rs, "ScriptCounter", false, DataSourceUpdateMode.OnPropertyChanged);
listBox1.DataBindings.Add(binding);
}
private void button1_Click(object sender, EventArgs e)
{
rs.RunScriptList();
}
Note I have used async/await in RunScriptList, If you do it in another thread you need to fire PropertyChanged event in main thread to avoid cross thread exception.

equivalent to silverlight busyindicator in MVC

in silverlight and MVVM , I can fire the busy indicator for example
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool _isBusy;
public bool IsBusy
{
get
{
return _isBusy;
}
set
{
_isBusy = value;
RaisePropertyChanged("IsBusy");
}
}
ObervableCollection<OperationBase> _pendingOperations = new ObervableCollection<OperationBase>();
public ViewModelBase()
{
_pendingOperations.CollectionChanged +=(s,e)=>
{
if( _pendingOperations.Count > 0)
IsBusy = true // has operation going on
else
IsBusy = false; //no operation
}
}
void LoadData()
{
LoadOperation op = Context.Load(YourQuery, lo=>
{
_pendingOperations.Remove(lo); // lo is the operation, remove it
if (lo.HasError)
{
lo.MarkErrorAsHandled();
MessageBox.Show(lo.Error.Message);
}
});
_pendingOperations.Add(op);// add operation to the collection
}
void SaveData()
{
SubmitOperation so = this.context.SubmitChanges(s =>
{
_pendingOperations.Remove(s);
if (s.HasError)
{
s.MarkErrorAsHandled();
MessageBox.Show(s.Error.Message);
}
}, null);
_pendingOperations.Add(so);// add operation to the collection }
}
...
}
I want to do the same in MVC , any idea how to achieve that for example on search , create or any long process I need to show busy indicator and close it at the end , I know there's no property changed , am wonder if ther's any way
Assumeing you mean MVC -> ASP.NET MVC or something HTML/Javascript based with some sort of web server component.
In general you would have an animated gif (A spinning wheel for example) and show hide them while you are waiting for a long running operation.
In Javascript you can take advantage of Promises or use generic callback functions.
//pseudo code
loadData(function(data){
// data came back async
// do something with data
$('#loader').hide();
});
$('#loader').show();
or with promises
//method should return promise
var promise = loadData();
$('#loader').show();
promise.done(function(data){
// do something with data
$('#loader').hide();
});
you should of course do handle the error case too but same principles apply....

INotifyPropertyChanged causes cross-thread error

Here is my scenarion:
I have a GridControl bound to a BindingList. At first what I was doing was creating a worker thread and access the BindingList directly, but this was throwing a "Cross-thread operation detected", so I followed the guide here:
http://www.devexpress.com/Support/Center/p/AK2981.aspx
By cloning the original BindingList into the worker thread and changing that one, I got the desired effect. However, I recently implemeneted the INotifyPropertyChanged into the object that is held into the BindingList, and I started getting the error again.
My guess is that the GridView is still listening to the INotifyPropertyChanged from the object.
How can I fix this?
My class:
public class Proxy : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
If you are manipulating the UI from outside of the UI thread (such as from a worker thread), then you need to rejoin the UI thread. You can do this by calling Invoke on the UI control. You can test if this is required by using InvokeRequired.
The pattern typically used is this:
public void ChangeText(string text)
{
if(this.InvokeRequired)
{
this.Invoke(new Action(() => ChangeText(text)));
}
else
{
label.Text = text;
}
}
In your case the UI is being manipulated as a result of INotifyPropertyChanged, so you need to make sure that either you always modify your entity on the UI thread (using the above technique), or use a generic asynchronous INotifyPropertyChanged helper. This is a wrapper around the item being bound. It uses the above technique to ensure the ChangeProperty event fires on the UI thread.
Here's a very crude example of a proxy for an Entity class. This ensures that the property change event rejoins the UI thread, and keeps the entity itself unmodified. Obviously you'll probably want to implement this more generically using DynamicObject for instance.
public class NotificationHelper : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly ISynchronizeInvoke invokeDelegate;
private readonly Entity entity;
public NotificationHelper(ISynchronizeInvoke invokeDelegate, Entity entity)
{
this.invokeDelegate = invokeDelegate;
this.entity = entity;
entity.PropertyChanged += OnPropertyChanged;
}
public string Name
{
get { return entity.Name; }
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
if (invokeDelegate.InvokeRequired)
{
invokeDelegate.Invoke(new PropertyChangedEventHandler(OnPropertyChanged),
new[] { sender, e });
return;
}
PropertyChanged(this, e);
}
}
}
I took a similar approach to TheGateKeeper's eventual solution. However I was binding to many different objects. So I needed something a bit more generic. The solution was to create a wrapper that implemented also ICustomTypeDescriptor. In this way, I do not need to create wrapper properties for everything that can be displayed in the UI.
public class SynchronizedNotifyPropertyChanged<T> : INotifyPropertyChanged, ICustomTypeDescriptor
where T : INotifyPropertyChanged
{
private readonly T _source;
private readonly ISynchronizeInvoke _syncObject;
public SynchronizedNotifyPropertyChanged(T source, ISynchronizeInvoke syncObject)
{
_source = source;
_syncObject = syncObject;
_source.PropertyChanged += (sender, args) => OnPropertyChanged(args.PropertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged == null) return;
var handler = PropertyChanged;
_syncObject.BeginInvoke(handler, new object[] { this, new PropertyChangedEventArgs(propertyName) });
}
public T Source { get { return _source; }}
#region ICustomTypeDescriptor
public AttributeCollection GetAttributes()
{
return new AttributeCollection(null);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(typeof(T));
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(typeof (T));
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(typeof (T));
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(typeof (T));
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(typeof(T));
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(typeof (T), editorBaseType);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(typeof(T));
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(typeof (T), attributes);
}
public PropertyDescriptorCollection GetProperties()
{
return TypeDescriptor.GetProperties(typeof (T));
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return TypeDescriptor.GetProperties(typeof(T), attributes);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return _source;
}
#endregion ICustomTypeDescriptor
}
Then in the Ui, I bind to this wrapper using something like:
private void CreateBindings()
{
if (_model == null) return;
var threadSafeModel = new SynchronizedNotifyPropertyChanged<MyViewModel>(_model, this);
directiveLabel.DataBindings.Add("Text", threadSafeModel, "DirectiveText", false, DataSourceUpdateMode.OnPropertyChanged);
}
MyViewModel has a "DirectiveText" property and implements INotifyPropertyChanged with no special consideration to threading or to the view classes.
I subclassed BindingList so I could check for a required Invoke. This way my business objects do not have a reference to the UI.
public class InvokingBindingList<T> : BindingList<T>
{
public InvokingBindingList(IList<T> list, Control control = null) : base(list)
{
this.Control = control;
}
public InvokingBindingList(Control control = null)
{
this.Control = control;
}
public Control Control { get; set; }
protected override void OnListChanged(ListChangedEventArgs e)
{
if (Control?.InvokeRequired == true)
Control.Invoke(new Action(() => base.OnListChanged(e)));
else
base.OnListChanged(e);
}
}
Just in case someone has run into the same problem... I managed to fix it after some hours. Here is what I did:
Basically the problem was that the object implementing INotifyPropertyChanged was living in a worker thread, and this causes problems when accessing the UI thread.
So what I did was pass a reference to the object that needs to be updated to the INotifyPropertyChanged object, and then use invoke on it.
Here is what it looks like:
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
//If the Proxy object is living in a non-UI thread, use invoke
if (c != null)
{
c.BeginInvoke(new Action(() => handler(this, new PropertyChangedEventArgs(name))));
}
//Otherwise update directly
else
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
//Use this to reference the object on the UI thread when there is need to
public Control C
{
set { c = value; }
}
From the thread, all I did was:
prox.c = this;
//Logic here
prox.c = null;
Hope this helps someone!!

Categories

Resources