DEV Community

Cover image for I Spent a Year Fighting GDI+ in WinForms So Yo
amir abedi
amir abedi

Posted on

I Spent a Year Fighting GDI+ in WinForms So Yo

I Spent a Year Fighting GDI+ in WinForms So You Don't Have To

Let's be honest: Windows Forms is still a workhorse.

For internal B2B applications, industrial automation software, administration tools, monitoring systems, and heavy desktop clients, WinForms can still get the job done remarkably fast.

And with WinForms continuing on modern .NET versions, including .NET 6, .NET 8, and beyond, it isn't disappearing anytime soon.

But there is one problem that developers keep running into:

The default UI hasn't aged as gracefully as the framework itself.

Show a client a modern web application and then show them a traditional WinForms form with gray buttons, old-school grids, and basic panels, and the visual difference can make an otherwise perfectly capable application feel dated.

A year ago, I decided to see how far I could push WinForms in the other direction.

I wanted modern-looking controls.

Rounded corners.

Better dashboards.

Dark mode.

Custom grids and navigation.

And, most importantly, something that still behaved like a proper WinForms application inside Visual Studio.

I thought it would be simple.

I was wrong.


The GDI+ Reality Check

My original plan was straightforward:

Override OnPaint(), draw some rounded rectangles, add some shadows, and move on.

That plan lasted about five minutes.

Once you start building a real collection of custom WinForms controls, rendering quickly becomes much more complicated than drawing a rectangle.

The problems don't appear when you're drawing one button on a test form.

They appear when you have dozens of controls, nested layouts, resizing, DPI scaling, design-time support, themes, images, animations, and frequent repaint operations happening throughout the application.

I eventually ran into three major areas of pain.


1. The Flickering Problem

WinForms applications can look perfectly fine until you start resizing a complex window.

Then everything changes.

Nested controls repaint.

Layouts are recalculated.

Invalidation propagates through the hierarchy.

And suddenly your beautiful dashboard starts flashing while the window is being resized.

Getting double buffering and repaint behavior right across a hierarchy of custom controls isn't simply a matter of adding:

DoubleBuffered = true;
Enter fullscreen mode Exit fullscreen mode

Sometimes it helps.

Sometimes it doesn't.

And sometimes the real problem is somewhere else in the rendering or layout pipeline.

I spent a lot of time testing resize scenarios and trying to understand exactly when controls were being invalidated and repainted.


2. GDI+ Resources, Rendering and High DPI

Once you start doing custom drawing, you also become responsible for much more than pixels.

Pens.

Brushes.

Fonts.

Bitmaps.

Graphics objects.

Paths.

Images.

All of these have to be handled carefully, especially inside controls that may repaint many times during their lifetime.

Then there is DPI scaling.

A control that looks perfect on one monitor can suddenly look slightly wrong on another.

Rounded corners can become uneven.

Icons can look blurry.

Spacing can feel inconsistent.

Shadows can look different at different scales.

None of this is particularly difficult in isolation.

The problem is that these small issues accumulate across an entire control library.


3. The Visual Studio Designer

Then there was the part I underestimated the most:

design-time behavior.

A custom control isn't only executed when your application runs.

Visual Studio can instantiate it inside the Form Designer.

Properties are inspected.

Design-time services are used.

Resources may be loaded.

And if your control makes an assumption that is valid at runtime but invalid at design-time, you can end up with a broken designer experience.

You build the application.

Everything works.

You open the Form Designer.

That's when you realize that a control library needs to be designed for two different worlds:

runtime and design-time.


What I Learned Building Custom WinForms Controls

After enough debugging sessions, I started noticing a pattern.

The hard part wasn't drawing a rounded button.

The hard part was building a reusable rendering architecture around it.

Here are some lessons that mattered most.


Don't treat every control as an OnPaint() problem

A control needs more than rendering logic.

State management, invalidation, sizing, DPI behavior, resource ownership, interaction states, and design-time behavior all matter.


Rendering and appearance should be separated

If every control handles colors independently, adding a global theme becomes painful.

A shared theme system makes the controls much easier to manage.


Design-time behavior needs to be considered from day one

Trying to "fix the designer later" is much harder than designing controls with Visual Studio's designer in mind from the beginning.


Resize testing should happen early

Don't wait until the end of development to test a complex dashboard while continuously resizing the window.

If your rendering architecture has problems, resizing will find them.

Fast.


A UI library is more than a collection of controls

The controls are only part of the problem.

You also need consistent behavior, shared styling, theming, resources, designers, documentation, and a predictable developer experience.

That realization eventually changed the direction of the project.


Turning That Experience Into FTR Controls

Instead of keeping everything inside a private project, I decided to turn the architecture into a reusable WinForms UI suite.

That's how FTR Controls started.

The idea wasn't to create another huge UI framework with hundreds of obscure properties.

I wanted something focused on a simpler goal:

Make modern WinForms UI development feel like normal WinForms development again.

Drop a control onto a form.

Open the Visual Studio Property Grid.

Configure it.

Write your application logic.

Don't spend your afternoon writing another custom OnPaint() implementation because a button needs rounded corners.

FTR.UI.WinForms includes 26 custom WinForms controls covering dashboards, grids, navigation, calendars, ribbons, cards, sliders, charts, notifications, and more.

The important part isn't just how the controls look.

It's how they behave inside a real WinForms development workflow.


Let's Build a Modern Dashboard

Here's a simple example of how the pieces fit together.

1. Build the Layout

Instead of implementing your own custom navigation and dashboard containers, you can compose the application using the provided controls.

For example:

  • Add an FTRRibbon to the top.
  • Add an FTRTreeList for navigation.
  • Add an FTRDashboard to the main content area.
  • Configure docking and sizing through the normal WinForms designer.

The goal is that this should still feel familiar to a WinForms developer.

You aren't learning an entirely different application framework.

You're still building a WinForms application.


2. Create Data Cards

Suppose you want a card on your dashboard.

You can create it directly in code:

var card = new FTRCard
{
    HeaderText = "Monthly Revenue",
    ShowHeader = true,
    CardBackColor = Color.White
};
Enter fullscreen mode Exit fullscreen mode

You can also configure the control through the Visual Studio Property Grid.

That's an important part of the design philosophy behind FTR Controls:

The developer should work with properties and application logic, not rendering code.

One-Click Dark Mode

Another problem that becomes surprisingly difficult when building a UI library from scratch is theming.

If every control owns its own colors, switching an application from Light to Dark can quickly become a maintenance nightmare.

FTR Controls uses a centralized theme system.

Switching the application's theme can be done through a single setting:

ThemeManager.CurrentTheme = ThemeManager.ThemeMode.Dark;
Enter fullscreen mode Exit fullscreen mode

Instead of manually finding every control and changing its colors, the shared theme system manages the common visual appearance across the application.

The available theme modes include:

  • Light
  • Dark
  • Color
  • Duotone

A consistent theme system is important because a modern application is not just a collection of controls.

It is a complete visual experience.


The Flicker-Free Challenge

This is the part where I don't want you to simply take my word for it.

Try to break it.

I've created a demo application so developers can test the controls in a real WinForms environment.

Try this:

  1. Download and run the demo application.
  2. Open it in Visual Studio if you want to explore the controls.
  3. Resize the window aggressively.
  4. Switch between Light and Dark themes.
  5. Navigate through different sections.
  6. Try the controls under your own workload.

Pay attention to how the UI behaves during continuous resizing and repainting.

A screenshot cannot demonstrate rendering behavior.

A real application can.

If you find a rendering issue, unexpected flickering, a DPI problem, or a designer-related problem, I would genuinely like to hear about it.


Why I Built It

There are already several mature commercial UI libraries for WinForms.

This project isn't about pretending they don't exist.

I built FTR Controls because I wanted to solve the problems I personally kept running into while building modern desktop applications.

The goal is a developer-friendly collection of controls that:

  • Fits naturally into the WinForms designer.
  • Works with the Visual Studio Property Grid.
  • Provides a consistent visual system.
  • Supports Light, Dark, Color, and Duotone themes.
  • Reduces the need to write custom rendering code for common UI patterns.
  • Works with modern .NET and supported .NET Framework applications.

FTR.UI.WinForms supports:

  • .NET Framework 4.7.2
  • .NET 6+ Windows applications

The most important goal is simple:

Developers should spend their time building their application, not rebuilding the same UI infrastructure over and over again.


If You're Still Building WinForms in 2026...

WinForms may not be the newest UI technology.

But for a huge number of business applications, internal tools, industrial systems, administration software, and desktop clients, it remains a very practical choice.

The framework isn't necessarily the problem.

Sometimes the problem is simply that we're still building the UI the way we did twenty years ago.

That's what I wanted to experiment with.

After a year of fighting with custom rendering, I ended up with something I can actually reuse.

That's FTR Controls.

If you're working on a WinForms application and modernizing its UI is becoming a bigger task than you expected, give the demo a try.

And if you manage to make it flicker, let me know. 👇


Try It Yourself

FTR UI Suite
https://ftr-ui-suite.vercel.app/

Documentation
https://ftr-ui-suite.vercel.app/docs

NuGet Package
https://www.nuget.org/packages/FTR.UI.WinForms


The core idea is simple:

Stop fighting with pixels. Build the application.

Top comments (0)