NDepend Blog

Improve your .NET code quality with NDepend

WPF / Winforms UI Refactoring: A Case Study

May 11, 2022 4 minutes read

WPF Winforms UI Refactoring A Case Study

WPF and Winforms are still so massively used that Microsoft fully supports those technologies in .NET Core, .NET 5, 6 , 7 and so on. However WPF and Winforms are not multi-platforms and it is clear that they are not the future of .NET desktop UI. Microsoft will soon release MAUI RTM (currently in Release Candidate stage) but there are debates about its adoption and future.

Indeed .NET Developers should be careful in which technology to invest in since Microsoft has a long track-record of dead (or so) UI technologies (Silverlight, UWP, WinUI, WinRT, Metro… ). Also many .NET shops already migrated to the Avalonia or Uno platforms and are happy with it.

In this context, the only wise action to prepare the future is to segregate as much code as possible from your actual UI framework. Of course this is the number one general advice when it comes to UI code. But in practice all legacies have far too much code still entangled with WPF and Winforms framework to decouple. We – at NDepend – are no exception, and since the UI future is uncertain we are now in this process of taming our UI dependencies.

Our goal is to have all our logic implemented in .NET Standard 2.0. The new APIs in .NET 6, .NET 7 and so on are very exciting but as explained in the post 5x Lessons Learned from Migrating a Large Legacy to .NET 5/6 it is quite a desirable achievement to be runnable within .NET Fx, .NET 5/6/7 process and who knows, maybe in the browser with Blazor that has full .NET standard support. Moreover we will still have to support users developing with .NET Fx based tools (like Visual Studio 2022 and lower) for many years. This is why .NET Standard still really makes sense.

Case Study

WPF and Winforms UI logic can usually be split in two categories:

The NDepend code query result panel is quite a beast. Hopefully when developing it we segregated it into a Physical namespace (that handles the rows on screen, expanding, collapsing, virtual data grid view…) and a Logical namespace (the rows model inferred from the code query result). See the Logical namespace Solution Explorer view below:

Large-Winforms-Namespace

This Logical namespace is still consuming Winforms and GDI a bit and is a good candidate to be re-used in the future. Thus it needs some refactoring. To better understand the coupling let’s edit this C# LINQ code query:

Here is the result (drawn by the code query result code analyzed by the way):

Coupling-with-winform-GDI

We can see that several rows classes like RowItemIssue are still coupled with the Winforms / GDI framework to do some drawing stuff. Thus thanks to this query result we can now choose which classes to uncouple and which one will remain on the WPF/Winforms side (like CellDrawer or FormSuppressIssue).

To ease this refactor-decision-phase we can export the 68 matched classes to the dependency graph to see how they interact. Here the class RowItemIssue is selected. Blue cells represent classes directly used by RowItemIssue while light-blue cells are classes used indirectly by RowItemIssue.

Dependency-Graph-UI-coupling

We can now go to the source code and eliminate the unwanted dependencies. For that we will define some interfaces to abstract the drawing logic from the row model that we want to preserve. This is the SOLID principle Dependency Inversion Principle (DIP) in action.

Conclusion

Today it is hard to chose a modern .NET desktop UI technology with confidence. The future is still uncertain. This is why it makes sense to segregate code now to prepare for the future.

Refactoring a large legacy to reduce (or eliminate) the adherence to a framework is not an easy task. However this process can be simplified by using a tool like NDepend that can be an eye-opener in many situations.