I've just written a small XBox 360 Wireless Controller managed interface that basically wraps around the low-lever SlimDX wrapper library and provides a easy, managed API for the XBOX 360 controller.
Internally, the class polls the gamepad every N ms, and shoots events as it detects changes in the underlying state of the controller.
I'm experiencing some what dead end with timers that is basiclly forcing to choose between the lesser of two evils:
Either make my XBox360GamePad class UI framework specific (i.e. support WPF/WinForms will be hard-coded in the class, and the class has to reference these frameworks...)
Make the class completely framework agnostic, but force the users to sprinkle their code with Dispatcher.Invoke / Invoke() calls to be able to update UI according to the events generated.
If I choose the latter option (of making the code UI agnostic), then I basically use the "generic" System.Timers.Timer or any timer that has no UI dependency. In that case I end up having events generated/called from a thread that is incapable of directly updating the UI, i.e. in WPF, I would have to issue every update originated form the 360 controller class through the (ugly) use of Dispatcher.Invoke.
On the other hand, If I use DispatcherTimer inside the XBox 360 Controller class I have a working component that can update the UI directly with no fuss, but now my whole controller class is coupled to WPF, and it can't be used without being dependent on WPF (i.e. in a pure console app)
What I'm kind of looking is a some sort solution that would allow me to be both framework agnostic and also update UI without having to resort to all kinds of Dispatcher.Invoke() techniques... If for example there was a shared base class for all timers, I could somehow inject the timer as a dependency according to the relevant scenario.. Has anyone ever dealt successfully with this sort of problem?
So... It appears the information / code @ http://geekswithblogs.net/robp/archive/2008/03/28/why-doesnt-dispatcher-implement-isynchronizeinvoke.aspx does indeed provide a working solution. The code is completely independent of any UI, and is instead only dependent on ISynchronizeInvoke for proper UI / Thread integration.
Having just used this, I'm still somewhat reluctant to leave it as is.
The gist of my problem is that every event invocation function looks something like this:
protected virtual void OnLeftThumbStickMove(ThumbStickEventArgs e)
{
if (LeftThumbStickMove == null) return;
if (_syncObj == null || !_syncObj.InvokeRequired)
LeftThumbStickMove(this, e);
else
_syncObj.BeginInvoke(LeftThumbStickMove, new object[] { this, e });
}
It's very annoying and confusing to write code this way, it looks, to me, like way too much fuss around just getting the damn thing to work. Basically I don't like having to wrap every event call with so much logic(!)
Therefore, I've opted for a different / additional strategy: Basically, the constructor for the XBox360GamePad class now looks like this:
public XBox360GamePad(UserIndex controllerIndex, Func<int, Action, object> timerSetupAction)
{
CurrentController = new Controller(controllerIndex);
_timerState = timerSetupAction(10, UpdateState);
}
As you can see, it accepts a Func<int, Action, object> that is responsible for creating the timer and hooking it up.
This means that by default, INSIDE the 360 Controller class I don't need to use any UI specific timers... In essence, my "default" constructor for the controller looks like this:
public XBox360GamePad(UserIndex controllerIndex) :
this(controllerIndex, (i,f) => new Timer(delegate { f(); }, null, i, i)) {}
I use a lambda function to code the dependency of the controller class on a timer "service".
From WPF, I use the more general constructor to ensure that the control will be using the DispatcherTimer like this:
_gamePad = new XBox360GamePad(UserIndex.One, (i, f) => {
var t = new DispatcherTimer(DispatcherPriority.Render) {Interval = new TimeSpan(0, 0, 0, 0, i) };
t.Tick += delegate { f(); };
t.Start();
return t;
});
This way, I basically leave it up to the "user" to provide the timer implementation for the control, where the default implementation doesn't have to use any WPF or WinForms specific code.
I personally find this more useful / nicer design than using ISynchronizeInvoke.
I would love to hear feedback from people that like this / want to improve this / are disgusted by this etc.