Reflecting on bugs

Posted

Howdy!

Today I wanted to just share abit about some development experiences in building my NDI Telestrator project. NDI Telestrator is a graphic annotation software built for the NDI protocol, which allows you to draw over a background and send the overlay over NDI for use in a vision mixer / production environment - pretty cool hey! Compared to the official (and now discontinued) application, I have a few improvements such as being able to export and recall annotations, as well as having multiple layers of annotations.


The official NDI Telestrator application. It’s paid; and also doesn’t work with NDI 4 or NDI 5.


The Issue

Today I was working on fixing a bug which caused my dropdown buttons to disappear right after clicking on them, have a look at the below recording.

Why was this happening?

After double checking my code and making sure that I didn’t make an error, I had a closer look at the Metro UI library that I was using.

It turns out that SplitButton.cs:331 was being called after line 306 (i.e. the line that executes my intended action to open the dropdown box) - this meant that my code to open the dropdown box was nullified. I need to somehow open the dropdown box on click without letting that ButtonClick event handler fire…

Why is that line there?

The code itself is correct, because it’s a button (hence SplitButton), rather than a dropdown box / combo box. There exists a DropdownButton component which one might try to use instead given its eponymous name, however I needed the functionality to set and get its value, which only SplitButton provided.

EDIT: Actually, not 100% if a DropdownButton would have worked instead.
I definitely must have tried it unsuccessfully last year when first designing this application…

SplitButton is intended to be a button - which should trigger an event related to the selected value. However, I was using the SplitButton as a fancy dropdown list - where clicking on the button shouldn’t actually call any operational function. Instead I wanted the action of pressing the main button to follow suit with opening the dropdown list.

As per original design

So if I assign the user command to open the dropdown menu, line 311 will just close the menu 😢

The library is code is correct, but my use for it violates its programmed behaviour

How can we fix this?

Sometimes you got to reflect on your mistakes.

Ideally we could just remove line 311 from the UI library, but that would require extra code maintenance. Otherwise we could try removing the ButtonClick event handler from the main button’s click event. But, both the main button and ButtonClick event handler were enforced with the private access modifier - which means that only SplitButton instance itself can access those properties… or can they…

Reflection

Reflection is a .NET paradigm that allows you to gain information about an object’s / assembly’s structure - such as its classes and the classes' methods, attributes, and properties - regardless of their access level (i.e. private and protected data can be accessed!).

By utilising reflection we could modify the behaviour of a particular SplitButton instance without having to modify its source code! So how exactly do we do that?

Step 1. Figuring out what to fix

Firstly, we need to figure out how to hack into the SplitButton code.

To make the dropdown show, we could perhaps add an additional event handler to the main button’s click event - however it will execute after ButtonClick finishes - and would hence cause a visual bug where the dropdown could close momentarily after opening, for it to then open again shortly after. Additionally, our event handler might not even fire if the <Event>.Handled property is set to true by any earlier event handler.

Instead, we need to remove the original ButtonClick event handler, and then add our own event handler. Performing this operation would remove the call to the user command (line 306), but we technically don’t need that command and could just toggle the dropdown menu in the reflection code ourselves!

Step 2. Applying reflection

The <Object>.getType() function allows us to access metafunctions related to a given class. Using GetField and GetMethod with different BindingFlags mask filters, we can locate the button field and ButtonClick method respectively for a given object of that class type.

With our now accessible reference to the main button component, as well as the ButtonClick event handler, we can deregister the event handler, and then register our own event handler to toggle the dropdown!

I refactored the reflection code so that I could reuse it for other dropdown boxes that existed in the application (which at the time of writing, were two).

Note: Reflection might fail during object initialisation, where properties may not yet be available, so if you need to implement a similar sort of functionality override in your own application, I would recommend hooking into the component’s Loaded event

Conclusion

As you can see, it’s all working!

Reflection is pretty cool, but also rather slow as the lookups are performed dynamically when called. If you ever need to reflect things in your code, cache where possible!
… or find a better library / write better code

Now to figure out how to increase rendering and display performance…

Previously

More posts

vvccacugfbkiihatethispleasestopbjcrneijnbjlh

Yubikeys...

Posted

Control Rings Are Weird

My opinion (rant) on the Canon RF lens control ring placement

Posted