Using Manual Reset Event till last property changes - c#

I have call back that comes as Property Changed Notifications from Firmware. Now in my code I want to wait till I hit the last property changed. I read the following post Manual Reset Event regarding ManualResetEvent but it says Manual Reset Events are used in case of multi threading. I am new to ManualResetEvents. This is my following code can I use the manual reset event in my case? if so how? If not whats the best way to wait there? Please help.
//This is some button click action of RelayCommand
private void StartCurrentRun(bool obj)
{
this.worker = new BackgroundWorker();
this.worker.WorkerReportsProgress = true;
this.worker.WorkerSupportsCancellation = true;
OriginalTime = SelectedVolumeEstimatedTime();
StartTimer();
WhenCancelledBlurVolumesGrid = false;
//this.worker.DoWork += this.DoWork;
//this.worker.ProgressChanged += this.ProgressChanged;
//this.worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
//this.worker.RunWorkerAsync();
IsLiveProgress = true;
CreateEventLogs.WriteToEventLog(string.Format("Run with Assay:{0} Volume{1} has been started", SelectedAssay, SelectedVolume), LogInformationType.Info);
var instance = ConnectToInstrument.InstrumentConnectionInstance;
instance.InitalizeRun(PopulateRespectiveVolumes());
PropertyCallBackChangedInstance.PropertyChanged += PropertyCallBackChangedInstance_PropertyChanged;
//Here I want to perform some action after I get a Processed state after the final property change event occurs.
//Can I do a manual reset event here.
}
private void PropertyCallBackChangedInstance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
try
{
if (e.PropertyName == "InstrumentStatusChanged")
{
var value = sender as InstrumentCallBackProperties;
if (value.InstrumentStatusChanged == CurrentInstrumentStatus.Busy)
{
CurrentStatus = Application.Current.TryFindResource("Wait").ToString();
}
}
if (e.PropertyName == "RunStepStatusName")
{
var value = sender as InstrumentCallBackProperties;
CurrentStatus = EnumExtensions.GetDescription(value.RunStepStatusName);
NewProgressValue += 20;
UpdateProgress = true;
}
else if (e.PropertyName == "CurrentCartridgeStatusChanged")
{
var value = sender as InstrumentCallBackProperties;
if (value.CurrentCartridgeStatusChanged == CurrentCartridgeStatus.Processed)
{
PropertyCallBackChangedInstance.PropertyChanged -= PropertyCallBackChangedInstance_PropertyChanged;
EstimatedTimeRemaining = "00:00:00";
stopWatch.Stop();
timer.Stop();
IsLiveProgress = false;
CreateEventLogs.WriteToEventLog(string.Format("Run with Assay:{0} Volume{1} has been completed", SelectedAssay, SelectedVolume), LogInformationType.Info);
if (IsRunSuccessfullyComplete != null && !WhenCancelledBlurVolumesGrid) //This indicates that Success will only open when the run is complete
{
IsRunSuccessfullyComplete();
}
WhenCancelledBlurVolumesGrid = true;
if (ClearSelections != null)
{
ClearSelections();
}
}
}
}
catch (Exception ex)
{
CreateEventLogs.WriteToEventLog(string.Format("Run with Assay:{0} Volume{1} failed", SelectedAssay, SelectedVolume), LogInformationType.Error);
}
}

It doesn't look to me like you want ManualResetEvent, which is used to signal multiple listeners. If your results were being produced by multiple tasks then you would use await WhenAll(...) however in your case the results are being informed by a property change not by a task completion. I would suggest that you simply record each property notification when it occurs, and check to see whether they are all complete. An easy way to do this is to use an enum with the [Flags] attribute: [Flags]public enum Completions { InstrumentStatusChanged, CurrentCartridgeStatusChanged, RunStepStatusName }. Just set the appropriate flag for each property callback and when you have set all the flags then you are done. If the property callbacks can occur in parallel or on different threads then you will need some form of locking such as a SemaphoreSlim(1,1).

Related

Webview2 Navigation

Currently in Webview2 browser if navigated to a particular URL as written below
browser.Source = URL;
This triggers NavigatingStarting event asynchronously.
How can I trigger a synchronous call for each source being set to trigger the event?
Problem: I am keeping a bool variable to check for the if navigation triggered inside my application and resetting it at the end for the navigatingstarting event. since it is an asynchronous call it is resetting after the first call without the next call being inside my application.
void SetBrowserUrl(Uri value)
{
m_bInsideNavigateCall = true;
priorsWebBrowser.Source = value;
}
void priorsWebBrowser_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
{
if(m_bInsideNavigateCall)
{
e.Cancel = false;
m_bInsideNavigateCall = false; // Reset for next inside call
}
else
{
e.Cancel = true;
}
}
Here the issue is if call SetBrowserUrl twice. Navigate starting cancels the second call made because it is not synchronous
I have created a list of strings.
List<String> insideNavigatingURLS; //Class level variable
Just before calling web-browser to navigate, I add the URL to list.
internalURLs.Add(uri.AbsolutePath.ToLower());
webBrowser.Source = uri;
In the NavigationStarting Event added a check to see if list contains the navigation url if it doesn't then will cancel the request.
void webBrowser_Navigating(object sender, CoreWebView2NavigationStartingEventArgs e)
{
if (!internalUrls.Contains(e.Uri))
{
e.Cancel = true;
}
else
{
internalUrls.Remove(e.Uri);
e.Cancel = false;
}
}
So when back or forward navigation is triggered, the list doesn't contain a URL and the navigation request is cancelled

How suppress multiple button clicks?

I would like to know the best way to handle an http Request on Xamarin.Forms.
For now I was handling the request this way:
First I have a button on my forms like this:
btn_1.Clicked += (sender, e) => {
Confirm(name, password);
};
My Confirm() function checks the entrees and throws the event of the request. Also it do the logic after the request event is completed. For example:
private async void Confirm(string name, string password) {
UserController user_controller = new UserController();
if (name != null && password != null) {
User user = new User(name, password);
bool ok = user_controller.Login(user);
if(ok){
Navigation.InsertPageBefore(new NextPage(), this);
await Navigation.PopAsync();
} else {
//Show error code...
}
}
}
My UserController has two functions for each http request. The first one does the request. The second one calls the first one and handles the answer.
1º:
private async Task<HttpResponseMessage> user_login(User user){
try {
Uri uri = new Uri("http://localhost/user/login");
string user_json = JsonConvert.SerializeObject(user);
StringContent content = new StringContent(user_json, Encoding.UTF8, "application/json");
return await Utilities.client.PostAsync(uri, content).ConfigureAwait(false);
} catch {
return null;
}
}
2º:
public bool Login(User user) {
http_response = user_login(user).GetAwaiter().GetResult();
//If it doesn't reach the server...
if (http_response != null) {
//Depending of the status of the response
switch (http_response.StatusCode) {
case (System.Net.HttpStatusCode)200:
try {
string content = http_response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Response response = JsonConvert.DeserializeObject<Response>(content);
return (bool) response.aux;
} catch {
}
break;
case (System.Net.HttpStatusCode)401:
...
break;
default:
...
break;
}
} else {
App.Current.MainPage.DisplayAlert("Error", "No server connection", "OK");
}
return false;
}
This completes my protocol for each request. My problem is:
1. I'm not sure if this is the correct way to do it
2. When I click several times the btn_1 it throws many times the request
How could I do to avoid this? I try to put a lock on my button but it doesn't work. I'm having many troubles with the asynchronous requests. I don't know which is the best way to handle the request to throw only one request at the time.
EDIT:
I have created this button extension:
public partial class LockableButton: Button {
public event EventHandler ThrowEvent;
public bool ShowLoading { get; set; }
public LockableSimpleButton() {
ShowLoading=false;
InitializeComponent();
this.Clicked += async (object sender,EventArgs e) => {
if (!Utilities.Unlocked) { return; }
Utilities.Unlocked=false;
try {
if (ShowLoading) {
await Navigation.PushModalAsync(new LoadingPopUp());
ThrowEvent(sender,e);
await Navigation.PopModalAsync();
} else {
ThrowEvent(sender,e);
}
} finally {
await Task.Delay(1000);
Utilities.Unlocked=true;
}
};
}
}
And now my buttons are like this:
btn_1.ThrowEvent += async (sender, e) => {
Navigation.InsertPageBefore(new Page(),this);
await Navigation.PopAsync(false);
};
How it is even posible that the error still persisting?
When I click several times the button it throws an error because it is trying to PopAsyc to many time the same page... It is the delay to short?
When I click several times the btn_1 it throws many times the request
This problem has nothing to do with handling an Async HTTP Request.
Here are two classic coding techniques for discarding extra button presses.
They are variations on having a flag set, and discarding any clicks that happen while that flag is set.
Common pseudo-code:
static bool _busy;
...click handler... {
if (_busy) return;
_busy = true;
// Special code as needed.
... handle the click ...
// This code must always be executed.
// If it isn't, the button action will never occur again.
_busy = false;
}
When you finish processing the first click, start a one-time timer. Until that timer fires, discard any additional clicks.
Pseudo-code:
...click handler... {
if (_busy) return;
_busy = true;
try {
... handle the click ...
} finally {
var timer = new Timer(TimerTick, 250 milliseconds, one-time);
timer.Start();
}
}
void TimerTick() {
// This code must always be executed.
// If it isn't, the button action will never occur again.
_busy = false;
//maybe timer.Stop();
}
When you start processing the first click, set a flag. Clear that flag when you are done processing. Discard any clicks that happen while that flag is set.
Pseudo-code:
// Must be `async` method, so events continue while processing.
// Otherwise, the second button press might simply be on hold,
// until after this finishes, so doesn't get suppressed.
...click handler... {
if (_busy) return;
_busy = true;
try {
... handle the click ...
} finally {
// This code must always be executed.
// If it isn't, the button action will never occur again.
_busy = false;
}
}

When does the BeginInvoke method runs?

I have two threads which uses the BeginInvoke method to change some Windows Form object's (Panel and Label) visibility attribute to false.The problem is that I'm not sure when the change happens. I can see that the panel is not there (so the BeginInvoke method works) but my if condition to check the visibility status always returns true the first time the form is activated.
bool notVisible = false;
private void LunchMainScreen_Activated(object sender, EventArgs e) {
String CurrentSite = "";
List<DateTime> availableDates = new List<DateTime>();
// Get available dates
Thread availableDatesThread = new Thread(delegate() {
availableDates = LunchUserPreferences.GetUserAvailableDates();
changeObjVisible(notVisible, selectAvailabilityPanel);
changeObjVisible(notVisible, whenLbl);
}
});
availableDatesThread.Start();
// Get user current site
Thread checkSiteThread = new Thread(delegate() {
CurrentSite = LunchUserPreferences.GetUserSite();
changeObjVisible(notVisible, selectSitePanel);
changeObjVisible(notVisible, whereLbl);
}
updateText(CurrentSite, CurrentSiteSetLbl);
});
checkSiteThread.Start();
while (selectSitePanel.Visible == false && selectAvailabilityPanel.Visible == false) {
// it NEVER gets here, even though the panels are NOT visible when the program loads
WhoLunchTable.Visible = false;
WhoLunchTable.SuspendLayout();
listOfAvailableGroups.Clear();
WhoLunchTable.Controls.Clear();
WhoLunchTable.RowStyles.Clear();
PopulateTable();
WhoLunchTable.Visible = true;
WhoLunchTable.ResumeLayout();
break;
}
}
private delegate void changeObjVisibleDelegate(bool visibility, object obj);
private void changeObjVisible(bool visibility, object obj) {
if (this.InvokeRequired) {
this.BeginInvoke(new changeObjVisibleDelegate(changeObjVisible), new object[] { visibility, obj });
return;
}
// downcast to the correct obj
if (obj is Panel) {
Panel panel = (Panel)obj;
panel.Visible = visibility;
}
if (obj is Label) {
Label lbl = (Label)obj;
lbl.Visible = visibility;
}
}
private delegate void updateTextDelegate(string text, Label lbl);
private void updateText(string text, Label lbl) {
if (this.InvokeRequired) {
this.BeginInvoke(new updateTextDelegate(updateText), new object[] { text, lbl });
return;
}
lbl.Text = text;
}
It does work fine when the Form is activated for the second time, for example:
The form loads for the first time and it doesn't go inside the while loop.
I minimize the form/program.
The LunchMainScreen_Activated runs again and it works as it should because it recognises that the panels are not visible.
UPDATE:
I had an idea after reading AlexF answer which solved the problem but it doesn't look like the ideal solution:
I've created a while condition that will only stop when both threads are not alive and an if condition inside it that will get this point in time and execute what I need:
while (availableDatesThread.IsAlive || checkSiteThread.IsAlive) {
// At least one thread is still alive, keeps checking it...
if (!availableDatesThread.IsAlive && !checkSiteThread.IsAlive) {
// Both threads should be dead now and the panels not visible
WhoLunchTable.Visible = false;
WhoLunchTable.SuspendLayout();
listOfAvailableGroups.Clear();
WhoLunchTable.Controls.Clear();
WhoLunchTable.RowStyles.Clear();
PopulateTable();
WhoLunchTable.Visible = true;
WhoLunchTable.ResumeLayout();
break;
}
}
Reading your code the first time the code doesn't enter in the while loop because selectSitePanel.Visible and selectAvailabilityPanel.Visible are true: this is because the availableDatesThread.Start(); and checkSiteThread.Start(); are started but not finished; those two calls are not blocking so the code continues and skips the while.
Meanwhile the two backgrund threads finishes so the second time the "Activated" event is raised the variables values are "correct" (at least for the last cycle).
Without waiting for the threads to finish you are rushing through the code before having a result for the needed value.
In other words, it's better to not use a background thread to update an interface for the use you need.
If you need you may continue to use the code the way you are using it but moving the "while" section in two separate functions: they may be called when the threads have finished their work and refresh the window in this moment and not in the "activate" event.

On ASP.NET formview when are the ModeChanged and ModeChanging events raised?

This popped up, when I was trying to find why the OnModeChanging handler wasn't being called when I called the ChangeMode event of my formview.
On the formview's ChangeMode method MSDN page , it is stated that it:
switches the FormView control to the specified data-entry mode
but also that:
the ModeChanged and ModeChanging events are not raised when this method is called
And in the ModeChanged and ModeChanging events pages, it says that they occur:
when the FormView control switches between edit, insert, and read-only mode
after/before the mode changes, respectively.
Can you explain it to me: when are the ModeChanged/ing events raised?
And, is there a way to force these events to be raised?
I think I know why now. I've found an answer in other forum, and though I didn't find the code of FormView, I've found a DetailsView implementation and I think in this case it might be similar.
Basically what I've understood of it, is that the ModeChanged/ing events are raised when command buttons are clicked (Cancel, Edit, Insert, New and Update), i.e. when one doesn't have direct control over these events, and when we use the ChangeMode method, we know that the mode has changed (or will be changed) and it would make no sense of raising an event..
DetailsView ChangeMode:
public void ChangeMode(DetailsViewMode newMode) {
Mode = newMode;
}
DetailsView command handlers:
private void HandleCancel() {
bool isBoundToDataSourceControl = IsBoundUsingDataSourceID;
DetailsViewModeEventArgs e = new DetailsViewModeEventArgs(DefaultMode, true);
OnModeChanging(e);
if (e.Cancel) {
return;
}
if (isBoundToDataSourceControl) {
Mode = e.NewMode;
OnModeChanged(EventArgs.Empty);
}
RequiresDataBinding = true;
}
private void HandleEdit() {
if (PageIndex < 0) {
return;
}
DetailsViewModeEventArgs e = new DetailsViewModeEventArgs(DetailsViewMode.Edit, false);
OnModeChanging(e);
if (e.Cancel) {
return;
}
if (IsBoundUsingDataSourceID) {
Mode = e.NewMode;
OnModeChanged(EventArgs.Empty);
}
RequiresDataBinding = true;
}
private bool HandleInsertCallback(int affectedRows, Exception ex) {
DetailsViewInsertedEventArgs dea = new DetailsViewInsertedEventArgs(affectedRows, ex);
dea.SetValues(_insertValues);
OnItemInserted(dea);
_insertValues = null;
if (ex != null && !dea.ExceptionHandled) {
if (PageIsValidAfterModelException()) {
return false;
}
dea.KeepInInsertMode = true;
}
if (!dea.KeepInInsertMode) {
DetailsViewModeEventArgs eMode = new DetailsViewModeEventArgs(DefaultMode, false);
OnModeChanging(eMode);
if (!eMode.Cancel) {
Mode = eMode.NewMode;
OnModeChanged(EventArgs.Empty);
RequiresDataBinding = true;
}
}
return true;
}
private void HandleNew() {
DetailsViewModeEventArgs e = new DetailsViewModeEventArgs(DetailsViewMode.Insert, false);
OnModeChanging(e);
if (e.Cancel) {
return;
}
if (IsBoundUsingDataSourceID) {
Mode = e.NewMode;
OnModeChanged(EventArgs.Empty);
}
RequiresDataBinding = true;
}
private bool HandleUpdateCallback(int affectedRows, Exception ex) {
DetailsViewUpdatedEventArgs dea = new DetailsViewUpdatedEventArgs(affectedRows, ex);
dea.SetOldValues(_updateOldValues);
dea.SetNewValues(_updateNewValues);
dea.SetKeys(_updateKeys);
OnItemUpdated(dea);
_updateKeys = null;
_updateOldValues = null;
_updateNewValues = null;
if (ex != null && !dea.ExceptionHandled) {
if (PageIsValidAfterModelException()) {
return false;
}
dea.KeepInEditMode = true;
}
if (!dea.KeepInEditMode) {
DetailsViewModeEventArgs eMode = new DetailsViewModeEventArgs(DefaultMode, false);
OnModeChanging(eMode);
if (!eMode.Cancel) {
Mode = eMode.NewMode;
OnModeChanged(EventArgs.Empty);
RequiresDataBinding = true;
}
}
return true;
}
With ChangeMode you are choosing that the control switch to one of it's modes.
When it starts to performing this task, the ModeChanging event is raised (to indicate that it's in progress) (optionally do something here).
Once that task is completed, it raises the ModeChanged event (to indicate that it's done) (optionally do something here).
[Updated]
I see your point. How could you consume the events if they don't get raised.
I'm going to guess at it, that they don't get raised initially because of nothing to do, just perform the changing of the mode.
In either case I guess, it's more of a state change than the raising of events.
[Updated]
I think what we are both saying is that if no one has subscribed to the event (i.e., no one is listening for it), there's no point in raising it.

C# Workflow ManualWorkflowSchedulerService Multithreading Question

Hey guys, I just wanted to verify what I'm doing is correct. It came to our attention that a Windows Service had a pretty serious memory leak. I was able to track it down to how Workflow was being called. I reworked it a bit to stop the memory leak, but I wanted to validate that the code is doing what I think it is. Note I do not know the first thing about Workflow, so I'm coming to you.
Basically, the code was executing the Workflow on a thread, but was not removing the handler to WorkflowRuntime.Terminated. I am trying to ensure that the Workflow is executed asynchronously. Here are the relevant portions of code:
Checking to ensure there is only once instance of the WorkflowRuntime:
private static void _CheckRuntimeInstance()
{
lock (_padlock)
{
if (_wfRuntime == null)
{
_wfRuntime = new WorkflowRuntime();
ManualWorkflowSchedulerService schedulerService = new ManualWorkflowSchedulerService();
_wfRuntime.AddService(schedulerService);
_wfRuntime.StartRuntime();
}
}
}
Inside a static method, creating the specific WorkflowInstance to run:
_CheckRuntimeInstance();
// create the instance
WorkflowInstance instance = _wfRuntime.CreateWorkflow(typeof(WorkflowType),parameters);
instance.Start();
Guid instanceId = instance.InstanceId;
ThreadPool.QueueUserWorkItem(CallbackMethod, instanceId);
Thread callback method:
private static void DeviceLocationAssignmentCallback(Object state)
{
Guid instanceId = (Guid)state;
EventHandler<WorkflowTerminatedEventArgs> workflowTerminatedHandler = null;
EventHandler<WorkflowCompletedEventArgs> workflowCompletedHandler = null;
workflowTerminatedHandler = delegate(object sender, WorkflowTerminatedEventArgs e)
{
if (instanceId == e.WorkflowInstance.InstanceId)
{
// Remove event registration.
_wfRuntime.WorkflowTerminated -= workflowTerminatedHandler;
_wfRuntime.WorkflowCompleted -= workflowCompletedHandler;
if (e.Exception != null)
{
// Log error.
}
}
};
_wfRuntime.WorkflowTerminated += workflowTerminatedHandler;
workflowCompletedHandler = delegate(object sender, WorkflowCompletedEventArgs e)
{
if (instanceId == e.WorkflowInstance.InstanceId)
{
// Remove event registrations.
_wfRuntime.WorkflowTerminated -= workflowTerminatedHandler;
_wfRuntime.WorkflowCompleted -= workflowCompletedHandler;
}
};
_wfRuntime.WorkflowCompleted += workflowCompletedHandler;
_wfRuntime.GetService<ManualWorkflowSchedulerService>().RunWorkflow(instanceId);
}
EDIT: Changed the title of the post to get more views.

Categories

Resources