OnAfterRender or OnInitializedAsync function to refresh data? - timer

I'd like to refresh my data each minute. for this, I use a timer.
`
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
//Configuration des graphiques
Alert.Info("OnInitializedAsync");
timer = new System.Threading.Timer(async (object? stateInfo) =>
{
loading = true;
GetDataAPI();
}, new System.Threading.AutoResetEvent(false), 2000, 2000);
}
`
this work fine, but when I load the page for the fisrt time, It spend a long time before to load data. when I delete the Time it's very faster.
so my question, is it in the OnInitializedAsync that I use the timer ? I've read a lot of documention on the cycle but don't really see the difference between OnAfterRender or OnInitializedAsync.
should I load data the first time in OnAfterRender with FirstRender ? and then the timer in OnInitializedAsync ?
thanks for your help.

You can break out the timer into a separate class with an event to drive updates:
public class MyTimer
{
private System.Timers.Timer _timer = new System.Timers.Timer();
public event EventHandler<ElapsedEventArgs>? TimerElapsed;
public MyTimer(double period)
=> SetTimer(period);
private void SetTimer(double period)
{
_timer = new System.Timers.Timer(period);
_timer.Elapsed += OnTimedEvent;
_timer.AutoReset = true;
_timer.Enabled = true;
}
private void OnTimedEvent(object? source, ElapsedEventArgs e)
=> this.TimerElapsed?.Invoke(this, e);
}
And then you can use it like this. I've added a simple message that is updated every 5 seconds to demo getting new data. Note there's no delay on the initial load.
#page "/"
#implements IDisposable
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<div class="alert alert-info">
#message
</div>
#code {
private MyTimer? timer;
private string message = DateTime.Now.ToLongTimeString();
private bool isGettingData;
protected async override Task OnInitializedAsync()
{
await this.GetDataAsync();
// Set for every 5 seconds
timer = new(5000);
timer.TimerElapsed += this.OnTimeElapsed;
}
private async void OnTimeElapsed(object? sender, EventArgs e)
{
await this.GetDataAsync();
// Update the UI
await this.InvokeAsync(StateHasChanged);
}
private async ValueTask GetDataAsync()
{
// Only get the data again if we finished the last get
if (isGettingData)
return;
isGettingData = true;
// emulate async fetching data
await Task.Delay(100);
message = DateTime.Now.ToLongTimeString();
isGettingData = false;
}
public void Dispose()
{
if (timer is not null)
timer.TimerElapsed -= this.OnTimeElapsed;
timer = null;
}
}

Related

Due to the use of Dispatcher,t he window still locks

I have a program similar to chatbot in Wpf.
I have a stack where I create the user controls I have and enter them.
I have to use Net3.5 .
The response from the server is delayed.
The problem I have is when I type and send the textbox the server does not answer,
I can not type another question and the window is locked.
Did I use Dispatcher correctly?
private void send_Click(object sender, RoutedEventArgs e)
{
stack.Children.Add(new UserControl_Send()
{
DataSend = txt_input.Text,
DateTimeBot = DateTime.Now.ToString("HH:mm")
});
DispatchFit();
}
private void DispatchFit()
{
Dispatcher.BeginInvoke(new Action(ResponsServer), DispatcherPriority.Background);
}
public void ResponsServer()
{
Thread.Sleep(3000);
stack.Children.Add(new UserControl_Receive()
{
DataRecive = get(txt_input.Text),
DateTimeBot = DateTime.Now.ToString("HH:mm"),
});
}
When that ResponsServer() callback is being processed on your UI thread, then that Sleep is elongating the amount of time that callback is taking to process (Sleep does not pump the UI's dispatcher message queue).
If you want your callback to be done after 3 seconds, then you need to use a timer, or you can use "async" to cause a delayed processing of your callback.
Look at this question: Delayed Dispatch Invoke?
Or use this to have a BackgroundWorker do the delay and then call your ResponsServer on the UI thread (not the best code as it creates a new BackgroundWorker each time).
https://www.codeproject.com/Tips/240274/Execute-later-for-delayed-action
You are a little confused about the methods.
If I understand correctly, then Sleep is an emulation of the delay in the execution of sending a message to the server.
Then you need something like:
private async void send_ClickAsync(object sender, RoutedEventArgs e)
{
stack.Children.Add(new UserControl_Send()
{
DataSend = txt_input.Text,
DateTimeBot = DateTime.Now.ToString("HH:mm")
});
await ResponsServerAsync();
stack.Children.Add(new UserControl_Receive()
{
DataRecive = get(txt_input.Text),
DateTimeBot = DateTime.Now.ToString("HH:mm"),
});
}
public async Task ResponsServerAsync()
{
Thread.Sleep(3000);
}
For .Net Framework 3.5
private void send_Click(object sender, RoutedEventArgs e)
{
stack.Children.Add(new UserControl_Send()
{
DataSend = txt_input.Text,
DateTimeBot = DateTime.Now.ToString("HH:mm")
});
Thread thread = new Thread(ResponsServer);
thread.Start();
}
public void ResponsServer()
{
Thread.Sleep(3000);
if (Dispatcher.CheckAccess())
{
StackChildrenAdd();
}
else
{
Dispatcher.InvokeAsync(StackChildrenAdd);
}
}
private void StackChildrenAdd()
{
stack.Children.Add(new UserControl_Receive()
{
DataRecive = get(txt_input.Text),
DateTimeBot = DateTime.Now.ToString("HH:mm"),
});
}

CefSharp 'CanExecuteJavascriptInMainFrame' false after page loaded async

I found error while using LoadPageAsync Method.
All code to reproduce is below.
1) Wait while new ChromiumWebBrowser("https://www.google.com/") page loaded.
2) then press button1.
Result: canExecuteJs will be false while canExecuteJs1 will be true.
In other hand if don't wait for new ChromiumWebBrowser("https://www.google.com/") page to be loaded, and in moment button appear on screen click it, both variables will be true.
public ChromiumWebBrowser chromeBrowser;
public Form1()
{
InitializeComponent();
Cef.EnableHighDPISupport();
Cef.Initialize(settings);
chromeBrowser = new ChromiumWebBrowser("https://www.google.com/");
Controls.Add(chromeBrowser);
chromeBrowser.Dock = DockStyle.Fill;
}
public Task LoadPageAsync(IWebBrowser browser, string address = null)
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<LoadingStateChangedEventArgs> handler = null;
handler = (sender, args) =>
{
if (!args.IsLoading)
{
browser.LoadingStateChanged -= handler;
tcs.TrySetResult(true);
}
};
browser.LoadingStateChanged += handler;
if (!string.IsNullOrEmpty(address))
browser.Load(address);
return tcs.Task;
}
private async void button1_Click(object sender, EventArgs e)
{
await LoadPageAsync(chromeBrowser, "https://stackoverflow.com/questions/59862329/wordpress-submit-form-into-new-tab-and-continue-processing");
var canExecuteJs = chromeBrowser.CanExecuteJavascriptInMainFrame; //FALSE
await LoadPageAsync(chromeBrowser, "https://stackoverflow.com/questions/59861819/getting-2-values-from-array-of-objects");
var canExecuteJs1 = chromeBrowser.CanExecuteJavascriptInMainFrame; //TRUE
}

keeping track of completion of many tasks

I have a WPF application in which lots of event initiate tasks. Here's how I am doing it. But I am not happy about how it looks now
var task = UpdatePersonModelAsync();
taskCollection.Add(task);
RaisePropertyChanged(nameof(IsUpdateInProgress));
await task;
taskCollection.Remove(task);
RaisePropertyChanged(nameof(IsUpdateInProgress));
The property which shows/hides spinner
public bool IsUpdateInProgress => taskCollection.Count > 0;
I was going through the Progress<T> it seems like a call back.
When all the incoming tasks are completed a small spinner will be hidden.
You probably should use await Task.WhenAll(taskCollection.ToArray()); for waiting all the tasks you need to wait. After that, put the code for hiding the spinner below the await statement.
I tried using a CustomTaskScheduler. I got it from https://www.infoworld.com/article/3063560/application-development/building-your-own-task-scheduler-in-c.html
http://www.codeguru.com/csharp/article.php/c18931/Understanding-the-NET-Task-Parallel-Library-TaskScheduler.htm
As tasks are created from various calls, I use the CustomTaskScheduler in Task.Factory.StartNew.
On debugging, I can get hits on QueueTask but Execute isn't getting called. What am I missing?
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable
{
public delegate void TaskStartedHandler(Task sender);
public delegate void AllTasksCompletedHandler(IEnumerable<Task> sender);
public event TaskStartedHandler TaskStarted;
public event AllTasksCompletedHandler AllTasksCompleted;
private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>();
private readonly Thread mainThread = null;
public CustomTaskScheduler()
{
mainThread = new Thread(new ThreadStart(Execute));
if (!mainThread.IsAlive)
{
mainThread.Start();
}
}
private void Execute()
{
foreach (var task in tasksCollection.GetConsumingEnumerable())
{
var isStarted = TryExecuteTask(task);
if (isStarted && TaskStarted != null)
{
TaskStarted(task);
}
}
if(tasksCollection.GetConsumingEnumerable().All(m => m.IsCompleted))
{
AllTasksCompleted?.Invoke(tasksCollection.GetConsumingEnumerable());
}
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return tasksCollection.ToArray();
}
protected override void QueueTask(Task task)
{
if (task != null)
{
tasksCollection.Add(task);
}
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return false;
}
private void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
tasksCollection.CompleteAdding();
tasksCollection.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
The code is not yet complete. It would be great if someone can point to right direction
Here's a new version
public class TaskTracker
{
public delegate void AllTasksCompletedHandler(object sender);
public delegate void TaskStartedHandler();
public event AllTasksCompletedHandler AllTasksCompleted;
public event TaskStartedHandler TaskStarted;
private object syncLock = new object();
private SynchronizedCollection<Task> tasksCollection;
private bool isTaskStartedNotified;
private readonly uint delay;
public TaskTracker(uint delayBeforeRemovingTasks)
{
tasksCollection = new SynchronizedCollection<Task>();
delay = delayBeforeRemovingTasks;
}
public void Add(Task task)
{
if (!isTaskStartedNotified)
{
isTaskStartedNotified = true;
TaskStarted?.Invoke();
}
task.ContinueWith(t =>
{
RemoveTask(t);
});
tasksCollection.Add(task);
}
private async void RemoveTask(Task task)
{
await Task.Delay(300);
await Task.Run(() =>
{
tasksCollection.Remove(task);
if (tasksCollection.Count == 0)
{
isTaskStartedNotified = false;
AllTasksCompleted?.Invoke(tasksCollection);
}
});
}
}

how to start progress-bar after sometime of interval

I have wpf application with progressbar. I want to start it after 1 minute. my progress bar working properly just want to start after specific time period.
code for progressbar
public login()
{
InitilizeComponent()
DispatcherTimer timer= new Timer(1000);
timer.Elapsed+= timer_Elapsed;
timer.start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
thsi.Dispatcher.Invoke(DispatcherProperty.Normal,(Action)() =>
{
if (progressBar<20)
{
progressBar.value+=1;
}
else
{
timer.stop();
}
}
)
}
You could use a combination of async/await methods and a timer to achieve this:
DispatcherTimer timerProgressBar;
public login()
{
InitializeComponent();
//Create the timer
timerProgressBar = new DispatcherTimer();
timerProgressBar.Interval = new TimeSpan(0,0,1);
timerProgressBar.Tick += timerProgressBar_Tick;
//Start waiting
WaitForProgressBar();
}
void timerProgressBar_Tick(object sender, EventArgs e)
{
this.Dispatcher.Invoke(() =>
{
if (progressBar.Value < 20)
progressBar.Value++;
else
timerProgressBar.Stop();
});
}
/// <summary>
/// Begins waiting for 1 minute before starting the timer.
/// </summary>
public async void WaitForProgressBar()
{
await Task.Run(() => System.Threading.Thread.Sleep(new TimeSpan(0, 1, 0)));
timerProgressBar.Start();
}
Using the async method, this will keep your UI responsive during the wait period. If this isn't what was intended then simply change the async methods to synchronous.

WPF Binding to TextBlock does not update target immediately

I don't think my question falls into any others here already so hopefully somebody can assist me.
I have a TextBlock binding set up using INotifyPropertyChnaged and it does work. The problem i am having is when it updates the target control (the TextBlock)
A quick run down of my code
namespace XYZ
{
public partial class Loading : Window
{
StatusMessage msg = StatusMessage.GetInstance();
public loading()
{
InitializeComponent();
this.DataContext = msg;
}
private void LoadBackEndUsers()
{
msg.Status = "Loading Users";
//txtStatus.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
//lblLoading.Focus();
this.txtStatus.DataContext = msg;
beUsers = new BackendUsers(Database);
allBEUsers = beUsers.GetAllUsers();
}
private void LoadProducts()
{
msg.Status = "Loading Products";
//txtStatus.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
//lblLoading.Focus();
this.txtStatus.DataContext = msg;
products = new Product(Database);
allProducts = products.GetAllProducts();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
LoadBackEndUsers();
LoadProducts();
}
}
}
Now my issue is that my textblock displays "Loading Products" only after the method LoadProducts() is completed. It doesn't show "Loading Users" at all, so the target is only updating after everything has completed.
How do I get it to update immediately. The commented out bits was me just trying various things to try to force an update.
Any help would be greatly appreciated.
Kind regards,
Neill
The issue is that your retrieving of data is occurring on the same thread as your UI logic. This means that even though you change a property value and raise the OnPropertyChanged it is not re-evaluated until after your blocking data access is done. Instead you should use a BackgroundWorker. Here is a great article that walks through how you can use this:
http://elegantcode.com/2009/07/03/wpf-multithreading-using-the-backgroundworker-and-reporting-the-progress-to-the-ui/
Your StatusMessage class should implement INotifyPropertyChanged:
Edit : Im pretty sure that your Window_ContentRendered eventhanler blocks every UI update. I wrote a little sample that works for me:
public partial class MainWindow : Window
{
StatusMessage msg = new StatusMessage();
public MainWindow()
{
InitializeComponent();
this.DataContext = msg;
}
private void LoadBackEndUsers()
{
Task.Factory.StartNew(() =>
{
this.Dispatcher.BeginInvoke(new ThreadStart(() => msg.Status = "Loading Users"), DispatcherPriority.Normal);
//Load users here:
Thread.Sleep(2000);
this.Dispatcher.BeginInvoke(new ThreadStart(() => msg.Status = "Users loaded"), DispatcherPriority.Normal);
// If users loaded start load products:
LoadProducts();
});
}
private void LoadProducts()
{
Task.Factory.StartNew(() =>
{
this.Dispatcher.BeginInvoke(new ThreadStart(() => msg.Status = "Loading Products"), DispatcherPriority.Normal);
//Load products here:
Thread.Sleep(2000);
this.Dispatcher.BeginInvoke(new ThreadStart(() => msg.Status = "Products Loaded"), DispatcherPriority.Normal);
});
}
private void Window_ContentRendered(object sender, EventArgs e)
{
LoadBackEndUsers();
//LoadProducts();
}
}

Resources