Most of the functions in my WinForms application have a key press alternative. There are a lot of them. At the moment I am handling this in a switch statement. My code analyzers tell me that this code is un-maintainable to varying degrees of nasty. I have to agree. I would extract the handling code out to separate methods but in most cases this is only a line or two and it does not really help me get a handle on things.....
Is there are better way to handle this of am I stuck with a huge switch?
Thanks in advance
Somewhere in the code, soon or later, you will need to make swicth/case or if/else, or whatever. So pushing actual handling code for every Key or Key combination to separate function is already good step.
You may be can other "nice" stuff: make a dictionary where
Key: is keyboard key
Value: delegate to call on that key press
And basically what you will need to do is to say, pseudocode!!
dictionary[key].Invoke();
Hope this will give some hints.
Regards.
You could use a Dictionary to map different key presses to functions, and add to that Dictionary wherever it made sense structurally. For a very simple example:
public partial class Form1 : Form
{
Dictionary<Keys, Action<object, KeyEventArgs>> KeyPressLookup;
public Form1()
{
KeyPressLookup = new Dictionary<Keys, Action<object, KeyEventArgs>>();
KeyPressLookup[Keys.F10] = (o, e) => MessageBox.Show("You pressed F10");
InitializeComponent();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if(KeyPressLookup.ContainsKey(e.KeyCode) // Could use TryGetValue instead
KeyPressLookup[e.KeyCode](sender, e);
}
}
This code has the limitation that only one function can be assigned to a key, which may be good or bad. Of course you could make a Dictionary of Lists or "MultiDictionary" if you want multiple functions per key.
Alternatively, you could have multiple Dictionaries, and have them be used depending on the circumstances, such as user options or the current "mode" (depending on how your application is structured).
Using lambda functions the resulting code overall can be much more concise than defining a "regular" function for every key.
You might also want to come up with a concise way to define functions mapped to key combinations such as Ctrl-F10. How far you want to go would depend on the size of your application.
Implement a Command system (Like WPF).
If you seperate the logic of all your functions into individual Commands you can then attach these commands to your menu items etc
take a look at http://www.ageektrapped.com/blog/using-the-command-pattern-in-windows-forms-clients/
If you modify that code slightly so that you add the command.Key into the Dictionary examples given in the other answers you get your solution.
Alternatively each Command could contain a delegate method for the Form_keydown event that you attach when you attach the command and you can then analyse the keypress in the command and choose to process if it matches.
Related
I read about events and delegates. I think I understand how they work but I do not understand why should I use them.
For example, I have online shop where customer has balance and is buying products using this money.
This is enough to do the job.
When user buys something, sells something or deposits money - UserWallet class is called.
class Shop
{
public static void BuyOrderFilled(){
if(userHasBalance()){
UserWallet userWallet = new UserWallet();
userWallet.DeductMoney();
}
UpdateInventory();
}
public static void SellOrderFilled(){
//Sell order has different logic
if(userHasProduct()){
UserWallet userWallet = new UserWallet();
userWallet.RemoveProductFromUser();
}
UpdateInventory();
}
}
class Deposit{
public static void UserGotDeposit(decimal amount){
UserWallet userWallet = new UserWallet();
userWallet.FillUserBalance(amount);
}
}
class UserWallet{
public void DeductMoney(){
//Some logic
}
public void RemoveProductFromUser(){
//Some logic
}
public void FillUserBalance(){
//Some logic
}
}
public class Main(){
Shop.BuyOrderFilled();
Shop.SellOrderFilled();
Deposit.UserGotDeposit(100);
}
Why should I use Events or delegates when I can just call UserWallet methods whenever I need them?
You don't have to use them..
..it's just that sometimes it's super handy to be able to pass methods around like they were data.
Quite often the utility in this is if you're providing a library for other people to use and you want to make it useful for them but you don't know a thing about their code or how they'll use it. An obvious one is List<T> in the framework; you might be writing something like it and want to provide a way for people to search if, but you don't know what kind of objects they will put in the list or how they want to search for them.
If, however you just provide them with a method like Find(delegate) then it's a "method that takes a method as a parameter" - you declare to the user "provide Find with a method that takes a T and returns a boolean of true if it should be included in the search results", then it means they can write a method like this:
bool IsSmith(Person p) {
return p.LastName == "Smith";
}
And they can pass it to your list Find method, your method runs their method, gives it the List object, gets a bool and decides what to do based on the result
var smiths = myListOfPeople.Find(IsSmith);
Nowadays we don't usually write the method out so long hand, we use the funky inline declaration where we just provide the parameter name and the logic, and the compiler inserts all the other stuff it can work out
var smiths = myListOfPeople.Find(p => p.LastName == "Smith");
In essence you can control every part of the process when you wrote your List class, you can implement the find, you can return the results - but in making your List truly flexible and letting people store what they want in it, you create a gap in the middle where you can't know how to search for a Whatever that the user put into it. Being able to let them pass a method in (of a known arguments type and return type) that you can call closes that gap
Another example; events this time but they're no different. An event is just a list of methods that the user of your class can fill with "bits of code that shall be run when something happens"
Take a button click: you want to download a file, your coworker wants to save an image, I want to calculate factors of a number entered in a text box.. We're all using that same Button class but we all want to do different things when we click our different buttons, so the easiest way for Microsoft to make the perfect button is to just leave that "do this when clicked" part for us to fill in, and that's done by having a way to associate a delegate (method passed round like a variable) with the button, and coding the button so that it runs the delegate when it's clicked, whatever the delegate may do
So all that is great for Microsoft, who create Buttons and Lists and other generic things for us to enjoy, but does it have a place for us in our own code? Sure, though more rare, I find it helpful to make some helper class for example- something that launches ffmpeg and reads from its output stream.. and mostly it's just nonsense but occasionally interesting messages are sent, so I might make an event that I raise when such a message is sent.. I use my helper in one project and I'm looking for dropped frames, in another I want to know if silence is detected.. In those cases I suppose the "person who provides the library to the person who consumes the library" was me at both ends. I have other projects where I want to perform similar tasks on different data, the writer routine is the same but the parsing is different; being able to pass a method to say how to pull a name out of this object but a number out of that one makes life a lot nicer than having some massive "if object is a person pull the name, else if object is a building pull the number and street" conditional block in a place it doesn't belong
We have a case where we have two externally-defined RoutedUICommand objects (i.e. we don't own them thus we can't change them) which come pre-wired to the same CTRL+N key binding. We can address that in our code by manually setting the InputBindings we want, like so (Note: AddMany is my convenience extension that takes a param array):
private void InitializeCommands(){
// Both NewFooCommand and NewLaaCommand define CTRL-N as their shortcut,
// so we have to explicitly assign the shortcuts we want for each
// Note: You should explicitly specify both as the system may 'choose' the wrong one to
// handle the default case. i.e. we can't just specify NewLaaCommand for the new shortcut
// because the system may also wire up CTRL+N to NewLaaCommand so it would now respond to
// both shortcuts while NewFooCommand now wouldn't respond to either.
InputBindings.AddMany(
new InputBinding(NewFooCommand, new KeyGesture(Key.N, ModifierKeys.Control)),
new InputBinding(NewLaaCommand, new KeyGesture(Key.N, ModifierKeys.Control | ModifierKeys.Alt))
);
CommandBindings.AddMany(
new CommandBinding(NewFooCommand, (s, e) => MessageBox.Show("New Foo!")),
new CommandBinding(NewLaaCommand, (s, e) => MessageBox.Show("New Laa!"))
);
}
This works fine and addresses that part of our issue so we can now use shortcuts as we see fit.
The issue we're still trying to address is we have global styles for buttons which automatically apply their tool tips to show their assigned shortcut keys. It's based on what's specified on the command itself, which in this case is of course CTRL+N so that's what they both show, even though one is now CTRL+ALT+N thanks to the manually-specified InputBinding.
Now while we could go around and hard-code the tool tips, I'm wondering if there's a way to instead of asking the command what it's binding/bindings is/are, if we can somehow query the system to see which current key bindings are in effect relative to a given control (i.e. the button.) However, I'm not sure how to get that information without having to manually walk the visual tree searching InputBinding collections for the specific RoutedUICommand.
Is it possible to ask the Input system which keybinding is actually in effect, not just which one is defined on the RoutedUICommand?
A 'hack' alternative is to just define our own 'mirror' commands with the correct key bindings so the style correctly updates the tool tip, then we simply delegate most actions to the actual command. Like I said, it's a hack, but it would work.
I am attempting to subscribe delegates to an event held in a List<KeyValuePair<KeyEnum, Delegate>
The goal is to associate a series of Handlers to keyboard keys and named axes, both of which are represented by Enums
Dispatching is fairly easy, I just iterate over the list of KVPs, check a condition, and if the condition is met, call the delegate simply with member.Value; I haven't encountered any issues with efficiency in processor time with this, and in fact, have found that it's significantly cleaner on the stack.
The issue is adding to the delegates after instantiated. Attempting to access it with collection.FirstOrDefault(n=>n.Key == KeyEnum.W).Value+= SomeMethod doesn't work, as Value is read only.
Is there a way to do this that doesn't require creating a new KeyValuePair every time, or a better solution than KeyValuePair in general
Just use a Dictionary<KeyEnum, Action>. I don't see why you would need sequential access of the KVPs. You should also specify the delegate type corresponding to the event handlers if you can. Action or EventHandler or Action<Something> depending on your needs.
Then you can easily add and call delegates:
// adding delegates
if (dictionary.ContainsKey(KeyEnum.W)) {
dictionary[KeyEnum.W] += SomeMethod;
} else {
dictionary.Add(KeyEnum.W, SomeMethod);
}
// calling delegates
if (dictionary.ContainsKey(KeyEnum.W)) {
dictionary[KeyEnum.W](...);
}
I'm writing a particle system and have a hierarchy of one emitter containing many particles.
Particles fire an event on collision with the world:
public event EventHandler<HitWrapperArguments> onHitCallback;
I am in the process of having the emitter subscribe to each particle so it can fire an event when any particle collides:
public event EventHandler<ParticleHitWrapperArguments> onParticleHitCallback;
My question is:
Should my ParticleHitWrapperArguments contain the HitWrapperArguments, or should I unpack HitWrapperArguments and put the collision information directly into ParticleHitWrapperArguments?
Personally it doesn't matter too much to me, but I would go for 2.
It just looks cleaner and I think it will be easier in use. It does need additional coding which could result in extra mistakes, but I wouldn't worry to much about it.
There is no correct answer but I would say use option 1.
It is less work and thus less error prone, and it will leave you with a structure resembling the order of creation which will make it easier to debug (think inner exceptions). If you change your structure you would have to perform minimal changes on the wrapping args class, just one example of how it is better design.
If you have to do any processing on the arguments at this stage however, then of course perform these operations and store the new results in your new args object.
I'm trying to use events to use the nice += -= syntax.
Here is my problem:
I have a lot of different events, so instead of creating many events, I would like to use only one,
where the registrant additionally supplies an additional parameter (string[]) detailing which (sub)events one wants to listen to.
The is basically what I'm doing now:
public delegate void dMethod(int index);
private Dictionary<string, dMethod> events;
public void RegisterEvents(dMethod m, string[] subEvents){
foreach(string subEvent : subEvents){
events[subEvent] += m;
}
}
Instead I would like to do something like this:
public delegate void dMethod(int index);
private Dictionary<string, dMethod> events;
public event dMethod Events(string[] subEvents) {
add {
foreach(string subEvent : subEvents){
events[subEvent] += value;
}
}
remove {
foreach(string subEvent : subEvents){
events[subEvent] -= value;
}
}
}
Is something like passing additional parameters upon event registration somehow possible?
The simplest solution I thought of was to use the delegates return value (changing it to string[]).
But that is just hacky and not worth it.
If it is not possible, is there some way I can define += -= myself?
Why exactly don't you want to use one event per... event ?
What you're trying to do would add an indirection step (which will hurt performance), and more importantly deny you the advantages of compile-time error detection and code analysis (what if your string doesn't match an existing event? wouldn't it be nice to find out about the error at compile-time rather than runtime? What about the "find all references" tool?)
If all of your events are really separate events (which have nothing to do with one another), you should create those events. Creating one single event for all kinds of things would be a bit like creating one big sql table with 255 Columns instead of a true database design.
On the other hand, you may be able to find an abstraction of all those events, and add an argument to the event handler (not to the subscription logic) specifying what sub-event has been called. See IBindingList for an example of this technique. Note the use of an enumeration instead of strings to avoid spelling mistakes.
Could you give us some example (business-wise) of the events you're working on ?
The simplest solution I thought of was
to use the delegates return value
(changing it to string[]).
I dont quite understand what you mean by this. Would the event handler return the events it wants to handle?
I don't think that what you want to do is an option. It may be possible (if you abandon the .Net event system) to use the += syntax by creating a custom class and some implicit conversion operators to produce code something like:
SomePropertyThatIsntARealEvent[new string[] {"event1", "event2"}] += someDelegate
// or even
SomePropertyThatIsntARealEvent["event1"]["event2"] += someDelegate
Note that, if it's even doable, it would be messy and hacky and not really worth it anyways. Also, unfortunately, you can't use params for indexers.
I would recommend that you just stick with the un-pretty way.
This isn't an answer to your main question, but for ease of use I'd add the "params" keyword to your method, like so:
public void RegisterEvents(dMethod m, params string[] subEvents)
This will let you call the method like this:
RegisterEvents(m, "Bob", "Doug", "Susie");
instead of:
RegisterEvents(m, new string[] { "Bob", "Doug", "Susie" });