WPF - Can I add a click event to multiple windows? - c#

I'm stumbling my way through my first WPF desktop application using C# and am trying to stick to good programming practice by not repeating code. I've come a little unstuck when trying to add an event handler to buttons in different windows.
I have two windows (named 'MainWindow' and 'ViewContent') which both contain buttons to exit the application.
The buttons are both identical in XAML, and are created in separate windows:
<Button Click="Exit_Application" />
The event handler for a button click will then run:
public void Exit_Application(object sender, RoutedEventArgs e)
{
// Exit the application
System.Windows.Application.Current.Shutdown();
}
This works when I include the 'Exit_Applicaiton' method in the code-behind for both windows, but I was hoping to only have to include this method once and be able to use it globally. I've searched around and can't seem to find any information on using click event handlers globally, Is this possible?
Any help would be greatly appreciated.

There are a few ways to handle this scenario (listed below), however I would recommend looking into the MVVM pattern to help clarify how to structure your application.
1. Shared method within App
Create a public method (example below) within the App class (typically App.xaml.cs in the project root). The App class is accessible from anywhere within your application so could be used to share exit logic that can be triggered from multiple <Window\>.
public void Shutdown()
{
// Insert any code that needs to run before shutdown
System.Windows.Application.Current.Shutdown();
}
From within each <Window\> you could call App.Shutdown() to exit the application.
2. Decide code sharing is not needed for this case
If the only line of code that needs to run when a user exits your application is System.Windows.Application.Current.Shutdown(); then there is no need to "share" this code. There is no logic within this code therefore calling this method from both <Window\> could be just fine.

The fact that you're using click events on buttons and asking the question means you don't understand commands in wpf. Commands in particular, binding and resources are the big plusses of using WPF. If you're not using these then that's ok for a trivial application but commercial teams use MVVM and even a hobby app of any substance will benefit from adopting this.
There is, however, a learning curve and if you're only ever writing one app then learning MVVM might not be worthwhile.
Either way, you can use a command by binding the command property of your buttons to a class implements icommand.
Usually, a command will be doing something or other with data and you'd be working with a viewmodel so a simple command would be in a property of a viewmodel like:
https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx
In this case your command doesn't need any data and is just working with the app. Which you can do from any piece of code.
You can therefore use a static class implements icommand. That could be just directly in the class or by using a library already does this for you - like MVVM light.
From this thread:
WPF Commands, How to declare Application level commands?
Here's a simple implementation:
class MyCommand : ICommand
{
// Singleton for the simple cases, may be replaced with your own factory
public static ICommand Instance { get; } = new MyCommand();
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
System.Windows.Application.Current.Shutdown();
}
}

you can declare in MainWindow (or another class) static ICommand ExitAppCommand and use it
class:
public static ICommand ExitAppCommand { get; } = new ActionCommand(() => System.Windows.Application.Current.Shutdown());
XAML:
<Button Command="{x:Static youClassNamespace:youClass.ExitAppCommand}">

Related

How to make a listener of a service on android

I have this code to check if a service is active, but I would like to know if there is any way for an EditText to show the status of the service, without making this query per second, or in a separate thread, or linking it in some way? that it is possible to detect if the service stopped
private bool MiServicioEstaCorriendo(Class #class, Context contexto)
{
ActivityManager manager = (ActivityManager)contexto.GetSystemService(Context.ActivityService);
foreach (ActivityManager.RunningServiceInfo service in manager.GetRunningServices(Integer.MaxValue))
{
if (#class.Name.Equals(service.Service.ClassName))
{
Log.Info(typeof(BroadcastGps).Name, "MiServcicioEstaCorriendo: true");
return true;
}
}
Log.Info(typeof(BroadcastGps).Name, "MiServcicioEstaCorriendo: false");
return false;
}
You are basically in need of a way to pass events/messages among classes within your application. So this question probably goes down to Android & C# implementation of such a pattern. Xamarin.Forms has a MessagingCenter class, but since you are using Xamarin.Native, you would have to create something yourself
There's nothing actually already baked in to Android or C#, but so you can implement one of the most common ways to let a class spread an event/message using the "Listener" (term used in Android) or "Delegate" (term used in C#) technique.
There are frameworks too like PubNub that you can use as Nuget packages that simplify everything for you.
Some more resources to understand the concept: Wikipedia, IBM.
And Some Android resources: Handlers, AsyncTasks, Parcelables.
Don't forget that your event to update your EditText may not be fired on the Main UIThread so you won't be able to see the changes unless you force that update line to be Run on UI Thread.

How to react differently to custom events, depending on the calling class?

I'm lost in how to design the best class architecture for my problem. I use a CardReader object, which throws the following events:
// CardReader has read a card
public event CardReader.CardReadHandler CardRead;
// CardReader has a log message which can be processed
public event CardReader.LoggingHandler CardReaderLogging;
// A new hardware is connected to the computer
public event CardReader.ConnectHandler ReaderConnect;
In my WPF application, I would like to react differently on these events. For example:
In a MainWindow, the CardRead event must lead to some kind of processing (card ID -> backend etc.)
In a ConfigurationWindow, the CardRead event only leads to a "Success"-Message or something
I hesitate to process those events in the window classes, it feels as if it belongs to an own processing class or something. Currently, I wrote a wrapper class, which takes for example the MainWindow and is then able to execute MainWindow methods (because it keeps a private reference to the calling MainWindow):
public MainWindow()
{
InitializeComponent();
this.cardReader = new CardReader(this);
}
If I continue this thought, this would lead to separate CardReader wrapper classes, each taking a different kind of window:
public ConfigurationWindow()
{
InitializeComponent();
this.cardReader = new CardReader(this);
}
Do you have a suggestion here?

How would you go about detecting events defined by libraries in other classes?

So I'm working with SdlDotNet - which basically converts SDL calls into what C# should look like and I ran into an issue.
That issue being that because the SdlDotNet is running in a different class to the main part of my application - I can't detect when it's closing.
The SdlDotNet library has an event that fires when it is told to close, and that event is:
SdlDotNet.Core.Events.Quit
In the object viewer - the event is shown as such:
public static event System.EventHandler<QuitEventArgs> Quit
Member of SdlDotNet.Core.Events
What I've done, is there is a main Windows form application that calls upon the SDL class like so:
private void drawToScreen()
{
//Starts the SDL off drawing to the screen
SDLDraw sdl = new SDLDraw();
sdl.startDrawing();
//How would I go about detecting SdlDotNet.Events.Quit
//From the class I've instanced
//When I was on my original Windows Forms implementation
//It worked like this:
////sdl.FormClosed += new FormClosedEventHandler(detectClose);
//But just copying that structure and trying
////sdl.Events.Quit += new QuitArgs(detectClose);
//Doesn't have the same effect, because sdl does not contain a definition for 'Events'
}
private void detectClose(object sender, QuitArgs e)
{
MessageBox.Show("SDL closed!")
}
So, I guess the question is how do I listen for Events.Quit firing in the class I called from the class I called it from?
Thanks in advance!
The declaration reveals that this is a static event, therefore it is associated with the class, not an instance. Use
SdlDotNet.Core.Events.Quit += new QuitArgs(detectClose);

Executing UI Code from ViewModel on MVVMCross

I have just started using MvvmCross, but i didn't find any info about to how i can execute UI code from a ViewModel.
On Caliburn there are coroutine so i can access the view and keep the ui code separated from the viewmodel code.
on my first case i need to open a dialow from a command inside a ViewModel, what is the correct way?
Right now i'm developing a WinRT app.
Thanks
There isn't any hard/fast rule on this within MvvmCross.
Generally, when I need to do this I use the Messenger plugin.
This answer assumes you are using the latest Alpha v3 code. For older vNext code you'll have to do some translation - see notes below.
To use this approach:
I reference Cirrious.MvvmCross.Plugins.Messenger.dll from both Core and UI projects.
Then I add a line somewhere in Setup.cs (e.g. in InitializeLastChance) to:
Cirrious.MvvmCross.Plugins.Messenger.PluginLoader.Instance.EnsureLoaded();
Then in the Core project I add a message:
public class InputIsNeededMessage : MvxMessage
{
public InputIsNeededMessage(object sender) : base(sender) {}
}
In the ViewModel I can get the Messenger by constructor injection or by:
var messenger = Mvx.Resolve<IMvxMessenger>();
and I can send messages by calling:
messenger.Publish(new InputIsNeededMessage(this));
In the View I can again get to the messenger and subscribe to messages using:
var messenger = Mvx.Resolve<IMvxMessenger>();
_token = messenger.SubscribeOnMainThread<InputIsNeededMessage>(OnInputIsNeeded);
where _token must be a member variable - if it isn't then the subscription won't persist - the subscription itself is weak by default (so you never have to unsubscribe)
and where OnInputIsNeeded is something like:
private void OnInputIsNeeded(InputIsNeededMessage message)
{
if (message.Sender != ViewModel)
return;
// do stuff here - you are already on the UI thread
}
The above sequence is what I normally do for 'proper code'
To start with using a Messenger/EventAggregator can feel uncomfortable - it certainly took me a while to get used to it - but after I did get used to it, then I now use it everywhere - the pub/sub Message decoupling is very flexible for testing and for future maintenance of code (IMO)
As alternatives to this approach above I do sometimes take shortcuts:
sometimes I fire normal C# events from the ViewModel and have the View respond to these
sometimes I have special marker properties and fire the UI code from them
Sorry for using v3 syntax - but the changeover is coming and it's what I'm now coding in...
To switch back to vNext I think you might need to:
use IMessenger instead of IMvxMessenger
use BaseMessage instead of the MvxMessage
use Subscribe instead of SubscribeOnMainThread - but then you will need to marshall the message onto the UI thread yourself.
There exists an easier way. Here is the method I use for executing any action on the main
thread:
protected void RunOnUIThread(Action action) {
var dispatcher = Mvx.Resolve<IMvxMainThreadDispatcher>();
dispatcher.RequestMainThreadAction(action);
}
Hope it helps. Cheers.

Trying to close a form after the next one is shown in C# cf

I've been studying Android lately and I tried to port one of its functions to C# compact framework.
What I did is create an Abstract class that I call Activity.
This class looks like this
internal abstract class Activity
{
protected Form myForm;
private static Activity myCurrentActivity = null;
private static Activity myNextActivity = null;
internal static void LoadNext(Activity nextActivity)
{
myNextActivity = nextActivity;
if (myNextActivity != null)
{
myNextActivity.Show();
if (myCurrentActivity != null)
{
myCurrentActivity.Close();
myCurrentActivity = null;
}
myCurrentActivity = myNextActivity;
myNextActivity = null;
}
}
internal void Show()
{
//PROBLEM IS HERE
Application.Run(myForm);
//myForm.Show();
//myForm.ShowDialog();
//
}
internal void Close()
{
myForm.Close();
}
internal void GenerateForm()
{
///Code that uses the Layout class to create a form, and then stores it in myForm
//then attaches click handlers on all the clickable controls in the form
//it is besides the point in this problem
}
protected abstract void Click(Control control);
//this receives all the click events from all the controls in the form
//it is besides the point in this problem
}
The problem I have is with running the part of the Show() command
Basically all my classes implement the above class, load an xml file and display it.
When I want to transition to a new class/form (for example going from ACMain to ACLogIn)
I use this code
Activity.LoadNext(new ACLogIn);
Which is supposed to load the next form, show it , and unload the current form
I have tried these solutions (in the Show() method) and here is the problem with each one
using myForm.ShowDialog()
This works, but blocks execution, which means that the old form does not close, and the more I move between the forms the more the process stack increases
using myForm.Show()
This works, closes the old form after the old one is shown, but immediately after that closes the program and terminates it
using Application.Run(myForm)
This works only on the first form loaded, when I move to the next form, it shows it then throws an exception saying "Value does not fall within the expected range"
Can someone help me fix this or find an alternative?
If you're really after creating your own framework for this navigation, you need to re-work you thinking. The Form instance passed into Application.Run must never close - when it does, Application.Run finishes execution and (typically) your static void Main entry point exits and the app terminates.
What I would propose is that you change your Activity to either being a UserControl:
public abstract class Activity : UserControl
{
....
}
or Composing one
public abstract class Activity
{
private UserControl m_control;
....
}
Then instead of closing and showing Forms, parent all of the Activities inside the main Form as a container.
As fair warning, this is going to get complex when you start wanting to show things in a Tab motif instead of a Stack, or having split views. Frameworks seem simple to create, but they're not so I'd at least consider using something already done unless you have compelling reasons to want to roll your own.
Application.Run is generally used with the overload that takes a Form parameter. This would be the "main" form that would be responsible for starting/showing other forms. This "main" form could be "hidden". But, I think that's a little awkward.
Alternatively, you don't need a main form, you can use Application.Run() to start a message pump to process Windows messages; but, then the thread is busy processing messages and cannot show dialogs (they must be shown in the thread that is running Application.Run). You can get around this by creating one or more form objects before calling Application.Run and these form objects could create a Timer object that would call Form.Show() or Form.ShowDialog() on the Timer.Tick event handler so that for form is shown after the call to Run. I think this is a little awkward as well.
Both of these solutions kind of circumvent the way you're expected to use Windows and WinForms; so, I think you need to think about re-designing this application to work with the way that Windows and .NET works.

Categories