Winforms, control only working with breakpoint - c#

I created a custom Toast Notification for my Winforms application, whenever a custom result is returned, I use a ShowMessage extension to display the notification.
On my login screen, I have a unlock application button that will show the toaster message whenever the password is incorrect.
When I put a breakpoint right before the result.ShowMessage(); the notification appears. But when I remove the breakpoint, it does not appear anymore. I do not change any debug values.
How can I resolve this? I did try implementing a thread.sleep but it did not change anything, the notification only appears when I insert a breakpoint and continue over it.
It is worth noting that the notification works perfectly everywhere else in the application.
Code for the Unlock Application:
private void UnlockApplication()
{
var result = new Business.Server.User().Get(_loginModel.UserName, _loginModel.Password.Encrypt());
if (result.IsSuccessful)
{
// Perform log in process
}
else
{
result.ShowMessage(); //Only works when I put breakpoint here
}
}
Code for ShowMessage extension
public static void ShowMessage<T>(this Models.Result<T> result) where T : class
{
Helpers.ToasterNotificationHelper.ShowNotification(result.ResultTypeKey.ToDescription(), result.Message, result.ResultImage(), result.ResultColor(), result.ResultTypeKey == Enums.ResultTypeEnum.Warning ? 5000 : 2500);
}
public static void ShowNotification(string header, string message, Image icon, Color backgroundColor, int durationInMilliseconds = 1000)
{
if (Application.OpenForms[0] is MyApplication)
{
var toasterNotification = new ToasterNotificationControl(header, message, icon, backgroundColor);
(Application.OpenForms[0] as MyApplication).toastNotificationCollectionControl1.AddNotification(toasterNotification, durationInMilliseconds);
}
}
My Toast Notification control is a DevExpress flyout panel control that gets added to the Main application form

Your "Get(_loginModel.UserName ..." seems to be asynchronous. Try to do the next to make the call awaitable:
private async System.Threading.Tasks.Task UnlockApplicationAsync()
{
var result = await new Business.Server.User().Get(_loginModel.UserName, _loginModel.Password.Encrypt());
...

Related

How to get notifications to appear right after page loads? (Xamarin, LocalNotifications Plugin)

I have a project that needs notifications about upcoming events to show when the user opens the main page, and I'm having problems with async and await (I think). I am using the LocalNotifications Nuget plugin, because that's the only one I was allowed to use for this project. I have some methods that are intended to check for and display notifications. I have tried loading them from my App file, My MainPage.Xaml.cs's OnAppearing method, and from my MainPageViewModel.cs, and the closest that I've gotten to something working is with a combination of creating the Notifications as a class, adding them from the ViewModel and displaying them in the OnAppearing Method of the code behind. The code works right up until the if statement's body in the OnAppearing Method tries to execute and then it just stops and continues on with displaying the main page, but never showing the notifications. I am really new to using asynchronous programming, as well as Xamarin, so I'm pretty confused. This is my relevant code.
//Code starts in MainPageViewModel.cs
public MainPageViewModel()
{
Task.Run(async() => await CheckNotifications());
}
private async Task CheckNotifications()
{
await DatabaseService.Init();
var eventList = await DatabaseService.db.Table<Event>().ToListAsync();
if (eventList.Count > 0)
{
foreach (Event event in eventList)
{
if (event.NotifyStartDate && (event.StartDate.Date == DateTime.Today))
{
Notifications notification = new Notifications
{
Name = $"{event.Name}",
NotifyDate = event.StartDate,
Occurrence = "Starting"
};
await DatabaseService.AddNotification(notification);
}
if (event.NotifyEndDate && (event.EndDate.Date == DateTime.Today))
{
Notifications notification = new Notifications
{
Name = $"{event.Name}",
NotifyDate = event.EndDate,
Occurrence = "Ending"
};
await DatabaseService.AddNotification(notification);
}
}
}
}
This all seems to work fine, it creates a Notification with the event that starts today and adds it to the Notifications Table in the database. Next it moves to my MainPage Code Behind's OnAppearing method--> which is an async void method and I can't figure out any other way to do this, because I get an error that "MainPage.OnAppearing() return type must be void" if I try to make it a Task.
protected override async void OnAppearing()
{
//List fills fine here and holds my Test Event starting today information with a count of 1
var notifications = await DatabaseService.db.Table<Notifications>().ToListAsync();
if (notifications.Count > 0)
{
foreach(Notifications notification in notifications)
{
CrossLocalNotifications.Current.Show("Reminder", $"Event {notification.Name} is {notification.Occurrence} Today" +
$"on {notification.NotifyDate}");
//code stops at this line and continues the rest of the program without executing notification
}
}
base.OnAppearing();
}
My Init, FillSampleData, and AddNotifications methods in the database are all async tasks, and they all await other methods, but the problem seems to occur at OnAppearing. To add to that I am Navigating to the Main Page from App.Xaml.cs Synchrounously, because when I tried to make it an asynchronous method it got ignored there.
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
Everywhere else in my app the navigation awaits. I don't know if I'm just doing the notifications all wrong, if I'm just doing Asynchronous programming all wrong, or a little bit of both. Any help would be much appreciated. Thanks guys.

Public method only runs in the "log" but not with labels, buttons etc

I have a masterdetailpage and a contentpage that works as a rootpage, so to make a function run from my masterdetailpage i load it via a public method. But when I do it, it works when I type something in the log but when I try to make something real happen, for example change the text of a label or make something bigger etc. It doesn't run. Even though the System.Diagnostics.Debug inside the method runs. My code looks like this:
My masterdetailpage:
knappen.Clicked += (object sender, EventArgs e) => {
var ourStartPage = new StartPage ();
ourStartPage.InitThisPage();
}
And this is my contentpage (StartPage) with the public method that loads my particular function i want loaded:
public void InitThisPage()
{
LoadData();
}
async void LoadData()
{
label.TextColor = Color.Red; //so this does not change color
System.Diagnostics.Debug.WriteLine ("Does it reach this?"); //but it reaches this and types this out in the log.
}
So above, it reaches the thing I type in the log but the Color of the label does not change. I have tried different labels, buttons, images etc but whatever I do beside run it in the log, does not seem to run.
What can the issue be here?
Try updating the label's color on the main UI thread:
InvokeOnMainThread ( () => {
label.TextColor = Color.Red; //so this does not change color
});
Ref: Working with the UI Thread

Prevent single click on WPF control when double click occured

I have Image control with PreviewMouseLeftButtonDown event handled.
The logic is to change image content when single click occured and to activate other visual style when double click occured.
I know about ClickCount property like some answers said (e.g. this) and successfully distinguish between single/double clicks, but problem is that single click occures always, wherether double click follow or not follow the next moment (which is fair enought, anyway). So, with double click both actions processed - for single click and the next moment for double click.
The question: is there any method to prevent single click before occuring right after that double click, other than handling this situation with some kind of timers magic?
Edit:
I found question with good comment, which makes an analogy with windows explorer - how it wait after single click on selected file, and start renaming just ensured that no other click occured after first click.
Delay will definitely exist in purpose to solve this problem, but does it mean that windows explorer using exactly timer, or maybe it have some other option (some property or event that can be awaited) to hold single click in case double click occured?
Finally there were no suggestions received with timer-unrelated solution (and I didn't find any either), so here is simple example how to prevent single click when double click occurred.
Xaml:
<Window x:Class="StackOverflow.DoubleClickExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="100" Width="150"
MouseDown="RootElement_OnMouseDown">
</Window>
Code-behind:
namespace StackOverflow.DoubleClickExample
{
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
public static extern uint GetDoubleClickTime();
public MainWindow()
{
this.InitializeComponent();
}
private Guid lastGuid = Guid.Empty;
private void RootElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
{
// Create new unique id and save it into field.
var guid = Guid.NewGuid();
this.lastGuid = guid;
// Run task asynchronously for ensuring that there is no another click
// happened in time interval when double-click can occure.
Task.Run(async () =>
{
// Wait system double-click time interval.
await Task.Delay((int)GetDoubleClickTime());
// If no double-click occured in awaited time interval, then
// last saved id (saved when first click occured) will be unchanged.
if (guid == this.lastGuid)
{
// Here is any logic for single-click handling.
Trace.WriteLine("Single-click occured");
}
});
return;
}
// Can be here only when e.ClickCount > 1, so must change last saved unique id.
// After that, asynchronously running task (for single-click) will detect
// that id was changed and so will NOT run single-click logic.
this.lastGuid = Guid.NewGuid();
// Here is any logic for double-click handling.
Trace.WriteLine("Double-click occured");
}
}
}
For testing, make clicks in window area and track messages writing into output window in visual studio (menu View -> Output).
Another way is using CancellationTokenSource and trigger its Cancel method when double-click occured. Just replace lastGuid field and RootElement_OnMouseDown method:
private CancellationTokenSource cancellationTokenSource;
private void RootElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
{
try
{
this.cancellationTokenSource = new CancellationTokenSource();
var token = this.cancellationTokenSource.Token;
// Run task asynchronously for ensuring that there is no another click
// happened in time interval when double-click can occure.
Task.Run(async () =>
{
// Wait system double-click time interval.
await Task.Delay((int)GetDoubleClickTime(), token);
// Here is any logic for single-click handling.
Trace.WriteLine("Single-click occured");
}, token);
}
catch (OperationCanceledException)
{
// This exception always occure when task is cancelled.
// It happening by design, just ignore it.
}
return;
}
// Cancel single-click task.
if (this.cancellationTokenSource != null)
{
this.cancellationTokenSource.Cancel();
}
// Here is any logic for double-click handling.
Trace.WriteLine("Double-click occured");
}
I guess you need to use a timer. For getting the max time that is valid for a double click still to occur you could use following function (tested; output is 500 ms):
[DllImport("user32.dll")]
static extern uint GetDoubleClickTime();
(source: how to get the double-click-time in WPF)
Usually when you have several values you want to bind to one WPF control you use something like ItemsSource and bind it to a List in view model. But I guess that doesn't work for Image control. So you should go with a timer and use the value of the function above for your timer.

Prepare data before go to the main view

When the user touch the app icon,
I want do these steps before user go to the main view
Fetch json string from URI
Use JArray.Parse to get the value
After all finish, go to the main view.
The problem is how can I prevent user to go to the main view
and put all the code
I tried to put it in Application_Launching method in the App.xaml.cs file
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
// code here
}
But it doesn't prevent the program to go to the main view before the fetching finished.
And I found that actually in the MainPage.xaml, if I put this code like this
protected override void OnNavigatedTo(NavigationEventArgs e)
{
while(true) {}
// it will prevent the program to go to the main view,
// and will stick with the loading screen until this function reach its end
}
So I think, I can put the all the code here, when I finish the fetch, I will just break the while and it will go to the main view automatically.
And I try, this is the code
protected override void OnNavigatedTo(NavigationEventArgs e)
{
bool isFetchFinished = false;
ObservableCollection<PromoViewModel> Promos = new ObservableCollection<PromoViewModel>();
WebClient client = new WebClient();
client.DownloadStringCompleted += (s, evt) =>
{
if (evt.Error == null)
{
// Retrieve the JSON
string jsonString = evt.Result;
JArray promos = JArray.Parse(jsonString);
foreach (JObject promo in promos)
{
string name = promo["name"].Value<string>();
string description = promo["description"].Value<string>();
string img = promo["image"].Value<string>();
Promos.Add(new PromoViewModel() { Name = name, Description = description, Img = img });
}
isFetchFinished = true;
System.Diagnostics.Debug.WriteLine("finish fetch");
}
};
// run
client.DownloadStringAsync(new Uri("the json url"));
while(true) {
if(isFetchFinished) {
App.ViewModel.LoadData(Promos); // pass value to main view model
break; // after complete, break
}
}
}
I thought it would work, but it was not.
This is what I found,
The WebClient DownloadStringAsync won't run until the OnNavigatedTo function finished.
Because it's still waiting for the while loop to break and reach the end function.
And this
isFetchFinished = true; // will never executed
Resulting infinite loop.
I think I put the fetch code in the wrong method. Where is the right place to put all of this?
Ouch, you are doing it all wrong. First of all, you have to specify the starting page. If you want to download some data before navigating to it, you can create a special "download" page that is actually the first page navigated to when starting the application. And then, once the download is completed, you navigate to your main page. This is actually a replacement for the extended splash screen.
Also, never put while (true) in any UI code, that will simply freeze the application. Besides, if the application is frozen, you never get the chance to "unfreeze" it.

MediaElement - can't change sources from outside Silverlight

I'm making a Silverlight application and I'm using a MediaElement to play a video from the user's disk that I know the path of (say, "C:/foo.MOV"). I'd like a Javascript trigger from the browser to change the source of the MediaElement to another known file (eg "C:/bar.MOV"). I can make a button to do this in Silverlight, and I can have a Javascript trigger execute code inside the Silverlight app, but when I do, the MediaElement appears empty.
I've even tried having the Javascript call the btnLoadNewMediaTest_Click event, and while that event works fine called from user clicks on the button, it doesn't affect the media at all when called from outside the app.
Looking at the MediaElement in the debug, it seems that when it's called from the Javascript the MediaElement's Source appears as null and it seems to have made an empty copy.
I can confirm the Javascript is triggering the events in Silverlight, as it trips breakpoints in the Silverlight code.
I have managed to solve this: I created an EntryPoint class that is scriptable from JavaScript. When the JavaScript sendCommand is triggered, it puts a command and args into a queue held by the entry point. Every tick of a timer in the Silverlight app, the app checks the Count() of the queue and gets any commands and processes them.
From the Javascript, I call silverlightControl.Context.EntryPoint.setCommand("commandname", "args").
In the EntryPoint I have
[ScriptableMember()]
public string setCommand(string commandValue, string argsValue)
{
commands.Enqueue(commandValue);
args.Enqueue(argsValue);
commandWaitingFlag = true;
return Application.Current.HasElevatedPermissions.ToString();
}
In the Silverlight itself, I have a DispatcherTimer with an interval of 100ms. This has a tick event:
public void Each_Tick(object o, EventArgs e)
{
//Other code
if (entryPoint.commandWaitingFlag)
{
handleEntryPointCommands();
}
}
From inside handleEntryPointCommands I call a method of the entryPoint, getCommand():
public string[] getCommand() {
string commandOut = string.Empty;
string argsOut = string.Empty;
if (commands.Count > 0)
{
commandOut = commands.Dequeue();
argsOut = args.Dequeue();
if (commands.Count == 0)
{
commandWaitingFlag = false;
}
return new string[2] { commandOut, argsOut };
}
else
{
return new string[2];
}
}
and then can use the command I've gotten however I like. Hopefully that's more helpful with some code.

Categories