Showing posts with label WinForms. Show all posts
Showing posts with label WinForms. Show all posts

Thursday, February 21, 2013

INotifyPropertyChanged: Worst. Interface. Ever

As any .Net UI developer will tell you, INotifyPropertyChanged is a fundamental part of 'binding' an object to a UI control. Without it binding is essentially one-way: changes in the control change the object, but if this has a ripple effect on other properties, or properties are changed by other 'below the UI' processes, the UI can't know to repaint. This is essentially an implementation of the Observer pattern[1].

Unfortunately it's not for free - you have to implement it yourself - and that's where the problems start. So much has been written on the pain of implementing INotifyPropertyChanged (INPC for short) that I need not repeat it all here. It's generated so many questions on StackOverflow you'd think it's due its own StackExchange site by now.

The principal complaints are around all the boilerplate code and magic strings required to implement, so for the sake of completeness I'll summarize some of the solutions available:

Pick one of these, stick with it and you're done. Ok, there's still a bit of griping about separation of concerns, and whether this is an anti-pattern, but you're done right?

No.

It's so much worse than that.

Do you remember the first time you implemented GetHashCode(), and later when you realized you'd done it wrong? And later when you really realized you'd done it wrong, that there was no good way you could override Equals() for a mutable object, and the sneaking realization that this whole problem existed only for the benefit of Hashtables? It's a bit like that.

What we have with INotifyPropertyChanged is an implicit contract, that is to say that a large part of the contract can't be formally defined in code. Which means you have to validate your implementation manually. In this case the implicit bit is about threading. INotifyPropertyChanged exists to support UI frameworks and (bizarrely, in this day-and-age) they are still single threaded, and can only execute on the thread that constructed them – including event handlers. Think about this a bit, and you will eventually conclude:
An object that implements INotifyPropertyChanged must raise the PropertyChanged event only on the thread that was originally used to construct any registered subscribers for that event

Now there's a problem[2].

Clearly this is something that's just not possible to check for at runtime, so your design has to cater for this. Passing objects that might have been bound to business logic that might mutate them? UI thread please. Adding an item into a collection that might be ObservableCollection? UI thread please. Doing some calculations in the background to pass back to an object that may have been bound? Marshal via UI thread please. And so on. And don't even get me started on what you do if you have two (or more) 'UI' threads[3].

This is a horrible, horrible creeping plague of uncertainty that spreads through your UI, where the validity of an operation can't be determined at the callsite, but must also take into account the underlying type of an object (violating polymorphism), where that object came from (violating encapsulation), and what thread is being used to process the call (violating all that is sacred). These are aspects that we just can't model or visualize well with current tooling, at least not at design time, and none of the solutions above will save you here.

So there you go. INotifyPropertyChanged: far, far worse than you imagined.


[1] ok, any use of .net events could be argued is Observer, but the intent here is the relevant bit: the object is explicitly signalling that it's changed state.
[2] Actually I've over-simplified, because you can have whole chains of objects listening to each other, and if any one of them is listened to by an object with some type of thread-affinity, that's the constraint you have to consider.
[3] Don’t try this at home. There are any number of lessons you’ll learn the hard way.

Saturday, November 14, 2009

What’s New in Windows Forms for the .Net Framework 4.0

Um. Well…

Nothing.

At least as far as one can tell from the documentation anyway. Check out the following:

What’s New in the .Net Framework Version 4 (Client)

Windows Forms Portal

…and compare with previous versions: [Search:] What’s New in Windows Forms

Ok, so I should really do a Reflector-compare on the assemblies and see if there really are absolutely no changes but the fact there’s not one trumpeted new feature speaks volumes about where Microsoft see the future of the client GUI, and they seem prepared to put that message across fairly bluntly. So much for whatever-they-said previously about ‘complimentary technologies’ (or something?)

(Albeit, all this is beta 2 doco, subject to change blah blah)

Thursday, November 05, 2009

WM_TOUCH

So finally some time last week HP shipped to web the final 64bit NTrig drivers for my TX2, and I now have a working Windows 7 Multitouch device. Sure, candidate drivers have been available from the NTrig site for ages, but I had issues with ghost touches, so they got uninstalled within a day or so as the page I was browsing kept scrolling off…

Something still bugs me – which his that if the final drivers only went to web last week, how come they’re already selling the TX2 with Windows 7 in Harvey Norman? The 32 bit driver’s still not there for download today (1/11/09). For HP to be putting drivers on shipping PCs prior to releasing them to existing owners seems bizarre, if not downright insulting.

But I digress. Check this out:

image

Ok, it’s not my best work. But the litmus test of working multitouch is, amazingly, Windows Paint. If you can do this with two fingers, you are off and running (fingers not shown in screenshot – sorry).

So finally I can play touch properly, Win 7 style. And it rocks.

I am, for example, loving the inertia-compliant scrolling support in IE, which totally refutes my long-held believe that a physical scroll wheel is required on tablets, and makes browsing through long documents a joy. It alone would justify the Win 7 upgrade cost for a touch-tablet user. It’s not all plain sailing: I’m sure Google Earth used to respond to ‘pinch zoom’ under the NTrig Vista drivers (which handled the gestures themselves, so presumably sent the app a ‘zoom’ message like what your keyboard zoom slider would produce), but doesn’t in Win 7, at least until they implement the proper handling. But in most cases ‘things just work’ thanks to default handling of unhandled touch-related windows messages (eg unhandled touch-pan gesture messages will cause a scroll message to get posted, which is more likely to be supported).

But how to play with this yourself, using .Net?

In terms of hardware we are in early adopter land big time. The HP TX2 is half the price of the Dell XT2, and there’s that HP desktop too. Much more interesting is Wacom entering the fray with multi-touch on their new range of Bamboo tablets, including the Bamboo Fun. This is huge: at that price point ($200 USD) Wacom could easily account for the single largest touch demographic for the next few years, and ‘touching something’ has some distinct advantages over ‘touching the screen’: you can keep your huge fancy monitor, use touch at a bigger distance and avoid putting big smudges on whatever you’re looking at. (If anyone made a cheap input tablet that was also a low-rez display/SideShow device you’d get the best of both worlds of course). Finally, there is at least one project in beta to use two mice instead of a multitouch input device, which is (I believe) something that the Surface SDK already provides.

SDK-wise, unfortunately the Windows API Code Pack doesn’t help here, so we are off into Win32-land. And whilst there are some good explanatory articles around, they’re mostly C++, some are outdated from early Win 7 builds, and some are just plain incorrect (wrong interop signature in one case, which – from experience – is a real nasty to get caught by, since it might not crash in Debug builds). The best bet seems to be the fairly decent MSDN documentation, and particularly the samples in the Windows 7 SDK, which – if nothing else – is a good place to copy the interop signatures / structures from (since they’re not up on the P/Invoke Wiki yet). But just to get something very basic up and running doesn’t take that long:

image

Again, without the fingers it’s a bit underwhelming, but what’s going on here is that those buttons light up when there’s a touch input detected over them, and I have two fingers on the screen. Here’s the guts:

protected override void WndProc(ref Message m)

{

    foreach (var touch in _lastTouch)

    {

        if (DateTime.Now - touch.Value > TimeSpan.FromMilliseconds(500))

            touch.Key.BackColor = DefaultBackColor;

    }

 

    var handled = false;

    switch (m.Msg)

    {

        case NativeMethods.WM_TOUCH:

            {

                var inputCount = m.WParam.ToInt32() & 0xffff;

                var inputs = new NativeMethods.TOUCHINPUT[inputCount];

                if (!NativeMethods.GetTouchInputInfo(m.LParam, inputCount, inputs, NativeMethods.TouchInputSize))

                {

                    handled = false;

                }else

                {

                    foreach (var input in inputs)

                    {

                        //Trace.WriteLine(string.Format("{0},{1} ({2},{3})", input.x, input.y, input.cxContact, input.cyContact));

                        var correctedPoint = this.PointToClient(new Point(input.x/100, input.y/100));

                        var control = GetChildAtPoint(correctedPoint);

                        if (control != null)

                        {

                            control.BackColor = Color.Red;

                            _lastTouch[control] = DateTime.Now;

                        }

                    }

                    handled = true;

                }

                richTextBox1.Text = inputCount.ToString();

            }

            break;

    }

 

    base.WndProc(ref m);

 

    if (handled)

    {

        // Acknowledge event if handled.

        m.Result = new System.IntPtr(1);

    }

}

Now I’ll have to have a look at inertia, and WPF. I hear there’s native support for touch in WPF 4, so there’s presumably some managed-API goodness in .Net 4 for all this too, not just through the WPF layer. Which is just as well, because getting it working under WPF 3.5 looks a bit hairy (no WndProc to overload, which is a bad start…)

Popular Posts