WatermarkAdorner class code:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace WPF水印装饰器
{
/// <summary>
/// Watermark adorner
/// </summary>
public class WatermarkAdorner : Adorner
{
private string _watermarkText;
public WatermarkAdorner(UIElement adornedElement, string watermarkText) : base(adornedElement)
{
_watermarkText = watermarkText;
this.IsHitTestVisible = false; // Make the watermark not capture events
}
protected override void OnRender(DrawingContext drawingContext)
{
Rect rect = new Rect(this.AdornedElement.RenderSize);
double centerX = rect.Right / 2.0;
double centerY = rect.Bottom / 2.0;
drawingContext.PushOpacity(0.5);
RotateTransform rotateTransform = new RotateTransform(45, centerX, centerY);
drawingContext.PushTransform(rotateTransform);
RotateTransform rt = new RotateTransform(-45, centerX, centerY);
Point point = default(Point);
double n = 5.0;
double margin = 40;
double halfWidth = GetTextLength(_watermarkText) * 10 / 2.0;
// Row 1: 3 items
point = RotatePoint(0.5, 0.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
point = RotatePoint(2.5, 0.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
point = RotatePoint(4.5, 0.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
// Row 2: 2 items
point = RotatePoint(1.5, 1.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
point = RotatePoint(3.5, 1.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
// Row 3: 3 items
point = RotatePoint(0.5, 2.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
point = RotatePoint(2.5, 2.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
point = RotatePoint(4.5, 2.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
// Row 4: 2 items
point = RotatePoint(1.5, 3.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
point = RotatePoint(3.5, 3.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
// Row 5: 3 items
point = RotatePoint(0.5, 4.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
point = RotatePoint(2.5, 4.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
point = RotatePoint(4.5, 4.5, n, rt, rect, margin);
DrawText(point.X, point.Y, halfWidth, drawingContext, _watermarkText);
}
private void DrawText(double x, double y, double textHalfWidth, DrawingContext drawingContext, string text)
{
int fontSize = 20;
SolidColorBrush colorBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#eeeef2"));
Point point = new Point(x - textHalfWidth, y - fontSize / 2.0);
FormattedText formattedText = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("宋体"), fontSize, colorBrush);
drawingContext.DrawText(formattedText, point);
}
/// <summary>
/// Rotate Point
/// </summary>
/// <param name="ratioX">Ratio of the text center point to the width of the area divided into n equal parts</param>
/// <param name="ratioY">Ratio of the text center point to the length of the area divided into n equal parts</param>
/// <param name="n">Divide the area width and length into n equal parts</param>
/// <param name="rotateTransform">Rotation object</param>
/// <param name="rect">Area</param>
/// <param name="margin">Margin</param>
private Point RotatePoint(double ratioX, double ratioY, double n, RotateTransform rotateTransform, Rect rect, double margin)
{
return rotateTransform.Transform(new Point(ratioX / n * rect.Right, ratioY / n * (rect.Bottom - 2 * margin) + margin));
}
#region Calculate text length (Chinese characters count as 2, uppercase letters count as 1.5, lowercase letters count as 1)
/// <summary>
/// Calculate text length (Chinese characters count as 2, uppercase letters count as 1.5, lowercase letters count as 1)
/// </summary>
private double GetTextLength(string text)
{
double length = 0;
Regex reg1 = new Regex("[\u4E00-\u9FFF]|[\uFE30-\uFFA0]");
Regex reg2 = new Regex("[A-Z]");
foreach (char c in text)
{
if (reg1.IsMatch(c.ToString()))
{
length += 2;
}
else if (reg2.IsMatch(c.ToString()))
{
length += 1.5;
}
else
{
length += 1;
}
}
return length;
}
#endregion
}
}
How to use:
In the Loaded method of the form or control, add the following code:
UIElement uiElement = (UIElement)this.Content;
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(uiElement);
adornerLayer.Add(new WatermarkAdorner(uiElement, _watermarkText));
Complete MainWindow.xaml code:
<Window
x:Class="WPF水印装饰器.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF水印装饰器"
mc:Ignorable="d"
Title="MainWindow"
Height="1040"
Width="1920"
Loaded="Window_Loaded"
WindowStyle="None"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
MouseRightButtonDown="Window_MouseRightButtonDown"
>
<Window.Template>
<ControlTemplate TargetType="{x:Type Window}">
<!-- ControlTemplate does not contain AdornerDecorator; you need to add AdornerDecorator in the ControlTemplate -->
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
</ControlTemplate>
</Window.Template>
<Window.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="tmplBtn" TargetType="{x:Type Button}">
<Border x:Name="border" Background="#068d6b" CornerRadius="5">
<TextBlock
Text="{TemplateBinding Content}"
Foreground="#ffffff"
HorizontalAlignment="Center"
VerticalAlignment="Center"
></TextBlock>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter
TargetName="border"
Property="Background"
Value="#069d8b"
></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="grid" Background="#094760">
<button
x:Name="button"
Content="Show Child Form"
Margin="0,0,0,0"
Width="100"
Height="35"
Click="button_Click"
Template="{StaticResource tmplBtn}"
></button>
<button
x:Name="button2"
Content="Show Child Form 2"
Margin="0,100,0,0"
Width="100"
Height="35"
Click="button2_Click"
Template="{StaticResource tmplBtn}"
></button>
</Grid>
</Window>
Note: If the form or control uses a ControlTemplate, because ControlTemplate does not contain AdornerDecorator, you need to add AdornerDecorator in the ControlTemplate.
Complete MainWindow.xaml.cs code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WPF水印装饰器
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private string _watermarkText = "Continuous R&D Test Account 34.8.99.64";
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
UIElement uiElement = (UIElement)this.Content;
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(uiElement);
adornerLayer.Add(new WatermarkAdorner(uiElement, _watermarkText));
}
private void Window_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
this.Close();
}
private void button_Click(object sender, RoutedEventArgs e)
{
Window2 win = new Window2(_watermarkText);
win.Owner = this;
win.WindowStartupLocation = WindowStartupLocation.CenterScreen;
win.Show();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
Watermark win = new Watermark(_watermarkText);
win.Owner = this;
win.WindowStartupLocation = WindowStartupLocation.CenterScreen;
win.Show();
}
}
}
Effect image:

There is a PPT plugin called "iSlide", whose Windows client is developed using WPF. It has a feature called PPT collage, and the watermark function in it is similar to the code above. Let's take a look at the effect and end this article:
