Displaying WPF-"NotifyIcon" on a separate thread - c#

I am currently working on an office add-in and I need to show a notification dialog that displays progress, I'm using Philipp Sumi's wpf-notifyicon.
I need to display the notifyicon from a separate thread as I have a lot of code that already executes on the main thread, this causes the wpf-notifyicon to block and wait because the messages in the windows message queue are not being processed.
I know that I should rather execute this time consuming code on a separate thread, and display the notifyicon from the main thread and update it accordingly, but that is unfortunately not an alternative because this whole solution is single-threaded.
Example:
private FancyPopup fancyPopup;
private void button1_Click(object sender, EventArgs e)
{
notifyIcon = new TaskbarIcon();
notifyIcon.Icon = Resources.Led;
fancyPopup = new FancyPopup();
Thread showThread = new Thread(delegate()
{
notifyIcon.ShowCustomBalloon(fancyPopup, System.Windows.Controls.Primitives.PopupAnimation.Fade, null);
});
showThread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fancyPopup.TextB.Text = "Doing something...";
//Keep the main thread busy.
Thread.Sleep(5000);
fancyPopup.TextB.Text = "Done doing something...";
}
Update
I have been able to progress a little further with this updated code:
I'm creating the TaskbarIcon object on a new thread , and using Application.Run to process the application message loop on that thread...
private FancyPopup fancyPopup;
private void button1_Click(object sender, EventArgs e)
{
Thread showThread = new Thread(delegate()
{
notifyIcon = new TaskbarIcon();
notifyIcon.Icon = Resources.Led;
fancyPopup = new FancyPopup();
notifyIcon.ShowCustomBalloon(fancyPopup, System.Windows.Controls.Primitives.PopupAnimation.Fade, null);
System.Windows.Forms.Application.Run();
});
showThread.SetApartmentState(ApartmentState.STA);
showThread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fancyPopup.Dispatcher.Invoke(new Action(delegate
{
fancyPopup.TextB.Text = "Doing something...";
}));
//Keep the main thread busy.
Thread.Sleep(5000);
fancyPopup.Dispatcher.Invoke(new Action(delegate
{
fancyPopup.TextB.Text = "Done doing something...";
}));
}

I have solved my problem, I had to initialize the notifyIcon on a separate STA thread and use Application.Run in order to start pumping windows messages on that thread.
var myThread = new Thread(delegate()
{
notifyIcon = new NotifyIcon();
Application.Run();
});
myThread.SetApartmentState(ApartmentState.STA);
myThread.Start();
Then I just had to Invoke the UI of my notification dialog.

Related

how to handle window in multi thread program

Hi i am creating a thread for a process, in that process i am trying to open a window but it open and quickly close. What can i do you that to hold that window and close by manually
private void supportCheckThread(object sender, RibbonControlEventArgs e)
{
ThreadObject thrdObj = new ThreadObject();
thrdObj.sender = sender;
thrdObj.e = e;
try
{
thrCheckStart = new System.Threading.Thread(new ParameterizedThreadStart(createCheckThread));
thrCheckStart.Start(thrdObj);
}
catch (Exception ex) {
}
finally { enableBtn(true); }
}
private void createCheckThread(object thrdParam)
{
ThreadObject ob = (ThreadObject)thrdParam;
btncheck_Click(ob.sender, ob.e);
}
private void newBtncheck_Click(object sender, RibbonControlEventArgs e)
{
enableBtn(false);
supportCheckThread(sender, e);
string s = "";
}
here how i open that window
h1 = new IDesignSpecWord2007.UI_Forms.HierarchyView(ref Globals.ThisAddIn.Application, ref docm);
h1.Show();
You should use the main thread only for UI tasks. A secondary thread is stopped as soon as the code/routine ends. You need to keep a secondary thread running indefinitely if you want to get a form visible all the time. When the thread stops all objects are destroyed and swiped out (so, the form disappears).

Threads and Tasks

I'm trying to open new windows form with thread and task, but this code open new windows, but do not show buttons, textfield, etc on it (form is loading)
private void newFomrm_Click(object sender, EventArgs e)
{
NewForm a = new NewForm(param); // my Form
Action showMethod = () => { a.Show(); };
Task t1 = new Task(showMethod);
Thread t = new Thread(new ThreadStart(t1.Start));
t.Start();
}
private void button1_Click(object sender, EventArgs e)
{
NewForm a = new NewForm(); // my Form
Action showMethod = () => {
Invoke(new Action(() => a.Show()));
};
Task t1 = new Task(showMethod);
Thread t = new Thread(new ThreadStart(t1.Start));
t.Start();
}
try this...you have to put the UI operation on the main thread

Multi-thread UI, unable to BeginInvoke

I have a requirement to launch a window on a separate thread from the main UI thread. It's not ideal, I know it's not normal.
The new window is launched as follows:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var thread = new Thread(() =>
{
var w = new ScrollingBanner(300,70,0,0);
w.Show();
w.Name = "BannerThread";
w.Closed += (sender2, e2) => w.Dispatcher.InvokeShutdown();
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
This launches the window and performs the action I need it to.
The control on the Window of this (threaded) UI calls an event I'm listening for in the code behind the window, as below:
private void ContentTicker_OnScrollComplete(object sender, EventArgs e)
{
Debug.WriteLine("Scroller Ended");
try
{
if (CheckAccess())
{
sliderText.Text = "Updated Text";
}
else
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() => sliderText.Text = "Updated Text"));
}
}
catch (Exception ex)
{
//breakpoint
}
}
ContentTicker_OnScrollComplete is called from a background thread under which the control runs.
I get an exception, a different thread owns the control; but as far as I'm aware I'm calling the Dispatcher to perform the action on the correct thread. (which works if I do all this on the main UI thread)
How can I update "sliderText" on the correct thread?
Thanks
Replace this:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() => sliderText.Text = "Updated Text"))
For this:
Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() => sliderText.Text = "Updated Text"))
because Application.Current.Dispatcher will return the "Main" Dispatcher, not the "Secondary" one.

Why is a disabled button clickable?

This case is using C# WPF. I want to instantly disable a button after clicking it to prevent clicking it twice in short succession. I disabled the button in OnClick_Event but still clickable.
Part of source is as below.
private void Button_Click_UpdateBurndownChart(object sender, RoutedEventArgs e)
{
if(threadNotWorking)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
private void startWorkThread()
{
... ...
//after finish required process
updateButton.IsEnabled = true;
}
Is there any way to accomplish this?
you may want to use a dispatcher, there is probably a threading problem (callback function running on seperate thread and trying to access ui which runs on another thread). try this . .
updateButton.Dispatcher.BeginInvoke(
new ThreadStart(() => updateButton.IsEnabled = false),
System.Windows.Threading.DispatcherPriority.Input, null);
instead of
updateButton.IsEnabled = false;
What happens if you were instead to change the order of your events from:
updateButton.IsEnabled = false;
startWorkThread();
To
startWorkThread();
updateButton.IsEnabled = false;
Let me know how this goes.
What it looks like is that you are starting your thread then immediatly enabling your button before your thread has finished. You would be better off using a BackgroundWorker and enable your Button in the RunWorkerCompleted Event. Though you can do something similar by enabling your button using a BeginInvoke at the end of your Process.
public void doWork()
{
System.Threading.Thread.Sleep(10000); //Simulating your Process
Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate() { updateButton.IsEnabled = true; }), System.Windows.Threading.DispatcherPriority.Background);
}
Example with BackgroundWorker
using System.ComponentModel;
public partial class MainWindow : Window
{
BackgroundWorker bgw;
public MainWindow()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
updateButton.IsEnabled = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000); //Simulating your work
}
private void startWorkThread()
{
bgw.RunWorkerAsync();
}
private void updateButton_Click(object sender, RoutedEventArgs e)
{
if (bgw.IsBusy != true)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
}

TopMost form in a thread?

I am using the following code to open a form in a new thread:
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
Form form = new Form();
form.TopMost = true;
form.ShowDialog();
}
But the newly created form isn't TopMost even though I set it to true.
How can I make a form in a thread TopMost ?
Usually you don't need another thread, you open the form as usual in modal or non modal mode, if the form needs to do a heavy process then you do the process inside a thread.
Specific to your question one option is to run the form from an Application.Run as described here.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
using (Form1 _form = new Form1())
{
_form.TopMost = true;
Application.Run(_form);
}
}
}
That will launch a new thread with its own message pump and will keep it as a TopMost form.
Just ran into this problem myself. It seems that if the form has an Owner, then TopMost works as expected. If the owning form was created on another thread, though, it's a little tricky to set. Here's what I used:
var form = new Form();
form.Shown += (sender, e) => {
Control.CheckForIllegalCrossThreadCalls = false;
form.Owner = /* Owning form here */;
form.CenterToParent(); // Not necessary
Control.CheckForIllegalCrossThreadCalls = true;
form.TopMost = true; // Works now!
};
Application.Run(form);
Instead of calling ShowDialog directly, try using this.Invoke to gain ownership of the form.
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
Form form = new Form();
form.TopMost = true;
this.Invoke((Action)delegate() { form.ShowDialog(); });
}

Categories