Check/Solve cross thread operation - c#

There is a StatusProgressBar, which is often accessed from threads. To ensure this, its Text property is as follow:-
[Browsable(true)]
public override string Text
{
get
{
return prg.Text ;
}
set
{
prg.Text = value;
}
}
And the prg.Text = value looks like this
public override string Text
{
get
{
return base.Text;
}
set
{
Misc.CrossThread(this , delegate
{
base.Text = value;
}, true);
}
public static void CrossThread(Control control, MethodInvoker d, bool forceSynchronous)
{
if (control.InvokeRequired)
{
if (forceSynchronous)
{
control.Invoke((Action)delegate { CrossThread(control, d, forceSynchronous); });
}
else
{
control.BeginInvoke((Action)delegate { CrossThread(control, d, forceSynchronous); });
}
}
else
{
if (control.IsDisposed)
{
throw new ObjectDisposedException("Control is already disposed.");
}
d();
}
}
Now the problem is, when its accessed from UI thread (sometimes), the text doesn't change..
ex.
if (cbGateway.SelectedIndex == -1)
{ bp.Text = "Select GATEWAY"; return; }
here 'bp' refers to StatusProgressBar object.
Now if I put a breakpoint at
{ bp.Text = "Select GATEWAY"; return; }
and then continue, everything happens as expected, text changes..
Why the text isn't changed first time?

If you access the progressbar from the UI thread during a long running task, the progressbar will be not refreshed until that long running task is finished.

Did you try doing the following in your CrossThread method?
Application.DoEvents();

Related

Unity wrong instance of class is being called on click. Creating my own input manager.

I am making my own InputManager to remap keys during the game.
The problem is that I have assigned class to each of shortcut's parent and when the button is pressed on the debugger I can see data from another key.
And only this key keeps being changed over and over again.
Here is my example: Up is the parent of button, UpS is button being pressed which call the method from it's parent. In each parent-children it is set the same way as there.
On button press I call
public void ToggleChangeButtonPannel(Button button)
{
this.ActionText.text = Description;
this.CurrentKeyText.text = Name;
if (Panel.enabled)
{
Panel.enabled = false;
}
else
{
Panel.enabled = true;
}
}
And at update I check if panel is visible. I think the problem might be with the specification of Update() method. Does it work on every instance in parallel?
If that is the case - is it possible to use Input.anyKeyDown outside the Update() method?
private void Update()
{
if (!Panel.enabled)
{
return;
}
if (Input.anyKeyDown)
{
var currentKey = Key;
try
{
var newKey = (KeyCode)System.Enum.Parse(typeof(KeyCode), Input.inputString.ToUpper());
if (Shortcuts.TryChangeKeyCode(currentKey, newKey))
{
RenameButtons(newKey);
}
else
{
//Error message handling
}
}
catch
{
//Error message handling
}
}
}
The problem was as I thought with Update() method. I have fixed it by using caroutine.
public void ToggleChangeButtonPannel(Button button)
{
ActionText.text = Description;
CurrentKeyText.text = Name;
if (Panel.enabled)
{
Panel.enabled = false;
}
else
{
Panel.enabled = true;
StartCoroutine(KeyRoutine());
}
EventSystem.current.SetSelectedGameObject(null);
}
IEnumerator KeyRoutine()
{
while (!Panel.enabled || !Input.anyKeyDown)
{
yield return null;
}
try
{
var newKey = (KeyCode)System.Enum.Parse(typeof(KeyCode), Input.inputString.ToUpper());
if (Shortcuts.TryChangeKeyCode(Key, newKey))
{
RenameButtons(newKey);
Key = newKey;
Panel.enabled = false;
}
else
{
StartCoroutine(RemoveAfterSeconds(3, Panel));
}
}
catch
{
StartCoroutine(RemoveAfterSeconds(3, Panel));
}
}

Change data in Class when List<sub-class> data is changed

I have a class STimer that has a List in it. The serviceDetail is monitored on a timer very often, and rarely changes, but when it does change I want to get the fact that the data changed without looping through the list due to processing power. Maybe this is a duplicate question that I just don't know how to search for it, but I have been trying. Here is a code Sample:
class STimers
{
public class ServiceDetail
{
private int _serviceKey;
private bool _isRunning = true;
private bool _runningStateChanged = false;
public bool isRunning
{
get { return _isRunning; }
set
{
//Check to see if the data is the same, if so, don't change, if not, change and flag as changed
if(_isRunning = value) { return; }
else
{
_isRunning = value;
_runningStateChanged = true;
<-- Update STimers._dataChanged to true -->
}
}
}
}
public List<ServiceDetail> _serviceMonitors = new List<ServiceDetail>();
public bool _dataChanged = false;
}
I could do a .Find on the list to return all of the _serviceMonitors._runningStateChanged=true, but that seems like a lot of work parsing the List every time the timer fires, when likely only 1 out of 1,000 loops will actually have a change.
Is this even possible, or do I need to move the check for changes out of the class?
You could get this by adding an event to your ServiceDetail class
public class ServiceDetail
{
public event EventHandler<ListChangedEventArgs> ListChanged;
private int _serviceKey;
private bool _isRunning = true;
private bool _runningStateChanged = false;
private void OnListChanged(ListChangedEventArgs e){
if (ListChanged != null) ListChanged(this, e);
}
public bool isRunning
{
get { return _isRunning; }
set
{
//Check to see if the data is the same, if so, don't change, if not, change and flag as changed
if(_isRunning = value) { return; }
else
{
_isRunning = value;
_runningStateChanged = true;
OnListChanged(new ListChangedEventArgs(this));
<-- Update STimers._dataChanged to true -->
}
}
}
}
And define your ListChangedEventArgs class like this
public class ListChangedEventArgs:EventArgs
{
public ServiceDetail serviceDetail { get; set; }
public ListChangedEventArgs(ServiceDetail s)
{
serviceDetail = s;
}
}
And then register to the event for each servicedetail added to the list
s.ListChanged += (sender, args) => YourFunction();
Hope it helps

View not updating when property is changed by event from another thread

I am trying to run something like a background thread and notify a ViewModel of the status of this background thread by raising an event. The ViewModel in turn raises the OnPropertyChanged event. Unfortunately the corresponding view will not update.
The part of the background thread where I notify the ViewModel can be seen here:
private void RunThread() {
while(true) {
suspendEvent.WaitOne(Timeout.Infinite);
if (shutDownEvent.WaitOne(0)) {
break;
}
if (pauseEvent.WaitOne(0)) {
pauseEvent.Reset();
}
Notify("Cleaning started!");
pauseEvent.WaitOne(TimeSpan.FromSeconds(5));
Clean();
Notify("Cleaning finished!");
pauseEvent.WaitOne(TimeSpan.FromSeconds(5));
}
}
private void Notify( String status ) {
NotifyOfCleanerStatusHandler handler = NotifyOfcleanerStatus;
if (handler != null) {
handler(status);
}
}
The part of the ViewModel where I receive the event can be seen here:
public void SetCleanerStatus( String status ) {
CleanerStatus = status;
}
And finally, the property that I bind my view to is seen here:
public String CleanerStatus {
get {
return cleanerStatus;
}
set {
if (value != null) {
cleanerStatus = value;
OnPropertyChanged("CleanerStatus");
}
}
}
The intriguing thing is: The View will show one of the above statuses, either "Cleaning started!" or "Cleaning finished!". One would think that they should alternate in an interval of 5 seconds. This is not the case.
If I remove the first waithandle call (pauseEvent.WaitOne(TimeSpan.FromSeconds(5));) the message in the view is "Cleaning finished" and stays that way. If i keep the line, the message is "Cleaning started!" and stays that way. Debugging shows that the line OnPropertyChanged(..) is reached.
What am I doing wrong?
Simplify your RunThread method:
private ManualResetEvent suspendEvent = new ManualResetEvent(false);
private bool shutdown;
private void RunThread()
{
while (!shutdown)
{
suspendEvent.WaitOne();
if (shutdown)
{
break;
}
Notify("Cleaning started!");
Thread.Sleep(TimeSpan.FromSeconds(5));
Notify("Cleaning finished!");
Thread.Sleep(TimeSpan.FromSeconds(5));
}
}
When updating the UI it might also be necessary to invoke the PropertyChanged handler in the UI thread:
public string CleanerStatus
{
get { return cleanerStatus; }
set
{
cleanerStatus = value;
App.Current.Dispatcher.BeginInvoke(
new Action(() => OnPropertyChanged("CleanerStatus")));
}
}

Application crashes when I switch to a different XAML

Hi I'm a novice in writing C# applications.
Sorry if its too basic.
I have a thread running in Main xaml which queries for some information and updates a property.
So once I detect that property is set to "X" I need to switch to a different XAML view.
The problem I'm facing is when I invoke the switch from the Property, my app crashes.
I think it has to do with the thread..
Qn : How do I switch to a different XAML view as soon as I detect the property value changed?
Sample Code :
public partial class MainWindow : Window
{
....
private Thread t;
public static enState dummy;
public enState SetSTATE
{
get
{
return dummy;
}
set
{
dummy = value;
if (dummy == A )
{
var NEWVIEW = new VIEW1();
contentGrid.Children.Add(NEWVIEW); // - crashes in this block
}
}
}
public void startThread()
{
t = new Thread(getInfo);
t.Isbackground = true;
t.start();
}
public void getInfo()
{
while (true)
{
int x = somefunc();
if (x == conditon)
{
SetSTATE = A;
}
Thread.Sleep(1000);
}
}
MainWindow() { startThread(); }
}
public partial class NEWVIEW: UserControl
You can't modify a collection from a background thread. You'll need to explicitly use Dispatcher.BeginInvoke to make the modification:
if (dummy == A )
{
contentGrid.Dispatcher.BeginInvoke(new Action(() =>
{
var NEWVIEW = new VIEW1();
contentGrid.Children.Add(NEWVIEW);
}));
}

Pass an INotifyProperty in a class so that the changes propagate back

I've implemented a class that takes care of loading objects asynchronously and takes care of changing the cursor accordingly, namely in the UpdateCursor method:
static Cursor cursor;
public AsyncLoader(Func<CbT> request, Callback callback, Cursor cursor)
{
this.request = request;
this.callback = callback;
AsyncLoader<CbT>.cursor = cursor;
LatestRequestId = Guid.NewGuid();
UpdateCursor();
...
}
void UpdateCursor()
{
if (LatestRequestId == Guid.Empty)
{
cursor = Cursors.Arrow;
}
else
{
cursor = Cursors.AppStarting;
}
}
In the class where I'm going to use this class I have the Cursor property which implements INotifyProperty and it's bound to the window's cursor:
private Cursor _CurrentCursor;
public Cursor CurrentCursor
{
get { return _CurrentCursor; }
set
{
if (value != _CurrentCursor)
{
_CurrentCursor = value;
OnPropertyChanged("CurrentCursor");
}
}
}
In the View:
Cursor="{Binding CurrentCursor}"
The question is, how can I pass the CurrentCursor to the AsyncLoader, so that when the UpdateCursor runs, the changes will be reflected back to the CurrentCursor and fire the PopertyChange event?
One of the solution would be to create an interface
interface ICursorHolder{
Cursor CurrentCursor{get;set;}
}
Pass instance of type ICursorHolder to the AsyncLoader and to implement this interface in your class with Cursor property
public AsyncLoader(Func<CbT> request, Callback callback, ICursorHolder cursorHolder)
{
this.request = request;
this.callback = callback;
AsyncLoader<CbT>.cursorHolder = cursorHolder;//change according to your logic
LatestRequestId = Guid.NewGuid();
UpdateCursor();
...
}
void UpdateCursor()
{
if (LatestRequestId == Guid.Empty)
{
cursorHolder.Cursor = Cursors.Arrow;
}
else
{
cursorHolder.Cursor = Cursors.AppStarting;
}
}
and your class
public class ShellPresenter: PresenterBase, ICursorHolder{
private Cursor _CurrentCursor;
public Cursor CurrentCursor
{
get { return _CurrentCursor; }
set {
if (value != _CurrentCursor){
_CurrentCursor = value;
OnPropertyChanged("CurrentCursor");
}
}
}
}
Upd1
Updated sample code
Upd2
private void CallAsync(){
ShellPresenter shell=GetInstance();
Loader.AsyncLoader(request,callback, shell);
}

Categories

Resources