To try it out for yourself, download the demo app.
These kinds of animations have a range of applications:
- Instant feedback, when button is pressed or action performed
- Loading animations
- Progress bars, where animation progress = percentage progress
- Red flashes for warnings/alerts
The project was born while working on my upcoming HTTP Client desktop app HyperPost. I wanted a loading animation to play when a request was sent.
Remember old web browsers used to have very thin progress bars while a page was being fetched and rendered? Many mobile web browsers still have this ... see the Chrome for Android screenshot below.
But these loading bars are placed between the application toolbar and the webpage content, whereas for my apps, these are not easily distinguishable. So I thought "where can I put a progress bar without breaking up the UI?" ... "how about on the window border itself?". It works surprisingly well.
Now I'll proceed to give a little FAQ summary, and explain the inner-workings.
Follow the instructions on the GitHub Readme. You can clone the project, and build on it. Or setup the required dependencies and copy the code into your own project.
Your Window class should take the following structure where <c:BorderLoop>
is the component containing one example border animation effect. Different components are available for different effects, each with different properties such as BColor
to set the color of the border animation.
<Window ...>
<Grid>
<!-- Add animations you want to use here -->
<c:BorderLoop x:Name="BorderLoop" BColor="Yellow"></c:BorderLoop>
<!-- Place your windows contents within this grid -->
<Grid Margin="1">
...
</Grid>
</Grid>
</Window>
Then to start the animation use the .Start()
method, e.g. BorderLoop.Start();
.
Can I use it for border animations on other WPF controls?
Yes. Use the same structure as above in place of your component:
<Grid>
<c:BorderLoop x:Name="BorderLoop" BColor="Yellow"></c:BorderLoop>
<MyControl Margin="1"/>
</Grid>
How does it add colours above the TitleBar?
To colour the borders, the window border width is actually set to 0, and custom controls are used within the window to make the colours appear. That means on the top border, a control has to be rendered above the titlebar. So how does that work?
In WPF titlebars are difficult to customise. Default WPF titlebars cannot be changed, hence they need to be removed entirely with a custom title bar put in their place. That's not an easy thing to do; you often loose native functionality by reimplementing titlebars youself. See my previous attempts creating customiable titlebars in WPF, and HTML-based WPF titlebars in blazor dektop apps.
... but WinUI provides much better controls for this. Hence I recommend using the ModernWpf framework which takes native titlebars from WinUI and lets you use them in your WPF application - and place additional controls like fake window borders within them! This project uses ModernWpf for this purpose.
How do the animations work? Are there performance overheads?
It uses WPF's built-in animations and storyboards … so it's very efficient.
However, animations in WPF are usually pixel based. You assign a From
value and a To
value in terms of pixels. This doesn't work very well for window border animation which need to span the full width or half-width or third-width of the window, and smoothly animate between these points in a way that scales correctly on window resizes. Of course that's never a problem in web development as CSS allow percentage based widths and heights, but sadly not in WPF 😢
Instead the way to achieve this in WPF is using Grids with column/row widths/heights using stars. E.g. setting a column to have width "43*" and a second column of width "100*" creates a 43:100 ratio or width of 43%.
Animating grid column widths and row heights like is this not possible out-of-the-box, instead I found an existing custom AnimationTimeline implementation from MahApps. You can find the source here.
You assign the component a name:
<Grid.ColumnDefinition Width="*" x:Name="MyColumn"/>
If you look at the codebehind for each animation component, you'll see the animations created like this:
GridLengthAnimation anim = new GridLengthAnimation();
anim.From = new GridLength(fromValue, GridUnitType.Star);
anim.To = new GridLength(toValue, GridUnitType.Star);
anim.Duration = ... ;
Storyboard.SetTargetName(anim, "MyColumn");
Storyboard.SetTargetProperty(anim, new PropertyPath(ColumnDefinition.WidthProperty));
The top dash animation is more complex, it consists of 16 columns which alernate between empty and coloured-in. The idea being that you only need to animate the widths of the first and last columns. The animation comes in two stages, first expanding width of column 2 and shrinking column 16, followed by expanding column 1 and shrinking column 15. After which, the animation immediately resets.
The complete border dash animation applies this to all 4 edges of the window at the same time, to make it look like the dashes are flowing around the entire window border.
That’s it
If you find it useful please star the github repository ⭐⭐⭐
Comments
Post a Comment