GUI and server application - c#

I have a question regarding WPF and server application. I was given a task to write a simple TCP server with a GUI. I'm new to C# (and GUIs in general), hence I have a question.
I have 2 classes:
App.xaml.cs
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow window = new MainWindow();
if (e.Args.Length != 1)
{
MessageBox.Show("Wrong number of arguments!", "An error has occured", MessageBoxButton.OK, MessageBoxImage.Error);
Environment.Exit(1);
}
window.Show();
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ShowConnectionsButton_Click(object sender, RoutedEventArgs e)
{
LogsTextBox.Text += "text\n";
}
}
that are both initially generated by Visual Studio. I assume that MainWindow.xaml is for handling GUI-related stuff and App.xaml is for application's logic. So, my (simple) question is, how should I start the server part? Should it be
server = new Server();
server.start();
window.Show();
or maybe
window.Show();
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
server = new Server();
server.start();
}).Start();
or maybe use BackgroundWorker?

Where you put the server logic will depend on how you want your UI to behave.
Usually, you will want the window to load separately with loading/retrieving data.
You could put your server call in the Loaded event. For example:
public void OnLoad(object sender, RoutedEventArgs e)
{
server = new Server();
server.start();
...
}
This will be called when the window has loaded, and can be started. How you update data bindings will depend on how your server object is built.

Related

Hosting an ASP Web Api inside a WPF app wont stop gracefully

I have an Asp Core 3.1 app hosting a rest API. It can run standalone, but I have a WPF app that I want to be able to also host the rest API while it is running.
I have gotten this working, but my problem is that when I close my last WPF window, I tell ASP's IHost to shutdown and it leaves the process open. I have recreated the problem with brand new projects with just a couple modifications:
In the WPF project, I have removed the StartupURI and use the Startup and Exit events:
private void Application_Startup(object sender, StartupEventArgs e)
{
RestApiProgram.StartAsync(CancellationToken.None).GetAwaiter().GetResult();
MainWindow window = new MainWindow();
window.Show();
}
private void Application_Exit(object sender, ExitEventArgs e)
{
RestApiProgram.StopAsync(CancellationToken.None).GetAwaiter().GetResult();
}
In the Asp project, I have modified the Program class so I can call start and stop on the IHost:
private static IHost _host;
//Main is called when running the ASP project by itself
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
//StartAsync is called by the WPF app
public static Task StartAsync(CancellationToken token)
{
_host = CreateHostBuilder(Array.Empty<string>()).Build();
return _host.StartAsync(token);
}
public static async Task StopAsync(CancellationToken token)
{
using (_host)
{
await _host.StopAsync(token);
}
}
When I close the main window, my WPF app calls StopAsync, I see the message "Microsoft.Hosting.Lifetime: Information: Application is shutting down..." in the output, but the process does not shut down. When I pause, it is stuck waiting for the StopAsync to complete. Any ideas why?
You should await the Task returned by StopAsync.
To prevent the WPF app from shutting down before the task has completed, you could handle the OnClosing event of the window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Closing += WhenClosing;
}
...
private async void WhenClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
await RestApiProgram.StopAsync(CancellationToken.None);
this.Closing -= WhenClosing;
Close();
}
}
The same goes for StartAsync:
private async void Application_Startup(object sender, StartupEventArgs e)
{
await RestApiProgram.StartAsync(CancellationToken.None);
MainWindow window = new MainWindow();
window.Show();
}
Calling .GetAwaiter().GetResult() is almost always a bad idea as it might cause a deadlock.

How to close or dipose a self host service?

I have a WPF application that host a service, the code behind is this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_host = new ServiceHost(typeof(GestorAplicacionesService));
_host.Open();
}
private ServiceHost _host;
}
I have read that it is good practice to close the service, but I don't know how to do it in this case? Because I have the main window, that if I close the application, I could close the service in the closing event. How ever, if there are some exception that could break the application that doesn't fire the closing event, then the service wouldn't be close.
So I was wondering how it would be the best way to close the service when it is hosted in a wpf application.
Thanks.
Handle the Closing event and close it there. You may also want to implement the IDisposable interface to cope with best practises for disposable fields:
public sealed partial class MainWindow : Window, IDisposable
{
private readonly ServiceHost _host = new ServiceHost(typeof(GestorAplicacionesService));
public MainWindow()
{
InitializeComponent();
_host.Open();
Closing += MainWindow_Closing;
}
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
Dispose();
}
public void Dispose()
{
_host.Close();
_host.Dispose();
}
}
This is the best you can do. If the entire process gets shut down unexpectedly, there is not much you can do about it in your WPF application. The memory will still be returned to the operating system.
you can check that all in app.xaml
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
base.OnStartup(e);
}
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
((MainWindow)Application.Current.MainWindow).host.Close();
}
protected override void OnExit(ExitEventArgs e)
{
if (((MainWindow)Application.Current.MainWindow).host.State == System.ServiceModel.CommunicationState.Opened)
((MainWindow)Application.Current.MainWindow).host.Close();
base.OnExit(e);
}
and for threading issues please follow this link : https://soumya.wordpress.com/2010/05/26/wcf-simplified-part-7-hosting-a-wcf-service-using-wpf/

Accesing WPF window properties from Web Forms page

I have a scenario, in witch i need to open new WPF window form Web Forms page, and ten bind to properities in that window and display it in update panel, so the data on web page, changes with user input in WPF window.
I tried something like this:
public partial class WorkerPanel : System.Web.UI.Page
{
private MainWindow _mainWindow;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
_mainWindow = new MainWindow();
_mainWindow.Show();
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
rptTransactions.DataSource = _mainWindow.Distributors;
rptTransactions.DataBind();
}
}
But it gives me the following error:
The calling thread must be STA, because many UI components require this.
Accoridingly to this question https://stackoverflow.com/questions/2329978 i changed my code to:
Thread t = new Thread(() =>
{
_mainWindow = new MainWindow();
_mainWindow.Show();
System.Windows.Threading.Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
With this, my web page and WPF window loads fine, but i cannot bind to this window properties, as it runs in new thread. Is such binding even possible or I should take different approach ?
I managed to do it my way using callbacks. For anyone with the same problem here's what i did:
First i added a delegate in my WPF window class
public delegate void DistributorsDataCallback(List<DistributorHandler> distributors);
private DistributorsDataCallback _callback;
then i created a new constructor for my window accepting this delegate as a parameter
public MainWindow(DistributorsDataCallback callbackDelegate)
{
InitializeComponent();
InitializeDistributors();
_callback = callbackDelegate;
}
and somwhere in code i call it with data i want to pass
_callback(Distributors);
On my Web Forms page:
Thread t = new Thread(() =>
{
MainWindow _mainWindow = new MainWindow(GetDistributorsData);
_mainWindow.Show();
System.Windows.Threading.Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
And now data can be accesed without any problems in GetDistributorsData function.
This solution should work in any multi-thread application.

wpf events between two windows

Can someone please illustrate for me how to set up a logic like this:
I have a WPF Control. When a button is pressed it does one of the two possible things.
A. It checks if a different WPF Window has been loaded. If it was, it triggers that window's Print method.
B. It checks if a different WPF Window has been loaded. If it was not, it instantiates that window and then triggers its Print method.
I struggle to understand the events system between two WPF Controls/Windows. It's a relatively new thing for me, so I would appreciate if someone walked me through this.
Ps. This is not a homework assignment, but rather a new hobby of mine. If its a totally noob question then just point me to a resource so I can educate myself.
Cheers!
First of all, what is the way by which you will check if new Window opened is what you need it to be ?
You might do this by comparing their Handle or their Type (public class MyWindowWithPrintMethod : Window).
There can be multiple ways of doing this.
I suggest my simple way, focusing on the WPF way, to solve your purpose in easiest way possible.
MyWindowWithPrintMethod obj_MyWindowWithPrintMethod;
private void btnNewWindow_Click(object sender, RoutedEventArgs e)
{
obj_MyWindowWithPrintMethod = new MyWindowWithPrintMethod();
obj_MyWindowWithPrintMethod.Show();
}
private void btnCheckNewWindow_Click(object sender, RoutedEventArgs e)
{
WindowInteropHelper tgtWindow = new WindowInteropHelper(obj_MyWindowWithPrintMethod);
foreach (Window w in Application.Current.Windows)
{
// Compare Handle
WindowInteropHelper wih = new WindowInteropHelper(w);
if (wih.Handle == tgtWindow.Handle)
{
((MyWindowWithPrintMethod)w).Print();
}
// Compare Type
if (w.GetType() == typeof(MyWindowWithPrintMethod))
{
((MyWindowWithPrintMethod)w).Print();
}
}
}
MyWindowWithPrintMethod.cs
public class MyWindowWithPrintMethod : Window
{
public void Print()
{
MessageBox.Show("Print invoked !");
}
}
This answer from this question about events from 2 windows may help:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Child childWindow = new Child();
childWindow.MyEvent += new EventHandler(childWindow_MyEvent);
childWindow.ShowDialog();
}
void childWindow_MyEvent(object sender, EventArgs e)
{
// handle event
MessageBox.Show("Handle");
}
}
Child window
public partial class Child : Window
{
// define event
public event EventHandler MyEvent;
protected void OnMyEvent()
{
if (this.MyEvent != null)
this.MyEvent(this, EventArgs.Empty);
}
public Child()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Child_Loaded);
}
void Child_Loaded(object sender, RoutedEventArgs e)
{
// call event
this.OnMyEvent();
}
}
The above code shows how to set up an event from one window to another. But, you might want to simply call a method in that other window instead. For example:
public void AddNewUser()
{
Window2 window = new Window2();
if (window.ShowDialog() == true)
{
// Update DataGrid
RefreshDataGrid();
}
}
If you are determined to stick with events, then you should read up on WPF routed events.

show message in new windows

now program show Messagebox and wait user decision.
How make, that program don't wait?
Show Messagebox, ant keep going.
(I do not need a user action. I just need to show text)
Or maybe a better option than to report the information to a new window?
I hope to understand my problem.
quick and easy way: use a BackgroundWorker to host your long running job and use the worker's events to pop up messages in the UI thread.
edit: might want to display the messages in the form of a message log.
public partial class MainWindow : Form
{
#region Constructor
public MainWindow()
{
InitializeComponent();
}
#endregion
#region Events
private void button_Click(object sender, EventArgs e)
{
listBox.Items.Add("Job started!");
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10; i++)
{
// send data from the background thread
backgroundWorker.ReportProgress(0, i);
}
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// communicate with UI thread
listBox.Items.Add(string.Format("Received message: {0}", e.UserState));
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
listBox.Items.Add("Job done!");
}
#endregion
}
Create a new Form, put some controls on it and show it to the user:
new PopupForm().Show();
If you just have to show a notification window, then this would be a help that I wrote sometime back; works like Outlook like notification window.

Categories

Resources