I got an interesting bug report today.
I have a DispatcherTimer whose Tick calls an EventHandler which contains a Stop() method call. This stops the timer on the platforms we use in development (Windows XP SP3 and Windows 7), but the timer does not seem to stop when run on a Windows Server 2008 SP2 machine.
This is a .NET 3.5 project.
I am wondering if this is a known bug in System.Windows.Threading in Win 2k8 or if I am doing something wrong in my code.
The relevant parts of the code are below:
public DispatcherTimer UserDelayTimer;
private void _HierTreeControlWPF_Loaded(object sender, RoutedEventArgs e)
{
UserDelayTimer = new DispatcherTimer();
UserDelayTimer.Interval = new TimeSpan(0, 0, 0, 0, 500); //500 ms
UserDelayTimer.Tick += new EventHandler(OnTimerEvent);
UserDelayTimer.Start();
}
/// <summary>
/// Timer to run update after the user has stopped making selections in the hierarchy view.
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
void OnTimerEvent(object sender, EventArgs e)
{
if (HierTreeAfterCheck_Event != null && !HierTreeCheckEvent_Suppressed)
HierTreeAfterCheck_Event();
UserDelayTimer.Stop();
}
//This method is run whenever the mouse moves or makes a selection in the hierarchy tree.
//The idea is that HierTreeAfterCheck_Event() will only run after the user has stopped making
//selections for a certain amount of time.
public void ResetUserDelayTimer(object sender, MouseButtonEventArgs e)
{
if (UserDelayTimer.IsEnabled) //if the timer is running, restart it to buy more time.
{
UserDelayTimer.Stop();
UserDelayTimer.Start();
}
}
Many thanks in advance!
Figured it out. Turns out I had to modify the sender of OnTimerEvent, instead of the public instance of the timer itself.
Related
This is an incredibly simple task tray app - using ApplicationContext and a few guides I found online.
The purpose of the app is to query a small REST API and show a message box to the user on a given result. I need to essentially have the API query in a background loop, running every 10 seconds or something similar. This is to report on data that I've made accessible via another service.
I've done some reading and it seems a BackgroundWorker and Timer is an appropriate option, but I'm lost on where to go next. How exactly can I achieve this? I initially tried adding a while(true) loop to the TaskTrayApplicationContext but it just created an infinite loop whereby you couldn't do anything else with the app.
namespace TaskTrayApplication
{
public class TaskTrayApplicationContext : ApplicationContext
{
NotifyIcon notifyIcon = new NotifyIcon();
Configuration configWindow = new Configuration();
public TaskTrayApplicationContext()
{
MenuItem configMenuItem = new MenuItem("Configuration", new EventHandler(ShowConfig));
MenuItem exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
notifyIcon.Icon = TaskTrayApplication.Properties.Resources.AppIcon;
notifyIcon.DoubleClick += new EventHandler(ShowMessage);
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { configMenuItem, exitMenuItem });
notifyIcon.Visible = true;
}
void ShowMessage(object sender, EventArgs e)
{
// Only show the message if the settings say we can.
if (TaskTrayApplication.Properties.Settings.Default.ShowMessage)
MessageBox.Show("This is the Serenity TaskTray Agent.");
}
void ShowConfig(object sender, EventArgs e)
{
// If we are already showing the window meerly focus it.
if (configWindow.Visible)
configWindow.Focus();
else
configWindow.ShowDialog();
}
void Exit(object sender, EventArgs e)
{
// We must manually tidy up and remove the icon before we exit.
// Otherwise it will be left behind until the user mouses over.
notifyIcon.Visible = false;
Application.Exit();
}
}
}
And the Program.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TaskTrayApplication
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Instead of running a form, we run an ApplicationContext.
Application.Run(new TaskTrayApplicationContext());
}
}
}
Threading is hard, concurrency is hard. Background worker and System.Timers are both constructs that run in their own thread.
winforms won't allow for interaction between threads that own a control (read: that created a control) and threads that don't. This is a whole subject apart i wont get into now - theres good stuff to read out there why this is and how to go about it: https://visualstudiomagazine.com/articles/2010/11/18/multithreading-in-winforms.aspx
There are tools to help, one is the dispatchertimer:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netcore-3.1
This is a special timer that instead of its own thread, schedules tasks on the main thread. The main thread in a winforms application handles the drawing of controls, showing of the different windows etc. e.g. this 'owns' all controls.
A sample can be seen on msdn, i adopted it here to show you what you could do:
public class TaskTrayApplicationContext : ApplicationContext
{
...
DispatcherTimer dispatcherTimer;
public TaskTrayApplicationContext()
{
...
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// Fetch your data via a rest api
var myData = MyDataFunction();
// check and show dialog if the data is not okay
if(myData.Result.Value = 'NOT_OKAY!')
ShowMessage(this, myData.Result); // or something.
}
...
Now since this does not utilize a second thread, this means the main ui thread could be blocked from drawing the windows, reacting to user input etc. because its busy doing work in the timer_tick function. This would for example happen if your rest call takes a long time.
This will make your application freeze and irresponsive. This could be a problem but most likely wont, so lets burn that bridge when we get to it.
For a project I need to get some data from a Bluetooth device on windows 10 using C#. I'm not too familiar with the Bluetooth API and can't figure out why the following is not working:
Using the BluetoothLEAdvertisementWatcher I search for advertisements, which works fine. I do receive the advertisement from the device (local name fits) as well as it's ServiceUuids. Next I try to connect to the device using the BluetoothAddress received together with the advertisement:
private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher,
BluetoothLEAdvertisementReceivedEventArgs eventArgs)
{
ulong blAdress = eventArgs.BluetoothAddress;
BluetoothLEDevice blDevice = await
Windows.Devices.Bluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(blAdress);
}
However, doing so results in an exception:
Element not found. (Exception from HRESULT: 0x80070490).
Is this the correct way to read data from the device? Are other options available to read the data from the services? Manually pairing the device in windows is not really an option and also seems to fail.
/Edit 1: I check for the local name of the device to make sure I only try to connect to the right one. So I guess there is a problem with connecting to this specific device, still I have no idea how to work around that. The service data was succesfully read on iOS, so it should be possible.
Until MS fixes this problem the only reliable solution to this I have found to connect to a BLE device is to ask the registry for a list of paired BLE devices and compare the bluetooth address in the advert with with registry list of paired able devices. My experience is that when FromBluetoothAddressAsync is called on an unpaired device Windows throws an exception and kills the watcher thread. I have some C++ code that I am happy to share that reads the registry and creates a list of paired BLE devices.
Hopefully MS will take the time to fully support BLE in the same manner Apple does.
Here is a reference from MS (https://social.msdn.microsoft.com/Forums/vstudio/en-US/e321cb3c-462a-4b16-b7e4-febdb3d0c7d6/windows-10-pairing-a-ble-device-from-code?forum=wdk). It seems that to use this BluetoothLEDevice.FromBluetoothAddressAsync we have to handle the exception when the device is advertising and not yet paired.
I got the same issue when I using the BluetoothLEAdvertisementWatcher directly.
Then I tested the different addresses listed by the watcher. I found it is related to the Bluetooth devices.
After adding the filter as following, I can connect to GATT device (TI Sensor Tag) successfully.
public sealed partial class MainPage : Page
{
private BluetoothLEAdvertisementWatcher watcher;
public MainPage()
{
this.InitializeComponent();
// Create and initialize a new watcher instance.
watcher = new BluetoothLEAdvertisementWatcher();
// Part 1B: Configuring the signal strength filter for proximity scenarios
// Configure the signal strength filter to only propagate events when in-range
// Please adjust these values if you cannot receive any advertisement
// Set the in-range threshold to -70dBm. This means advertisements with RSSI >= -70dBm
// will start to be considered "in-range".
watcher.SignalStrengthFilter.InRangeThresholdInDBm = -70;
// Set the out-of-range threshold to -75dBm (give some buffer). Used in conjunction with OutOfRangeTimeout
// to determine when an advertisement is no longer considered "in-range"
watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -75;
// Set the out-of-range timeout to be 2 seconds. Used in conjunction with OutOfRangeThresholdInDBm
// to determine when an advertisement is no longer considered "in-range"
watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(2000);
// By default, the sampling interval is set to zero, which means there is no sampling and all
// the advertisement received is returned in the Received event
// End of watcher configuration. There is no need to comment out any code beyond this point.
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
watcher.Received += OnAdvertisementReceived;
watcher.Stopped += OnAdvertisementWatcherStopped;
App.Current.Suspending += App_Suspending;
App.Current.Resuming += App_Resuming;
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
App.Current.Suspending -= App_Suspending;
App.Current.Resuming -= App_Resuming;
watcher.Stop();
watcher.Received -= OnAdvertisementReceived;
watcher.Stopped -= OnAdvertisementWatcherStopped;
base.OnNavigatingFrom(e);
}
private void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
// Make sure to stop the watcher on suspend.
watcher.Stop();
// Always unregister the handlers to release the resources to prevent leaks.
watcher.Received -= OnAdvertisementReceived;
watcher.Stopped -= OnAdvertisementWatcherStopped;
}
private void App_Resuming(object sender, object e)
{
watcher.Received += OnAdvertisementReceived;
watcher.Stopped += OnAdvertisementWatcherStopped;
}
private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
{
var address = eventArgs.BluetoothAddress;
BluetoothLEDevice device = await BluetoothLEDevice.FromBluetoothAddressAsync(address);
var cnt =device.GattServices.Count;
watcher.Stop();
}
/// <summary>
/// Invoked as an event handler when the watcher is stopped or aborted.
/// </summary>
/// <param name="watcher">Instance of watcher that triggered the event.</param>
/// <param name="eventArgs">Event data containing information about why the watcher stopped or aborted.</param>
private void OnAdvertisementWatcherStopped(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementWatcherStoppedEventArgs eventArgs)
{
}
private void start_Click(object sender, RoutedEventArgs e)
{
watcher.Start();
}
}
Just a guess, but maybe you need this:
watcher.ScanningMode = BluetoothLEScanningMode.Active;
and in the OnAdvertisementReceived event
if (e.AdvertisementType == BluetoothLEAdvertisementType.ScanResponse)
{
BluetoothLEDevice blDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(e.BluetoothAddress);
}
If this is a UWP project, ensure you enable Bluetooth capabilities.
To do so in Visual Studio solution explorer double click the *.appxmanifest, choose the 'Capabilities' tab and ensure that 'Bluetooth' is checked.
It will add some xml not unlike the following;
<Capabilities>
<Capability Name="internetClientServer" />
<DeviceCapability Name="bluetooth" />
</Capabilities>
This Question is over 3 years old, but because it has over 13000 views, I will answer.
The reason for Element not found is that Windows.Devices is not aware of advertising Ble-devices until they are paired or connected.
Instead in the OnAdvertisementReceived just use:
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress);
I also have a very simple uwp example on github, it has no controls to keep it as simple as possible. All the results are shown in the debug output window.
The most usefull info is in MainPage.xaml.cs
check it out: https://github.com/GrooverFromHolland/SimpleBleExample_by_Devicename
If I try to right-click close the minimised form application or using task manager applications tab to end the process the FormClosing event does not trigger while a timer is defined and is running/enabled.
//Extract from Form designer.cs:
....
private System.Windows.Forms.Timer timer1;
....
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.timer1.Interval = 5000;
....
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
......
this.Load += new System.EventHandler(this.FormSI_Load);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormSI_FormClosing);
//Extract from form.cs:
namespace namespace
{
public partial class FormSI : Form
{
.....
public FormSaturnInterface()
{
InitializeComponent();
......
timer1.Interval = intTimerIntervalms;
//timer1.Enabled = true; //FormClosing event not triggered if timer1 enabled
timer1.Enabled = false; //FormClosing event triggered if timer1 not enabled
........
private void FormSI_Load(object sender, EventArgs e)
{
MessageBox.Show("Load Event triggered["+ e.ToString() + "]", "Load Event triggered", MessageBoxButtons.OK); //Load event always triggered with or without timer1 enabled
}
private void FormSI_FormClosing(Object sender, FormClosingEventArgs e)
{
//e.Cancel = true; //stops app/form closing
timer1.Stop();
System.Text.StringBuilder messageBoxCS = new System.Text.StringBuilder();
messageBoxCS.AppendFormat("{0} = {1}", "CloseReason", e.CloseReason);
messageBoxCS.AppendLine();
messageBoxCS.AppendFormat("{0} = {1}", "Cancel", e.Cancel);
messageBoxCS.AppendLine();
MessageBox.Show(messageBoxCS.ToString(), "FormClosing Event");
}
........
I do not wish to stop/disable the timer until an attempt is made to close/end the application yet still want the Formclosing event to trigger. What are possible solutions to this scenario? Any assistance will be greatly appreciated.
Please note that the FormSI.designer.cs already contains this code:
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
MessageBox.Show("Disposing", "Disposing", MessageBoxButtons.OK);
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
This Dispose method is called as is the Form_FormClosing event when the timer1 is disabled, but neither appear to being called when the timer1 is enabled and in this case end task is the only way of terminating the app/form. Could it be the thread is too busy with the time to process the Close request? If so how can one cause the thread to be periodically interrupted to allow it to service the close request?
The issue, was that the app could not be closed in an orderly fashion allowing a user to be prompted to save data for example or did they really mean to close the app. The issue was to do with the timer being caught in a loop when certain databases were not available and therefore no other messages/events could be processed by the single thread. As soon as the timer exited from the loop, by either timing out or reaching the required database all queued messages / events were then processed correctly. Thank you to all respondents for the time and feedback this helps the thought processes and allows you to thing of alternative reasons for certain behaviors. :)
The issue was to do with the timer being caught in a loop when certain databases were not available and therefore no other messages/events could be processed by the single thread. As soon as the timer exited from the loop, by either timing out or reaching the required database all queued messages / events were then processed correctly. Thank you to all respondents for the time and feedback this helps the thought processes and allows you to thing of alternative reasons for certain behaviors. :)
Hello I am trying to program some checkboxes to become checked and unchecked in a specific sequence programmatically. I know it sounds dumb, but this is corresponding to some LED controls that I've already coded the check events for.
I want to check a checkbox to start this sequence and uncheck it to stop it. Currently the checking and unchecking of my D2 checkbox occurs fine, but the do while loop freezes the form so I can't actually uncheck the cycle box. I probably should not be using Thread.Sleep either. Any advice is appreciated.
private void cycleCheckBox_CheckedChanged(object sender, EventArgs e)
{
do
{
D2.Checked = true;
Thread.Sleep(1000);
D2.Checked = false;
Thread.Sleep(1000);
} while (cycleCheckBox.Checked);
}
The Thread.Sleep method will run on the UI thread if called directly in the checked event which is why the UI is freezing. Push the work into a System.Windows.Forms.Timer (assumption is this is a WinForms app):
Implements a timer that raises an event at user-defined intervals.
This timer is optimized for use in Windows Forms applications and must
be used in a window.
Example based on your question:
Timer _timer;
private void cycleCheckBox_CheckedChanged(object sender, EventArgs e)
{
if(_timer == null )
{
_timer = new Timer();
_timer.Interval = 1000; // 1 second
_timer.Tick += DoTimerWork;
}
if(cycleCheckBox.Checked)
{
_timer.Start();
}
else
{
_timer.Stop();
}
}
private void DoTimerWork(object obj, EventArgs args)
{
D2.Checked = !D2.Checked;
}
I don't know if this will work for you but what I would do is drag in a Timer Control at 1000 ms and use a method to figure out which checkbox should currently be checked by using an integer that loops to 0 at a certain point and gets incremented at each tick.
I want to prevent the user clicking two times on a button when it has been already executing and the process is not finished.
I am using compact framework 3.5 and I have problems when the user clicks two times on a button that is already executing or some other button. I want to disable all buttons when the program is executing and enable them again when the process is done.
OS: Windows mobile 6.1
Framework: .NET 3.5 CF
Try adding this.Enabled = false first thing (this being the form in question) in the scope of your Click handler. Be sure to set it back to true when done. You may need to Application.DoEvents() or Update() to display visible progress if this all in the scope of the handler. Probably the preferred way to do any extended processing though would be to spawn a background thread and update your UI from it using Invoke and BeginInvoke.
I found that I needed to do this quite often when building a windows mobile application so made a simple utility class.
public static class FormUtility
{
/// <summary>
/// Lock the form whilst processing
/// </summary>
/// <param name="controlCollection"></param>
/// <param name="enabled"></param>
public static void FormState(Control.ControlCollection controlCollection, bool enabled)
{
foreach (Control c in controlCollection)
{
c.Enabled = enabled;
c.Invalidate();
c.Refresh();
}
}
}
All I need to do was then call one line to lock the form down.
FormUtility.FormState(this.Controls, false);
You should end up with something like
private void btnSave_Click(object sender, EventArgs e)
{
FormUtility.FormState(this.Controls, false);
//Do your work
if (!SaveSuccessful())
//Renable if your validation failed
FormUtility.FormState(this.Controls, true);
}
EDIT : I think what #tcarvin is suggesting is that you do not need to call refresh on every control but simply invalidate the controls and then refresh the container which will cause all the invalidated controls to redraw at once. I haven't tested this but a small change to something like...
public static void FormState(Form form, bool enabled)
{
foreach (Control c in form.Controls)
{
c.Enabled = enabled;
c.Invalidate();
}
form.Refresh();
}
Then use
FormUtility.FormState(this, true);
This is the easiest way, for a button called button1:
void button1_Clicked(object sender, EventArgs e) {
button1.Enabled = false;
try {
// put your code here
} finally {
button1.Enabled = true;
}
}