|
I've often wondered about this before but my google searches aren't revealing much.
In most modern games, you have a set of keyboard shortcuts that do various things in the game. You can generally remap these keys at runtime with ease. If you don't like pulling up the Map in the game with the 'M' key just re-map it to Tab for example.
Most of what I've found in my searches involves assigning specific keyboard keys to specific menu items, setting KeyPreview to True and having a BUST (big ugly switch statement) or some low end keyboard hook that forces you to do essentially the same thing. I'm hoping for something I bit less messy than that.
I'm wondering if anyone knows a decent library to do stuff like this. Obviously I could write something that sits there monitoring every keypress and checking for key combinations in a dictionary or something. But I'd rather not reinvent the wheel if I can help it.
Anyone have any suggestions?
Thanks a lot
E
|
|
|
|
|
Just to make sure I understand what you want:
You want to catch keys (probably by registering a keyboard hook or by RegisterHotKey), and then re-issue another key (emulate another key press)?
(if that is the case, you may have problems on Win8+, but I want to know if this is indeed what you want)
Best,
John
-- Log Wizard - a Log Viewer that is easy and fun to use!
|
|
|
|
|
I would imagine that you're going to have a dictionary of some sort in there - and you'll map that to your commands. Off the top of my head, you'd do a two phase approach to building this up. In the first phase, you'd have your application commands (looking something like this):
public class ApplicationCommand
{
public char Shortcut { get; set; }
public Action Command { get; set; }
} You would populate these as a default stage (possibly in a dictionary), and then read through some configuration to remap those elements that need to be updated:
public class CommandMap
{
private Dictionary<string, ApplicationCommand> applicationCommands = new Dictionary<string, ApplicationCommand>();
public void AddCommand(string application, ApplicationCommand command)
{
Add(application, command);
}
}
public class MyGameLoop
{
private CommandMap commandMap = new CommandMap();
public void Initialize()
{
commandMap.AddCommand("Load", new ApplicationCommand('L', ()=> { Debug.WriteLine("Loading"); }));
commandMap.AddCommand("Save", new ApplicationCommand('S', ()=> { Debug.WriteLine("Saving"); }));
}
public void UpdateEntries()
{
ConfigurationStoreReader reader = new ConfigurationStoreReader();
foreach (Tuple<string, char> element in reader.CommandUpdateMap)
{
command[element.Item1] = element.Item2;
}
}
public CommandMap Mapping { get { return command; } }
} From that, we have everything in place that we need to expose a key press to Action map
public class Game
{
private Dictionary<char, Action> actionToPerform = new Dictionary<char, Action>();
public Game(CommandMap mapping)
{
foreach (ApplicationCommand appCommand in mapping.Mapping)
{
actionToPerform.Add(appCommand.Shortcut, appCommand.Command);
}
}
public void HandleKeyPress(char keyPress)
{
if (actionToPerform.ContainsKey(keyPress))
{
actionToPerform[keyPress]();
}
}
} I've just typed this up in the web editor here, so apologies if there are minor syntactic issues but this should serve to demonstrate what I'm talking about.
This space for rent
|
|
|
|
|
Pete O'Hanlon wrote: I've just typed this up in the web editor here And it's almost ready for publication as a Tip.
|
|
|
|
|
Thanks Richard. I enjoyed the mental challenge of keeping the moving parts in place while typing it out in the editor. It removes any distractions from the IDE and Intellisense.
This space for rent
|
|
|
|
|
I have the following dynamically added user control that is loaded during Page_Init
foreach (WSEmployerServices.PensionContributionDetail objWSPenDetailFEL in objArrXMLPensionDetails)
{
try
{
AspEmployeeDetailsPension ctlEEDPensionControl = (AspEmployeeDetailsPension)Page.LoadControl("~/AspEmployeeDetailsPension.ascx");
ctlEEDPensionControl.ID = "ctlEEDPensionControl" + objWSPenDetailFEL.PenUploadDetailId;
ctlEEDPensionControl.EEDControlLanguage = Master.MasterStrLanguage;
ctlEEDPensionControl.EEDControlResourceManager = Master.MasterResourceManager;
ctlEEDPensionControl.strIsCurrentPayroll = mobjXmlPayrollHeader.IsCurrentPayroll;
ctlEEDPensionControl.intEmpDetailId = Convert.ToInt32(objXMLPayrollEmployee.EmpUploadDetailId);
ctlEEDPensionControl.intOrgId = Convert.ToInt32(objXMLPayrollEmployee.EmployerOrgId);
ctlEEDPensionControl.intOrgStatusId = mintOrgStatusId;
ctlEEDPensionControl.intUserId = mintUserId;
ctlEEDPensionControl.intPyrllStmtId = mintPyrllStmtId;
ctlEEDPensionControl.objArrRoleAccModSelXml = mobjArrRoleAccModSelXml;
ctlEEDPensionControl.EEDPensionDetail = objWSPenDetailFEL;
ctlEEDPensionControl.SetupControl();
EEDPensionUserControlArea.Controls.Add(ctlEEDPensionControl);
}
catch (Exception ex)
{
HandleError(ex);
}
}
When I do a post back the data disappears from the control. I thought that the viewstate would repopulate the data. Any help would be very much appreciated
|
|
|
|
|
|
I have the following class:
internal class WeakPropertyDescriptor
{
public PropertyMediator _MethodOwner;
public WeakReference _sender;
public string _PropertyName;
public string _MessageKey;
public EventHandler handler;
public WeakPropertyDescriptor(PropertyMediator MethodOwner, Object sender, string PropertyName, string MessageKey)
{
_MethodOwner = MethodOwner;
_sender = new WeakReference(sender);
_PropertyName = PropertyName;
_MessageKey = MessageKey;
handler = (s, e) => _MethodOwner.NotifyColleaguesOfValueChanged(s, e, _MessageKey, _PropertyName);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(_sender.Target);
System.ComponentModel.PropertyDescriptor myProperty = properties.Find(PropertyName, false);
myProperty.AddValueChanged(_sender.Target, handler);
}
}
The goal is to make the PropertyDescriptor or the PropertyChanged event into a weak event, and I have tried lots of things but I'm unable to make it work. I tried to use Josh Smiths PropertyObserver
[^]
or this sample on WeakEvent here:
Muhammad Shujaat Siddiqi: Weak Event Pattern Improvements : WPF 4.5 RC New Feature[^]. The result was the same, I don't understand how to make the WeakEventManager based on reflection, or if it is even possible to do that?
modified 24-Jan-16 5:28am.
|
|
|
|
|
As much as I remember you can not set the type of any generics at run-time...
int n;
Stack<int> s;
Stack<n.GetType()> ns;
Skipper: We'll fix it.
Alex: Fix it? How you gonna fix this?
Skipper: Grit, spit and a whole lotta duct tape.
|
|
|
|
|
Not like that but you can write code that will at runtime construct an object of a generic, at runtime determined type.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Yes. Using reflection to create the generics itself...
I'm not sure I want to see code such that...
(Type.MakeGenericType Method (Type[]) (System)[^])
Skipper: We'll fix it.
Alex: Fix it? How you gonna fix this?
Skipper: Grit, spit and a whole lotta duct tape.
|
|
|
|
|
It's a lot of fun
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Yeah, I couldn't make it compile, it was just a way of showing what I tried to do. I thought, surely it must be possible to do in one way or another.
In fact, why don't you reflect upon that
|
|
|
|
|
Don't call me Shirley!!!
Sure it is possible not in one way, but another...but that another way is messy a bit...
You know where it begins, but can't see the end...[^]
Skipper: We'll fix it.
Alex: Fix it? How you gonna fix this?
Skipper: Grit, spit and a whole lotta duct tape.
|
|
|
|
|
|
I'm not familiar with WPF and PropertyDescriptor, so this might not be all you need but I think it'll get you a step further. I simplified it a bit so that I could test it in a new WPF-project. Note that you need a generic EventHandler for the AddHandler(..)-call and that you need to provide the name of the event there, not the name of the property:
public MainWindow()
{
InitializeComponent();
object sender = this;
WeakReference _sender = new WeakReference(sender);
EventHandler<EventArgs> handler = (s, e) => this.Title += ".";
string eventName = "MouseMove";
Type unboundWEMType = typeof(WeakEventManager<,>);
Type[] typeArgs = { _sender.Target.GetType(), typeof(EventArgs) };
Type constructedWEMType = unboundWEMType.MakeGenericType(typeArgs);
MethodInfo addHandlerMethod = constructedWEMType.GetMethod("AddHandler");
addHandlerMethod.Invoke(null, new object[] { _sender.Target, eventName, handler });
}
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
This looks very promising indeed. This piece of code is intended to be used in a Mediator, i.a a static instance that you can use to synchronize two or more properties in separate classes. The code is very similar to Sacha Barbers Mediator in his MVVM helper class Cinch, where you can raise an event in a property and it will invoke a delegate to a subroutine (void) in a separate class (in my cases between two ViewModels). I want to change the behavior to update a property if another property changed. The problem is that the connections with the shared (static) Mediator must be of a weak type, if not you might get a lot of memory leaks as a direct consequence of the strong references the normal event creates.
To do this I created an attribute that you can decorate a property with:
[AttributeUsage(AttributeTargets.Property)]
public sealed class PropertyMediatorAttribute : Attribute
{
public string MessageKey { get; private set; }
public PropertyMediatorAttribute()
{
MessageKey = null;
}
public PropertyMediatorAttribute(string messageKey)
{
MessageKey = messageKey;
}
}
My full code so far is (it does work but it won't allow the connected classes to be garbage collected):
public class PropertyMediator
{
#region Data
static readonly PropertyMediator instance = new PropertyMediator();
static readonly object syncLock = new object();
private readonly Dictionary<string, List<WeakPropertyDescriptor>> _registeredListners =
new Dictionary<string, List<WeakPropertyDescriptor>>();
#endregion
#region Ctor
static PropertyMediator()
{
}
private PropertyMediator()
{
}
#endregion
public static PropertyMediator Instance
{
get
{
return instance;
}
}
internal class WeakPropertyDescriptor
{
public PropertyMediator _MethodOwner;
public WeakReference _sender;
public string _PropertyName;
public string _MessageKey;
public EventHandler handler;
public WeakPropertyDescriptor(PropertyMediator MethodOwner, Object sender, string PropertyName, string MessageKey)
{
_MethodOwner = MethodOwner;
_sender = new WeakReference(sender);
_PropertyName = PropertyName;
_MessageKey = MessageKey;
handler = (s, e) => _MethodOwner.NotifyColleaguesOfValueChanged(s, e, _MessageKey, _PropertyName);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(_sender.Target);
System.ComponentModel.PropertyDescriptor myProperty = properties.Find(PropertyName, false);
myProperty.AddValueChanged(_sender.Target, handler);
}
}
internal void NotifyColleaguesOfValueChanged(object sender, EventArgs e, string key, string PropertyName)
{
object message = sender.GetType().GetProperty(PropertyName).GetValue(sender, null);
List<WeakPropertyDescriptor> wr;
if (_registeredListners.TryGetValue(key, out wr))
{
foreach (WeakPropertyDescriptor item in wr)
{
if (item._sender.IsAlive)
{
PropertyInfo MyProperty = item._sender.Target.GetType().GetProperty(item._PropertyName, BindingFlags.Public | BindingFlags.Instance);
if (message != MyProperty.GetValue(item._sender.Target, null))
{
if (null != MyProperty && MyProperty.CanWrite)
{
MyProperty.SetValue(item._sender.Target, message, null);
}
}
}
else
{
}
}
}
}
private void RemoveSourceFromValueChangedEventManager(object source)
{
Assembly assembly = Assembly.GetAssembly(typeof(FrameworkElement));
Type type = assembly.GetType("MS.Internal.Data.ValueChangedEventManager");
PropertyInfo propertyInfo = type.GetProperty("CurrentManager", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo currentManagerGetter = propertyInfo.GetGetMethod(true);
object manager = currentManagerGetter.Invoke(null, null);
MethodInfo remove = type.GetMethod("Remove", BindingFlags.NonPublic | BindingFlags.Instance);
remove.Invoke(manager, new object[] { source });
FieldInfo valueChangedHandlersInfo = typeof(PropertyDescriptor).GetField("valueChangedHandlers", BindingFlags.Instance | BindingFlags.NonPublic);
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(source);
foreach (PropertyDescriptor pd in pdc)
{
Hashtable changeHandlers = (Hashtable)valueChangedHandlersInfo.GetValue(pd);
if (changeHandlers != null)
{
changeHandlers.Remove(source);
}
}
}
private void RegisterProperty(string _MessageKey, object sender, string PropertyName)
{
WeakPropertyDescriptor WeakPropertyReferance = new WeakPropertyDescriptor(this, sender, PropertyName, _MessageKey);
List<WeakPropertyDescriptor> wr;
if (_registeredListners.TryGetValue(_MessageKey, out wr))
{
if (wr != null)
{
wr.Add(WeakPropertyReferance);
}
}
else
{
wr = new List<WeakPropertyDescriptor>();
wr.Add(WeakPropertyReferance);
_registeredListners[_MessageKey] = wr;
}
}
public void Register(object view)
{
foreach (var mi in view.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
foreach (var att in mi.GetCustomAttributes(typeof(PropertyMediatorAttribute)))
{
var mha = (PropertyMediatorAttribute)att;
RegisterProperty(mha.MessageKey, view, mi.Name);
}
}
}
public void UnRegister(object view)
{
foreach (var mi in view.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
foreach (var att in mi.GetCustomAttributes(typeof(PropertyMediatorAttribute)))
{
var mha = (PropertyMediatorAttribute)att;
RemoveSourceFromValueChangedEventManager(view);
}
}
}
}
The usage is rather simple:
public class A : NotifierBase
{
public A()
{
PropertyMediator.Instance.Register(this);
}
private string m_Name = "test";
[PropertyMediatorAttribute("MyProp")]
public string Name
{
get { return m_Name; }
set
{
SetProperty(ref m_Name, value);
}
}
~A()
{
PropertyMediator.Instance.UnRegister(this);
}
}
|
|
|
|
|
Can you also show me the source for NotifierBase? I'll give it a shot then.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
It's basically just a helper that saves me some typing:
public abstract class NotifierBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
|
|
|
|
|
Kenneth Haugland wrote: it does work but it won't allow the connected classes to be garbage collected Do you want connected classes to be able to be GC'ed individually or is it enough that they can be GC'ed once you're done with all of them?
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
I have to allow them to be GB individually, as I might exchange one of the classes with a new instance.
|
|
|
|
|
I was able to make it work now:
internal class WeakPropertyDescriptor
{
public PropertyMediator _MethodOwner;
public WeakReference _sender;
public string _PropertyName;
public string _MessageKey;
public EventHandler<EventArgs> handler;
private Type constructedWEMType;
public WeakPropertyDescriptor(PropertyMediator MethodOwner, Object sender, string PropertyName, string MessageKey)
{
_MethodOwner = MethodOwner;
_sender = new WeakReference(sender);
_PropertyName = PropertyName;
_MessageKey = MessageKey;
handler = new EventHandler<EventArgs>(WeakPropertyDescriptor_PropertyChanged);
Type unboundWEMType = typeof(WeakEventManager<,>);
Type[] typeArgs = { _sender.Target.GetType(), typeof(EventArgs) };
constructedWEMType = unboundWEMType.MakeGenericType(typeArgs);
MethodInfo addHandlerMethod = constructedWEMType.GetMethod("AddHandler");
addHandlerMethod.Invoke(null, new object[] {_sender.Target , "PropertyChanged" , handler});
}
private void WeakPropertyDescriptor_PropertyChanged(object sender, EventArgs e)
{
PropertyChangedEventArgs arg = (PropertyChangedEventArgs)e;
if (arg.PropertyName == _PropertyName)
_MethodOwner.NotifyColleaguesOfValueChanged(_sender.Target, e, _MessageKey, _PropertyName);
}
public void RemoveHandler()
{
MethodInfo removeHandlerMethod = constructedWEMType.GetMethod("RemoveHandler");
removeHandlerMethod.Invoke(null, new object[] { _sender.Target, "PropertyChanged", handler });
}
}
modified 25-Jan-16 4:53am.
|
|
|
|
|
To be honest, given what you're trying to do, I would look to use RX to observe property changes. In fact, that's exactly what I have done for some of my clients.
This space for rent
|
|
|
|
|
|
I tend to use a variation of code that builds on something like this[^].
This space for rent
|
|
|
|
|