I'm trying to use a WPF window as a message popup that will close once a task has been performed. All the documentation I've seen says that this can't be done with a messageBox, that's why I'm going with the WPF. I found one code snip that allowed me to open the WPF window but it wouldn't progress the application to the next process. Below is the last code example I found that I thought showed promise but the window isn't opening -
[STAThread]
static void Main(string[] args)
{
try
{
string filePath = "my new directory";
var popup = new PopupTest();
popup.Dispatcher.BeginInvoke
(System.Windows.Threading.DispatcherPriority.Normal,
(Action)(() =>
{
popup.Show();
}));
// Do some console application stuff
do
{
Directory.CreateDirectory(filePath);
} while (!Directory.Exists(filePath));
popup.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
}
}
The cs.xaml file is just the default
/// Interaction logic for PopupTest.xaml
/// </summary>
public partial class PopupTest : Window
{
public PopupTest()
{
InitializeComponent();
}
}
I feel like this should be simpler than I'm making it. Anything that can point me in the right direction is appreciated.
You need to reference the WPF assemblies and create and run a System.Windows.Application on the STA thread:
[STAThread]
static void Main(string[] args)
{
var app = new System.Windows.Application();
app.Run(new PopupTest());
}
The Run method blocks and doesn't return until the app is shut down.
If you want to do some stuff while the app is running, you need to do this on another thread:
[STAThread]
static async Task Main(string[] args)
{
Task t = Task.Run(() =>
{
string filePath = "my new directory";
do
{
Directory.CreateDirectory(filePath);
} while (!Directory.Exists(filePath));
});
var app = new System.Windows.Application();
app.Run(new MainWindow());
await t;
}
Related
I have a simple task I was trying to get to repeat using FluentScheduler, but I can't seem to get it to run properly. I am entirely new to "Jobs" and "Actions", so I am sure it is something stupid I am not accounting for.
I am modifying my original question as the supplied solutions were working for a console app, but not a Windows Forms app. I have tried moving the line Application.Run(ThisForm); to be before and after the JobManager and the Task, but it never updates the TextBox with the string.
public class Program
{
public class MyRegistry : Registry
{
public MyRegistry()
{
Action someMethod = new Action(() =>
{
Form1 ThisForm = new Form1();
ThisForm.Text ="HELLO";
});
this.Schedule(someMethod).ToRunNow().AndEvery(2).Seconds();
}
}
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(ThisForm);
JobManager.Initialize(new MyRegistry());
var t = Task.Run(async delegate
{
await Task.Delay(-1);
});
t.Wait();
}
}
I try to open dialog window before the runnig wpf application:
public class Program
{
[STAThread]
public static void Main(string[] args)
{
var app = new App();
var win = new MainWindow();
if (win.ShowDialog().GetValueOrDefault())
{
app.Run();
}
}
}
class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var win = new Window1();
win.Show();
}
}
Why does win.ShowDialog() run the app (app.OnStartup is executed)?
But, win.Show() doesn't run the app
[STAThread]
public static void Main(string[] args)
{
var app = new App();
var win = new MainWindow();
win.Show();
app.Run();
}
Why is this behavior???
Thanks
ShowDialog starts its own message loop. It does almost the same thing as Application.Run, and since you already created an instance of the application, the startup message (well, dispatch) has already been sent, and will be interpreted by the message loop (dispatcher) in ShowDialog. Show basically only sends a message to the queue, so it doesn't do anything unless there's a message loop processing the messages.
If you want to show a dialog before the startup is registered, don't create the application instance before showing the dialog.
I have a windows form application which is supposed to show a splash screen with a label field that I want to update as the main form (called welcome.cs) loads in the background. The splash screen shows & hides just fine, but the label doesn't update.
I've done a lot of research but haven't quite found the solution.
Program.cs
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (new SingleGlobalInstance(1000))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen splashscreen = new SplashScreen();
splashscreen.ShowSplashScreen();
Welcome welcome = new Welcome(splashscreen); //Takes some time to load
splashscreen.CloseForm();
Application.Run(welcome);
}
}
Splashscreen.cs
public partial class SplashScreen : Form
{
//Delegate for cross thread call to close
private delegate void CloseDelegate();
private delegate void UpdateStatusDelegate(string status);
private static SplashScreen splashScreen;
private Thread thread = null;
public SplashScreen()
{
InitializeComponent();
}
public void ShowSplashScreen()
{
// Make sure it is only launched once.
if (splashScreen != null)
return;
thread = new Thread(ShowForm);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
static private void ShowForm()
{
splashScreen = new SplashScreen();
Application.Run(splashScreen);
}
public void CloseForm()
{
splashScreen.Invoke(new CloseDelegate(CloseFormInternal));
}
static private void CloseFormInternal()
{
splashScreen.Close();
}
public void UpdateStatus(string status)
{
splashScreen.Invoke(new UpdateStatusDelegate(UpdateStatusInternal), status);
}
private void UpdateStatusInternal (string status)
{
if (splashScreen != null && splashScreen.IsHandleCreated)
{
lblStatus.Text = status;
}
}
}
Welcome.cs
public Welcome(Splashscreen splashscreen)
{
InitializeComponent();
//Code to log the user into the system
splashScreen.UpdateStatus("Logging in...");
//my expectation is that UpdateStatus call will update the label displayed on the splash screen but it doesn't.
//Do more stuff.....
}
Does it have something to do with multi-threading or is it because im creating a new instance of splashscreen in welcome.cs before calling UpdateStatus? How would I get around this?
You could do the following
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string[] args = Environment.GetCommandLineArgs();
// Creates the Splash
splash = new FrmSplash();
//Opens the Splash in a new Thread, this way any gifs, progress bars, lablels changes will work because the main thread isnt blocked
var t = Task.Factory.StartNew(() =>
{
splash.ShowDialog();
});
while (!splash.Created) // wait the splash screen form load process
System.Threading.Thread.Sleep(300);
UpdateSplashMessage("Loading the program... Please wait");
// Some slow initialization code.
// ...
//Close splash screen
CloseSplash();
Application.Run(args);
}
static void CloseSplash()
{
splash.Invoke(new MethodInvoker(() =>
{
splash.Close(); // Closes the splash that is running in the other thread
}));
}
static void UpdateSplashMessage(string msg)
{
splash.Invoke(new MethodInvoker(() =>
{
splash.AtualizarMensagem(msg);
}));
}
Note that you will need to create a method called AtualizarMensagem(string str) in your splash screen form, like this
public void AtualizarMensagem(string novaMsg)
{
lblCarregando.Text = novaMsg;
}
I have this code in my "useful snnipets" folder, it always works for me.
Hope this helps.
I want to let the users to select only the following extension files: .jpg,.png, .tiff, .gif, .png. using Windows Explorer Context Menu I followed this link:http://www.codeproject.com/Articles/15171/Simple-shell-context-menu?msg=4779433#xx4779433xx and I could register and un-register successfully for .jpg file.
When I click on the command fileCopytoDirAnothing is happening i.e the function is not working. (I followed the same approach using console application with my function it works).
Where & How should i call the function during the 'fileCopytoDirA click?? Any help?
![enter image description here][1]
Code to register in the registry:
InitializeComponent();
string menuCommand = string.Format("\"{0}\" \"%L\"", Application.Current);
FileShellExtension.Register("OISjpegfile", "fileCopytoDirA", "fileCopytoDirA", menuCommand);
Function to be executed during click:
static void fileCopytoDirA(string filePath)
{
try
{
File.Copy(filePath, System.IO.Path.Combine(#"C:\Test\Directories\", System.IO.Path.GetFileName(filePath)), true);
}
catch (Exception ex)
{
MessageBox.Show(string.Format("An error occurred: {0}", ex.Message));
return;
}
}
Function to un register the registry entries during WPF application close:
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
FileShellExtension.Unregister("OISjpegfile", "fileCopytoDirA");
}
[1]: http://i.stack.imgur.com/eAN5F.png
Edit afterMetadingsanswer:
App()
{
InitializeComponent();
string menuCommand = string.Format("\"{0}\" \"%L\"", System.Reflection.Assembly.GetExecutingAssembly().Location);
FileShellExtension.Register("OISjpegfile", "fileCopytoDirA", "fileCopytoDirA", menuCommand);
}
[STAThread]
public static void Main(string args)
{
if (string.IsNullOrEmpty(args))
{
// Run your Main Form
// (blocks until Form1 is closed)
Window3 window = new Window3();
App app = new App();
app.Run(window);
}
else
{
// Run the context menu action
fileCopytoDirA(args);
}
// exit
}
static void fileCopytoDirA(string args)
{
try
{
File.Copy(args, System.IO.Path.Combine(#"C:\Test\Directories\", System.IO.Path.GetFileName(args)), true);
}
catch (Exception ex)
{
MessageBox.Show(string.Format("An error occurred: {0}", ex.Message));
return;
}
}
The (simple) registry setting is that your application is executed by arguments, as it would be called in a console:
app.exe "TheJpegFile.jpg"
So the entry point is static void Main(string args), from there you can call fileCopytoDirA(args). There is no magic way Explorer calls a function by its name. You can either implement the COM interfaces, as for example this project does, or you go the quick and dirty way, by redirecting your Main; if there is not an argument, run the (windows forms) application - if there is an argument, do the action and exit:
using System;
using System.Windows.Forms;
public static class Program {
public static void Main(string args) {
if (string.IsNullOrEmpty(args)) {
// Run your Main Form
// (blocks until Form1 is closed)
Application.Run(new Form1());
}
else {
// Run the context menu action
fileCopytoDirA(args);
}
// exit
}
}
The FileShellExtension.Register function is defined as
public static void Register(string fileType,
string shellKeyName, string menuText, string menuCommand)
So the arguments are
string fileType - the HKC registry key for the file extension
string shellKeyName - just a registry key name for Explorer to distinguish shell extensions
string menuText - what the user can see in Explorer's context menu
string menuCommand - the shell command Explorer executes just like you can do in a console or by a link
P.S: In WPF it's similar, but you create new YourApp class (derived from System.Windows.Application) and then call Run.
Assuming Application.xaml looks like
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
</Application>
and your application class is in namespace WpfApplication1 named App, and you have a Window1.xaml, the q'n'd looks like
namespace WpfApplication1
{
public partial class App : Application
{
App()
{
InitializeComponent();
}
[STAThread]
static void Main(string args)
{
if (string.IsNullOrEmpty(args)) {
// Run your Main Form
// (blocks until Form1 is closed)
Window1 window = new Window1();
App app = new App();
app.Run(window);
}
else {
// Run the context menu action
fileCopytoDirA(args);
}
// exit
}
static void fileCopytoDirA(string args) {
// this your part ;)
}
}
}
Btw. I took the WPF Main part from this source and it seems to be important that you remove the StartupURI="Window1.xaml" attribute from your Application.xaml, that it looks now like
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
I have a C# application that acts as a web server to the local machine only.
I have used the example from this site as the base for my web server.
So, here is my problem, the main application spawns a listener thread to listen for and handle the response.
In Program.cs
static class Program
{
[STAThread]
static void Main()
{
Application.Run(new SysTrayApp());
}
}
In SysTrayApp.cs:
public partial class SysTrayApp : Form
{
...
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
WebServer myWebServer = new WebServer(WebServer.Response, "http://localhost:8080/");
myWebServer.Run();
}
}
In WebServer.cs
...
public void Run()
{
ThreadPool.QueueUserWorkItem((o) =>
{
Console.WriteLine("Webserver running...");
try
{
while (_listener.IsListening)
{
var ctx = _listener.GetContext();
try
{
string rstr = _responderMethod(ctx.Request);
byte[] buf = Encoding.UTF8.GetBytes(rstr);
ctx.Response.ContentLength64 = buf.Length;
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
}
catch { } // suppress any exceptions
finally
{
// always close the stream
ctx.Response.OutputStream.Close();
}
}
}
catch { } // suppress any exceptions
});
}
When a request is received, I want to display a Windows Form to the local environment (not the HTTP response). The problem is that I am no longer in the main STAThread when I receive the request, so I can't open a form properly.
If I try open it in the listener thread, the form just freezes because the listener starts listening and blocks the thread. A similar thing happens if I open a threadpool thread.
public static string Response(HttpListenerRequest request)
{
Form form = new Form();
form.Show();
return "TEST!";
}
If I open it in a new normal Thread, the form pops up and then the thread closes and then the form closes again.
public static string Response(HttpListenerRequest request)
{
Thread thread = new Thread(startForm);
thread.Start();
return "TEST!";
}
public static void startForm()
{
Form form = new Form();
form.Show();
}
So, from what I can figure out, only forms in the main application/UI thread seem to work properly and also, you can't block the main thread, or else the forms freeze. So, from within the WebServer listener, how do I trigger the opening of a form on the main thread?
Should I create an event on the main form that launches the second form and try to trigger it from the listener thread?
Or is there a better way to do it?
P.S. I am a PHP/Python programmer that has had to venture into C# just for a single project, so I am not really sure what I am doing.
You can invoke a method of SysTrayApp in the worker thread as below
public class SysTrayApp : Form
{
public SysTrayApp()
{
Task.Factory.StartNew(Process, TaskCreationOptions.LongRunning);
}
void ActualWork(DateTime dt)
{
this.Text = dt.ToString();
}
void Process()
{
while(true)
{
this.Invoke((Action)(() => ActualWork(DateTime.Now)));
Thread.Sleep(1000);
}
}
}
BTW: Don't use threads from threadpool for long running tasks. Why .net Threadpool is used only for short time span tasks?