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?
Related
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}">
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);
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.
Forgive me if this is a bit garbled, I'm a bit new on Windows Forms, having spent months in ASP.NET
Basically, I am using Quartz.NET in my Windows Form application - when a job is executed, it fires another class file - the parameters it passes in do not contain a reference to the form, and I don't think I can change this.
What I want to do is refresh a grid on the page after the job executes - and the only place that 'tells' me a job has been executed are in other files, rather than the forms code. I can't figure out a way of accessing methods/objects on the form without starting a new instance of it, which I don't want to do.
EDIT: To sum up, I just want a way to sent a message or something to the already open Main form from another class
Why not raise event from your class to winform. Thats the elegant way to do this. To do send message, you can use interop to call sendMessage which requires handle of the window
Actualy, if members of a class were not static, you wont be able to access them without an instance of that class. Try to accuire the same instance of the class that your actions are applied on it.
The easiest way is to pass the instance of the main form to the class consuming the Quartz.NET event, so that the consuming class can then call methods on the main form. I'm guessing that class would be created in the main form somewhere anyway, so it would be something like:
var quartzConsumer = new QuartzConsumer(this);
...
class QuartzConsumer {
MainForm _form;
public QuartzConsumer(MainForm form) {
_form = form;
...
}
void OnTimer(..) {
_form.UpdateGrid();
}
}
EDIT as #hundryMind says, another solution is for the main form to subscribe to an event on the consuming class:
class QuartzConsumer {
public delegate void DataChangedEventHandler();
public event DataChangedEventHandler DataChanged;
void OnTimer(..) {
if (this.DataChanged != null) this.DataChanged();
}
}
// in MainForm:
var quartzConsumer = new QuartzConsumer(..);
quartzConsumer.DataChanged += this.OnDataChanged;
...
void OnDataChanged() {
// update the grid
}
What is the best design decision for a 'top-level' class to attach to an event to a class that may be '5+ layers down in the callstack?
For example, perhaps the MainForm has spawned an object, and that object has spawned a callstack of several other object calls. The most obvious way would be to chain the event up the object hierarchy, but this seems messy and requires a lot of work.
One other solution ive seen is to use the observer pattern by creating a publically accessible static object which exposes the event, and acts as a proxy between the bottom-level object, and the top-level 'form'.
Any recommendations?
Here's a pseudo-code example. In this example, the MainForm instantiates 'SomeObject', and attaches to an event. 'SomeObject' attaches to an object it instantiates, in an effort to carry the event up to the MainForm listener.
class Mainform
{
public void OnLoad()
{
SomeObject someObject = new SomeObject();
someObject.OnSomeEvent += MyHandler;
someObject.DoStuff();
}
public void MyHandler()
{
}
}
class SomeObject
{
public void DoStuff()
{
SomeOtherObject otherObject = new SomeOtherObject();
otherObject.OnSomeEvent += MyHandler;
otherObject.DoStuff();
}
public void MyHandler()
{
if( OnSomeEvent != null )
OnSomeEvent();
}
public event Action OnSomeEvent;
}
If your application isn't based on Composite UI Application Blocks, the easiest solution is to put a "listener" class between Main form and your other components which both classes can easily access. Conceptually, the classes are laid out as follows:
---------- ----------------
| MainForm | | Some Component |
--------- ----------------
| |
Hooks onto Notifies
| |
\ /
-----------------
| Proxy Notifier |
-----------------
Here's some example code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
FakeMainForm form = new FakeMainForm();
form.CreateComponentAndListenForMessage();
Console.ReadKey(true);
}
}
class FakeMainForm
{
public FakeMainForm()
{
Listener.AddListener(MessageRecieved);
}
void MessageRecieved(string msg)
{
Console.WriteLine("FakeMainForm.MessageRecieved: {0}", msg);
}
public void CreateComponentAndListenForMessage()
{
ComponentClass component = new ComponentClass();
component.PretendToProcessData();
}
}
class Listener
{
private static event Action<string> Notify;
public static void AddListener(Action<string> handler)
{
Notify += handler;
}
public static void InvokeListener(string msg)
{
if (Notify != null) { Notify(msg); }
}
}
class ComponentClass
{
public void PretendToProcessData()
{
Listener.InvokeListener("ComponentClass.PretendToProcessData() was called");
}
}
}
This program outputs the following:
FakeMainForm.MessageRecieved: ComponentClass.PretendToProcessData() was called
This code allows you to invoke methods directly on any listener, no matter how far apart they are in the call stack.
Its easy to rewrite your Listener class so that its a little more generic and works on different types, but you should get the idea.
My initial intention would be to try and avoid that, so that an object's scope has obvious boundaries. In the particular case of Forms, I would attempt to have the child's parent form manage all required communications withs its ancestors. Can you be more specific about your case?
My first thought is that from your MainForm's perspective, it should have no idea what is going on 5 levels down. It should only know about its interactions with the object that it spawned.
With that, if you main form wants to perform some action asynchronously, it should be able to do that by calling a method on the spawned object asynchronously.
Now from your spawned object's point of view, if you allowed your caller to perform some method asynchronously, there's no need to push the event model further down... just call the methods directly down the stack. You're already on another thread.
Hopefully that helps a little. Just remember the levels of your app should only be aware of what goes on in the level immediately below them.
WPF uses routed events. These are static and can bubble up or tunnel down the element tree. I don't know if you are using WPF, but the idea of static events might help you out.
I wouldn't say this is a design fault, there are valid reasons for the main form to want to listen to what an object is doing. One scenario I've encountered is displaying status messages to the user to indicate what background processes are doing, or what multiple controls are doing in a multi-threaded app that lets you have multiple screens/"pages" open at once.
In the Composite UI Application Block, the basic equivalent of a dependency injection container wires up events when its instantiating objects in the same work item (a work item is just an object container for a group of related user controls). It does this by scanning for special attributes such as [EventPublication("StatusChanged")] on events and [EventSubscription("StatusChanged")] on public methods. One of my applications uses this functionality so that a user control instantiated way down in the innards of the application can broadcast status information (such as "Loading customer data...45%") without knowing that that data is going to end up in the main form's status bar.
So a UserControl can do something like this:
public void DoSomethingInTheBackground()
{
using (StatusNotification sn = new StatusNotification(this.WorkItem))
{
sn.Message("Loading customer data...", 33);
// Block while loading the customer data....
sn.Message("Loading order history...", 66);
// Block while loading the order history...
sn.Message("Done!", 100);
}
}
...where the StatusNotification class has an event with the a signature like
[EventPublication("StatusChanged")]
public event EventHandler<StatusEventArgs> StatusChanged;
... and the above Message() and Dispose() methods on that class invoke that event appropriately. But that class didn't explicitly have that event hooked up to anything. The object instantiator will have automatically hooked up the events to anybody with a subscription attribute of the same name.
So the MainForm has an event handler that looks something like this:
[EventSubscription("StatusChanged", ThreadOption=ThreadOption.UserInterface)]
public void OnStatusChanged(object sender, StatusEventArgs e)
{
this.statusLabel.Text = e.Text;
if (e.ProgressPercentage != -1)
{
this.progressBar.Visible = true;
this.progressBar.Value = e.ProgressPercentage;
}
}
... or some such. It's more complicated than that since it will rotate through multiple status notifications for a given number of seconds since multiple user controls can be broadcasting status messages around the same time.
So to recreate this behavior without actually switching over to CAB (which, to be honest, is much more complicated than I think it really needs to be), you could either have a MessageNotificationService object that you pass around your application or that you turn into a static/singleton object (I usually avoid this approach since it's harder to test), OR you could have you sub usercontrols be instantiated by a factory class that does the event wiring up for you. Objects could register with the factory by attributes of your own creation or by explicitly calling methods that say "hey, anytime you create an object with an event of this signature, I want to know about it."
Just be careful to have whatever class you implement unhook the events when an object gets disposed because it's stupid easy in this scenario to end up with something that won't get garbage collected.
Hope this helps!