Custom windows in wpf

Magnus Montin

My blog about software development on the Microsoft® stack .

How to create a custom window in WPF

This introductory post will provide a walkthrough on how to create your own custom looking window control with resize, drag, minimize, restore and close functionality and how to use it in your WPF applications.

The solution includes two Visual Studio projects, the first one a class library where the control itself is implemented and the second one a sample WPF application which will have a reference to the class library and use the custom window control as its main window.

Get started

Start by creating a new C# class library in Visual Studio (File->New Project) and give it a suitable name like Mm.Wpf.Controls.

Add references

After removing the automatically generated Class1.cs from the project the first thing to do is to add some references. We will need a reference to the PresentationFramework.dll where the out-of-the-box System.Windows.Window class, which our custom window class will inherit from, is implemented. We also need to add a reference to PresentationCore.dll where the RoutedEventArgs class lives, System.Xaml.dll and WindowsBase.dll. You add a reference in Visual Studio by choosing “Add reference” from the Project menu or by right-clicking the project in the solution explorer and the required DLLs should be found under the .NET tab.

Implement the control

The next step is to add a new class called CustomWindow.cs to the project by right-clicking the project name in the solution explorer and choose Add->Class. The class will be public and inherit from the default WPF window class (Sytem.Windows.Window). By extending the default Window class we can use its built-in functionality for minimizing, restoring and closing the window. We add the three click event handlers shown below to take care of this.

When you create a custom control in WPF you define a template for it in XAML. The default style must be located in a folder called “Themes” at the root of the class library project and for our custom window control to be able to find its default style we add a static constructor where the default value of the DefaultStyleKey property is set to the type object of our class.

To define the style for the window we then add a “Themes” folder to the project. Inside this folder you can add different templates for each Windows theme but since theming is out of the scope of this article we will only add a so called fallback style. This style must be located in (or defined in a file referenced from) a file called “Generic.xaml” in the “Themes” folder.

Since the project template is a generic class library you probably won’t find the option to add a WPF Resource dictionary when right-clicking the newly created folder and choosing Add. Instead, choose to add a simple text file, name it “Generic.xaml” and give it the below content.

Note that the buttons for minimizing, restoring and closing the window have their own template defined above the actual custom window template. Also these buttons have been assigned a name by the x:Name attribute for us to be able to find them in code and hook up their click event handlers. This is done by overriding the OnApplyTemplate in our CustomWindow class.

Читайте также:  При запуске windows завершение работы

Make it draggable

At this stage our custom window is almost (the ThemeAttribute is missing) ready to be used in a WPF application but it cannot yet be dragged around on the screen nor resized. You may have noticed that the template above contains a rectangle named “moveRectangle” and this one will serve as a ‘handle’ to drag our window. We add a PreviewMouseDown event handler for the Rectangle in the OnApplyTemplate method and implement it as below.

Make it resizable

To make the window resizable requires a bit more effort. We start by adding a bunch of invisible (Fill=”Transparent”) rectangles, one for each side of the window and another one for each corner of the window, to our XAML defined template. These rectangles will serve as the window’s edges and we will show the resizing cursors when the mouse moves over these. It is accomplished by implementing the MouseMove event for each of the rectangles. Note that the rectangle’s name decides which type of cursor to display.

We also need to remember to reset the cursor to its default state when the mouse moves from one of the edges into the content area and for this we add a constructor to our custom window class and an event handler to the Window’s PreviewMouseMove event.

To implement the actual resizing behavior in the PreviewMouseDown event handler for each of the edge rectangles we then make use of the platform invocation services (PInvoke) to call the unmanaged Windows API SendMessage method.

For the above ResizeWindow wrapper method to compile we need to add a member variable of type HwndSource to represent our window and provide access to its window handle. The variable is initialized in the Window.SourceInitialized event.

At this point the class library should compile just fine if you have included the following required using statements at the top of the CustomWindow.cs file.

The last thing to do is to add a ThemeInfo attribute to our assembly to specify where WPF should look for the possible theme dictionaries and the generic dictionary. As we don’t have any specific theme styles and our Generic.xaml is located in the current assembly we add the following to the automatically generated AssemblyInfo.cs located under the Properties folder at the root of the project.

Use the control in a WPF application

The CustomWindow control is now ready be used. We add a new WPF application to our solution by choosing File->Add->New project on the menu bar.

We then set the newly added project to the startup project by right-clicking on it in the solution explorer and choosing “Set as StartUp project”. If you run the solution at this point you should see an ordinary empty window popup on your screen.

To be able to replace this window with our custom style window, we first add a reference to the Mm.Wpf.Controls class library by right-clicking the WPF application project and choosing “Add reference” and the “Projects” tab.

Then we simply edit the MainWindow class, both the code-behind C# and the XAML, to inherit from our custom CustomWindow class instead of the default Window class as shown below.

The application’s main window should now show up with our custom style and you should be able to drag it around on the screen, resize it, minimize it, restore it and close it just like you would with the default window.

Implementing a Custom Window Title Bar in WPF

There are several good reasons for wanting custom window chrome in WPF, such as fitting in additional UI or implementing a Dark theme. However the actual implementation is kind of tricky, since it is now your job to provide a bunch of features that you used to get for free. I’ll walk you through my implementation.

Читайте также:  Все окна windows с черным фоном

Appearance

I chose to emulate the Windows 10 style.

Windows 10 standard title bar UI

This will keep my UI consistent with the rest of Windows. I am choosing not to attempt to match the style for old versions of Windows, as I don’t think that would be a great time investment. It’s one little thing you lose when going with a full custom title bar, but it should be worth it by allowing full cohesive theming. Buttons are 46 wide, 32 tall.

Building the UI

First, set WindowStyle=”None” on your Window. That will remove the built-in title bar and allow you to do everything on your own.

Next, allocate space for your title bar in your UI. I like to allocate a Grid row for it.

Add this under the node in your XAML:

The CaptionHeight tells the OS to treat the top 32px of your window as if it was a title bar. This means that click to drag works, along with double clicking to maximize/restore, shaking to minimize other windows, etc. The ResizeBorderThickness allows the standard window resize logic to work, so we don’t need to reimplement that either.

Now we need to make the actual UI. This is mine:

I’ve got the app icon. I chose not to implement the special drop-down menu that comes with the standard title bar since it’s not often used and other major apps like Visual Studio Code don’t bother with it. But it’s certainly something you could add.

The title text has a trigger to change its color based on the “Active” state of the window. That allows the user to better tell if the window has focus or not.

The actual buttons use TitleBarButtonStyle and TitleBarCloseButtonStyle:

These are buttons with stripped-down control templates to remove a lot of the extra gunk. They have triggers to change the background color on mouse over (and the foreground color in the case of the Close button). Also they set WindowChrome.IsHitTestVisibleInChrome to True, which allows them to pick up clicks even though they are in the 32px caption area we set up earlier.

The button content itself uses

to draw the icons. The minimize button uses RenderOptions.EdgeMode=”Aliased” to disable anti-aliasing and make sure it renders crisply without blurring over into other pixels. I set the Stroke to pick up the Foreground color from the parent button. The Path data for the maximize/restore buttons are all set on .5 to make sure it renders cleanly at the standard 96 DPI. With whole numbers it ends up drawing on the edge of the pixel and blurring the lines. We can’t use the same “Aliased” trick here as that might cause the pixel count for different lines to change and look off at different zoom levels like 125%/150%.

Looking good!

Responding to button clicks

Now that we have the UI in place, we need to respond to those button clicks. I normally use databinding/MVVM, but in this case I decided to bypass the viewmodel since these are really talking directly to the window.

Event handler methods:

Helper method to refresh the maximize/restore button:

This, we call in the constructor and in an event handler for Window.StateChanged:

This will make sure the button displays correctly no matter how the window state change is invoked.

Maximized placement

You thought we were done? Hah. Windows has other plans. You might notice that when you maximize your window, some content is getting cut off and it’s hiding your task bar. The default values it picks for maximized window placement are really weird, where it cuts off 7px of your window content and doesn’t account for task bar placement.

Читайте также:  Windows 10 icon gone

To fix this, we need to listen for the WM_GETMINMAXINFO WndProc message to tell our window it needs to go somewhere different when maximize. Put this in your window codebehind:

When the system asks the window where it should be when it maximizes, this code will ask what monitor it’s on, then place itself in the work area of the monitor (not overlapping the task bar).

Window border

Finally, the window can be kind of hard to pick out when it doesn’t have a border, if it’s put against the wrong background. Let’s fix that now. Wrap your window UI in this:

This is the style:

This will remove the 1px border when the window is maximized.

Okay, now we’re actually done

At least until Windows decides they want to shake up the title bar style again.

How to create custom window chrome in wpf?

How can I create a basic custom window chrome for a WPF window, that doesn’t include the close button and still a moveable and resizeable window?

5 Answers 5

You set your Window’s WindowStyle=»None» , then build your own window interface. You need to build in your own Min/Max/Close/Drag event handlers, but Resizing is still maintained.

And here’s some example code-behind for common window functionality

.NET 4.5 added a new class that greatly simplifies this.

The WindowChrome class enables you to extend Windows Presentation Foundation (WPF) content into the non-client area of a window that is typically reserved for the operating system’s window manager.

I’ve just used the example below for .net 4.5 and it works very well. Interestingly, it uses a code behind for a resource dictionary for the click events. All you have to do is reference the resource dictionary in your app.xaml file and then assign the Window the Style CustomWindowStyle . This was shamelessly stolen from http://www.eidias.com/blog/2014/1/27/restyle-your-window.

And for the code behind:

Here is an easy solution which looks very similar to the default Windows 10 buttons, it simply uses the same Font for the symbols:

If you want Support for older Windows Versions (7 and 8) have a look here: https://stackoverflow.com/a/27911618/9758687

Here’s an overview of the approach you’ll need to take:

  • Set WindowStyle=»None» to do your own UI.
  • Use WindowChrome.CaptionHeight to get standard title bar drag/double click/shake behavior and set WindowChrome.IsHitTestVisibleInChrome=»True» on your buttons to make them clickable.
  • Implement click handlers for your buttons.
  • Hook into the Window.StateChanged event to handle maximize/restore changes and update your UI. You can’t assume everyone is using your title bar buttons to maximize and restore. This can happen via keyboard shortcuts (Win+Up/Win+Down) or double-clicking the title bar.
  • 7px of your window gets cut off from all sides when you maximize. You need to compensate with extra margin when the window is maximized.
  • You’ll need to re-implement a window border to provide contrast over different backgrounds.
  • Use

to render the title bar icons so they look good at different DPIs.

  • Change the look of the title bar depending on whether the window is active, to give the user an indication of which window has focus.
  • Not the answer you’re looking for? Browse other questions tagged wpf xaml window or ask your own question.

    Linked

    Hot Network Questions

    Subscribe to RSS

    To subscribe to this RSS feed, copy and paste this URL into your RSS reader.

    site design / logo © 2021 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2021.4.16.39093

    By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.

    Оцените статью