Window code, note that we are binding the Overlay to the ImageSource property of the Counter.
<Window x:Class="IMAPChecker.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:main="clr-namespace:IMAPChecker"
>
<Window.TaskbarItemInfo>
<TaskbarItemInfo
Overlay="{Binding ElementName=Counter, Path=ImageSource}"
/>
</Window.TaskbarItemInfo>
<Grid Margin="10">
…
<main:CountControl x:Name="Counter"
DisplayCount="{Binding Path=Unseen}"
HasIssue="{Binding Path=HasError}"
IsChecking="{Binding Path=IsChecking}"
/>
…
</Grid>
</Window>
The counter control Xaml is mainly defining brushes.
<UserControl x:Class="IMAPChecker.CountControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cc="clr-namespace:IMAPChecker"
mc:Ignorable="d" Height="16" Width="16">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="boolToVisibility"/>
<RadialGradientBrush x:Key="EmptyBrush"
GradientOrigin="0.5,0.1" Center="0.5,0" RadiusX="2" RadiusY="0.9" >
<GradientStop Color="#a0ffffff" Offset="0" />
<GradientStop Color="#40ffffff" Offset="0.6"/>
<GradientStop Color="#40000000" Offset="0.7"/>
<GradientStop Color="#00000000" Offset="1.25"/>
</RadialGradientBrush>
<RadialGradientBrush x:Key="HighlightBrush"
GradientOrigin="0.5,0.1" Center="0.5,0" RadiusX="2" RadiusY="0.9" >
<GradientStop Color="LightGreen" Offset="0" />
<GradientStop Color="Green" Offset="0.6"/>
<GradientStop Color="DarkGreen" Offset="0.7"/>
<GradientStop Color="Green" Offset="1.25"/>
</RadialGradientBrush>
<RadialGradientBrush x:Key="MutedBrush"
GradientOrigin="0.5,0.1" Center="0.5,0" RadiusX="2" RadiusY="0.9" >
<GradientStop Color="LightYellow" Offset="0" />
<GradientStop Color="CornflowerBlue" Offset="0.6"/>
<GradientStop Color="Blue" Offset="0.7"/>
<GradientStop Color="Gray" Offset="1.25"/>
</RadialGradientBrush>
<RadialGradientBrush x:Key="CheckingBrush"
GradientOrigin="0.5,0.1" Center="0.5,0" RadiusX="2" RadiusY="0.9" >
<GradientStop Color="LightYellow" Offset="0" />
<GradientStop Color="Gold" Offset="0.6"/>
<GradientStop Color="GoldenRod" Offset="0.7"/>
<GradientStop Color="Gold" Offset="1.25"/>
</RadialGradientBrush>
<RadialGradientBrush x:Key="IssueBrush"
GradientOrigin="0.5,0.1" Center="0.5,0" RadiusX="2" RadiusY="0.9" >
<GradientStop Color="LightPink" Offset="0" />
<GradientStop Color="Red" Offset="0.6"/>
<GradientStop Color="DarkRed" Offset="0.7"/>
<GradientStop Color="Red" Offset="1.25"/>
</RadialGradientBrush>
</UserControl.Resources>
<Border BorderThickness="1" Name="ColourBorder"
CornerRadius="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{StaticResource CheckingBrush}">
<Viewbox HorizontalAlignment="Center">
<TextBlock Name="TextBlock"
HorizontalAlignment="Stretch" VerticalAlignment="Center"
TextAlignment="Center" TextWrapping="NoWrap"
Foreground="Gray" FontWeight="Bold"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type cc:CountControl}}, Path=DisplayCount}"
/>
<!--Text="1"-->
</Viewbox>
<Border.BorderBrush>
<LinearGradientBrush EndPoint="1,1" StartPoint="0,0">
<GradientStop Color="LightGray" Offset="0" />
<GradientStop Color="DarkSlateGray" Offset="1" />
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
</UserControl>
The fun begins in the code behind, first there is state logic mapping the state to brushes, which I could alternatively do via a Convertor but the logic seemed to be part of the control so made sense here
public partial class CountControl : UserControl
{
public CountControl()
{
InitializeComponent();
InitializeBitmapGeneration();
DisplayCount = 40;
}
public static readonly DependencyProperty DisplayCountProperty = DependencyProperty.Register(
"DisplayCount",
typeof(int),
typeof(CountControl),
new UIPropertyMetadata(0,
(d, e) => ((CountControl)d)._OnDisplayChanged()));
public static readonly DependencyProperty HasIssueProperty = DependencyProperty.Register(
"HasIssue",
typeof(bool),
typeof(CountControl),
new UIPropertyMetadata(false,
(d, e) => ((CountControl)d)._OnDisplayChanged()));
public static readonly DependencyProperty IsCheckingProperty = DependencyProperty.Register(
"IsChecking",
typeof(bool),
typeof(CountControl),
new UIPropertyMetadata(false,
(d, e) => ((CountControl)d)._OnDisplayChanged()));
public static readonly DependencyProperty HasVolumeProperty = DependencyProperty.Register(
"HasVolume",
typeof(bool),
typeof(CountControl),
new UIPropertyMetadata(false,
(d, e) => ((CountControl)d)._OnDisplayChanged()));
public int DisplayCount
{
get { return (int)GetValue(DisplayCountProperty); }
set { SetValue(DisplayCountProperty, value); }
}
public bool HasIssue
{
get { return (bool)GetValue(HasIssueProperty); }
set { SetValue(HasIssueProperty, value); }
}
public bool HasVolume
{
get { return (bool)GetValue(HasVolumeProperty); }
set { SetValue(HasVolumeProperty, value); }
}
public bool IsChecking
{
get { return (bool)GetValue(IsCheckingProperty); }
set { SetValue(IsCheckingProperty, value); }
}
private void _OnDisplayChanged()
{
ImageSource = null;
if (IsChecking)
{
ColourBorder.Background = (Brush)Resources["CheckingBrush"];
TextBlock.Foreground = Brushes.Black;
return;
}
if (HasIssue)
{
ColourBorder.Background = (Brush)Resources["IssueBrush"];
TextBlock.Foreground = Brushes.White;
return;
}
if (HasVolume == false)
{
ColourBorder.Background = (Brush)Resources["MutedBrush"];
TextBlock.Foreground = Brushes.Black;
return;
}
if (DisplayCount == 0)
{
ColourBorder.Background = (Brush)Resources["EmptyBrush"];
TextBlock.Foreground = Brushes.Black;
}
else
{
ColourBorder.Background = (Brush)Resources["HighlightBrush"];
TextBlock.Foreground = Brushes.White;
}
}
And then we have the fun part where we convert the Control into an image.
#region Should be in base class
protected void InitializeBitmapGeneration()
{
LayoutUpdated += (sender, e) => _UpdateImageSource();
}
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
"ImageSource",
typeof(ImageSource),
typeof(CountControl),
new PropertyMetadata(null));
/// <summary>
/// Gets or sets the ImageSource property. This dependency property
/// indicates ....
/// </summary>
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
private void _UpdateImageSource()
{
if (ActualWidth == 0 || ActualHeight == 0)
{
return;
}
ImageSource = GenerateBitmapSource(this, 16, 16);
}
public static BitmapSource GenerateBitmapSource(ImageSource img)
{
return GenerateBitmapSource(img, img.Width, img.Height);
}
public static BitmapSource GenerateBitmapSource(ImageSource img, double renderWidth, double renderHeight)
{
var dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
dc.DrawImage(img, new Rect(0, 0, renderWidth, renderHeight));
}
var bmp = new RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96, PixelFormats.Pbgra32);
bmp.Render(dv);
return bmp;
}
public static BitmapSource GenerateBitmapSource(Visual visual, double renderWidth, double renderHeight)
{
var bmp = new RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96, PixelFormats.Pbgra32);
var dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
dc.DrawRectangle(new VisualBrush(visual), null, new Rect(0, 0, renderWidth, renderHeight));
}
bmp.Render(dv);
return bmp;
}
#endregion
}
}