I’ve learned a lot in porting .NETpad, if partly, to the Windows Presentation Foundation (WPF) and Universal Windows Platform (UWP). Key among that learning is that my original decision to develop this application using Windows Forms was the right choice: WinForms was expressly designed for this type of classic desktop application.
WPF and UWP, by comparison, include many modern improvements over WinForms, and their similar approaches to supporting dynamic layouts, high-DPI displays, and display scaling are particularly well-done. Indeed, WPF and UWP share a nearly-identical declarative approach to application design via XAML that I find very, very compelling. But it’s equally clear that Microsoft also simply ignored vast swathes of functionality that many developers would need. And that in doing so, it severely handicapped WPF and UWP.
There are many examples of this in both frameworks. As I wrote in What I’ve Learned About C#, Visual Basic, Windows Forms, XAML, and WPF (Premium), Microsoft completely changed how developers access fonts in WPF, making it nearly impossible to use the classic Font dialog that was so easily available from Windows Forms. In fact, it was so easy to display and use this dialog, that it just required one line of code in the original (VB) version of .NETpad:
TextBox1.Font = FontDialog1.Font
That’s it. Not only can you display the standard system Font dialog, which provides such niceties as formatting each font in the list using those fonts so you can see what they look like, but pulling all of the configuration changes that the user selected simply requires passing along one simple (Font) object. It is the epitome of why Windows Forms is so special, because you can get so much done with no code or with very little code.

In WPF (and in UWP), the Font object has been replaced by separate FontFamily, FontSize, FontWeight, FontStyle, and FontStretch objects. And you cannot “map” properties of the WinForms Font object to these new objects, either easily or at all. If you use WPF with the .NET Framework, you can at least add a reference to Windows Forms, which will give you access to that Font object. But then you have to handle moving between the Font object and the new WPF objects. It’s a mess.
But if you use WPF with .NET Core, as I’m now doing in my second pass at a WPF version of .NETpad, you can’t even do that: .NET Core is not compatible with Windows Forms, at least not yet, so its objects are unavailable to you. And that means that if you want to work with fonts, you are on your own.
What does it mean to be on your own?
It means that you will need to build your own Font dialog or find a third-party alternative online. And since most of those alternatives are part of bigger sets of control packages from vendors that charge upwards of $1000 for the privilege of using their products, doing the latter is expensive. Unfortunately, doing the form manually is incredibly time-consuming.
Remember. This required only a single line of code in WinForms.
To see what this might entail, I’ve spent the better part of the past week building a basic Font dialog in WPF and integrating it into my WPF version of .NETpad. This required a lot of XAML experimentation, which I honestly did enjoy. It required learning lots of new concepts, such as the rather incredible but complex data binding capabilities of XAML. It required changing my approach to application/user settings, since the Font object (or its newer equivalents) are no longer provided as an option there, as it was in WinForms. And a lot more. I feel like I have PTSD.
What I’ve come up with isn’t quite where I want it, but it works. And some of the success I’ve had really pleases me: For example, the Font, Font style, and Size columns correctly select the items that represent the font used in the application’s text box, and each scrolls to the correct items so that they’re visible when the dialog launches. But there are also some frustrating missing bits—the fonts in the Font list don’t render as each font, nor do the styles—and some layout work to fix (like that overly-large Cancel button).

But my God, what a mess.
In the C#/Windows Forms/.NET Framework version of .NETpad, I only need a single line of code to read in the user setting for the font when the application starts. That way, whatever font the user selected is always configured for them. It looks like so:
TextBox1.Font = Properties.Settings.Default.MyFont;
Simple. But in the C#/WPF/.NET Core version, I have to accommodate multiple user settings because there isn’t a single Font object, and WPF application/user settings don’t natively support the new objects:
string fontName = Settings.Default.MyFontFamily; TextBox1.FontFamily = new System.Windows.Media.FontFamily(fontName); TextBox1.FontSize = Settings.Default.MyFontSize; if (Settings.Default.MyFontBold) TextBox1.FontWeight = FontWeights.Bold; else TextBox1.FontWeight = FontWeights.Normal; if (Settings.Default.MyFontItalic) TextBox1.FontStyle = FontStyles.Italic; else TextBox1.FontStyle = FontStyles.Normal;
Writing out those values when the application closes of course requires a similar amount of additional code.
But it is the homemade Font dialog where this thing really goes off the rails. When the user selects Format > Font, I need to write all those values to user settings so that the dialog can access them, open the dialog, and then write the values back to user settings if the user selected OK (as opposed to Cancel).
The Font dialog I created then needs to read in the relevant values from user settings, populate the Font, Font style, and Size list boxes, and then select the correct items in each list box. And that looks like so:
// Set the correct font properties in the sample text
string fontName = Settings.Default.MyFontFamily;
SampleLabel.FontFamily = new System.Windows.Media.FontFamily(fontName);
SampleLabel.FontSize = Settings.Default.MyFontSize;
if (Settings.Default.MyFontBold)
SampleLabel.FontWeight = FontWeights.Bold;
else
SampleLabel.FontWeight = FontWeights.Normal;
if (Settings.Default.MyFontItalic)
SampleLabel.FontStyle = FontStyles.Italic;
else
SampleLabel.FontStyle = FontStyles.Normal;
// Populate Font Style list box
List<string> fontStyle = new List<string>
{
"Normal", "Italic", "Bold", "Bold Italic"
};
FontStyleList.DataContext = fontStyle;
// Populate Font Size list box
List<double> fontSize = new List<double>
{
8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72
};
FontSizeList.DataContext = fontSize;
// Select correct items in each list box
for (int x = 0; x <= Fonts.SystemFontFamilies.Count; x++)
if (FontList.Items[x].ToString() == SampleLabel.FontFamily.ToString())
{
FontList.SelectedItem = FontList.Items[x];
FontList.ScrollIntoView(FontList.Items[x]);
break;
}
for (int x = 0; x < fontStyle.Count; x++)
if (fontStyle[x].ToString() == SampleLabel.FontStyle.ToString())
{
FontStyleList.SelectedIndex = x;
break;
}
for (int x = 0; x < fontSize.Count; x++)
if (fontSize[x].ToString() == SampleLabel.FontSize.ToString())
{
FontSizeList.SelectedIndex = x;
FontSizeList.ScrollIntoView(fontSize[x]);
break;
}
Ugh.
What you don’t see there is the XAML data binding for the Font list box; that section of the XAML looks like so:
<ListBox Name="FontList" ItemsSource="{x:Static Fonts.SystemFontFamilies}" Grid.Row="1" Grid.Column="0" Margin="10,0,0,0" SelectionChanged="FontList_SelectionChanged"/>
<ListBox Name="FontStyleList" ItemsSource="{Binding}" Grid.Row="1" Grid.Column="1" Margin="10,0,0,0" SelectionChanged="FontStyleList_SelectionChanged"></ListBox>
<ListBox Name="FontSizeList" ItemsSource="{Binding}" Grid.Row="1" Grid.Column="2" Margin="10,0,10,0" SelectionChanged="FontSizeList_SelectionChanged"></ListBox>
Additionally, each time the user selects a new font, font style, or size, the sample text needs to change so they can see what it will look like. So each has its own event—SelectionChanged, not Click as I’d expected—with varying degrees of code. They look like so:
private void FontList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string fontName = FontList.SelectedItem.ToString();
SampleLabel.FontFamily = new System.Windows.Media.FontFamily(fontName);
}
private void FontStyleList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (FontStyleList.SelectedItem.ToString() == "Normal")
{
SampleLabel.FontStyle = FontStyles.Normal;
SampleLabel.FontWeight = FontWeights.Normal;
}
else if (FontStyleList.SelectedItem.ToString() == "Italic")
{
SampleLabel.FontStyle = FontStyles.Italic;
SampleLabel.FontWeight = FontWeights.Normal;
}
else if (FontStyleList.SelectedItem.ToString() == "Bold")
{
SampleLabel.FontStyle = FontStyles.Normal;
SampleLabel.FontWeight = FontWeights.Bold;
}
else // Bold Italic
{
SampleLabel.FontStyle = FontStyles.Italic;
SampleLabel.FontWeight = FontWeights.Bold;
}
}
private void FontSizeList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string size = FontSizeList.SelectedItem.ToString();
SampleLabel.FontSize = Convert.ToDouble(size);
}
My point here isn’t to teach you anything, nor am I particularly interested in any advice about how to do any of this better/more efficiently; you can’t get a full picture of this mess from the code snippets above. My point is only that this one thing—selecting a font—was very easy to do in Windows Forms. And it is very hard to do in more modern frameworks. Why Microsoft didn’t provide a modern Font picker for WPF is not just unclear to me, it’s criminal. It doesn’t make any sense.
And again, this is just one small aspect to this application. It was so small when I did it originally in WinForms, in fact, that I didn’t even write a whole article about it. Font selection was just part of an article that also included other work. Here, I’ve only provided you with some of the code I had to write to make font selection work in WPF. This isn’t even all of it.
My experience with both WPF and UWP has shown me that Microsoft really dropped the ball in its rush to move forward. Yes, there are major improvements in both, in particular with user interface design, and of course UWP provides developers with mobile application functionality for free, essentially, on top of that. But I cannot believe how much they skipped over, how many important and useful—necessary, really—bits were never added to either, even over many years. WPF is almost 15 years old!
Anyway. I’ll keep going. And I’ll see how much of .NETpad I can make with WPF. But this is mostly very frustrating. And that frustration just seems unnecessary.
With technology shaping our everyday lives, how could we not dig deeper?
Thurrott Premium delivers an honest and thorough perspective about the technologies we use and rely on everyday. Discover deeper content as a Premium member.