WPF: Watermarking using the AdornerDecorator

WPF: Watermarking using the AdornerDecorator

Basically it's all code

最后更新 9/9/2021 11:55 PM
秋荷雨翔
预计阅读 7 分钟
分类
WPF
标签
.NET WPF watermark

Watermark decorator 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>
    /// 水印装饰器
    /// </summary>
    public class WatermarkAdorner : Adorner
    {
        private string _watermarkText;

        public WatermarkAdorner(UIElement adornedElement, string watermarkText) : base(adornedElement)
        {
            _watermarkText = watermarkText;
            this.IsHitTestVisible = false; //使水印不捕获事件
        }

        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;

            //第1排3个
            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);

            //第2排2个
            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);

            //第3排3个
            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);

            //第4排2个
            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);

            //第5排3个
            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>
        /// 旋转Point
        /// </summary>
        /// <param name="ratioX">文本中心点占区域宽度n等分的比例</param>
        /// <param name="ratioY">文本中心点占区域长度n等分的比例</param>
        /// <param name="n">区域长宽n等分</param>
        /// <param name="rotateTransform">旋转对象</param>
        /// <param name="rect">区域</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 计算文本长度(汉字计为2 大写字母计为1.5 小写字母计为1)
        /// <summary>
        /// 计算文本长度(汉字计为2 大写字母计为1.5 小写字母计为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不包含AdornerDecorator,需要在ControlTemplate中添加AdornerDecorator -->
      <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="显示子窗体"
      Margin="0,0,0,0"
      Width="100"
      Height="35"
      Click="button_Click"
      Template="{StaticResource tmplBtn}"
    ></button>
    <button
      x:Name="button2"
      Content="显示子窗体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 ControlTemplate, since the ControlTemplate does not contain AdornerDecorator, you need to add AdornerDecorator to the ControlTemplate. **

Full 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>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private string _watermarkText = "持续研发测试账号 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();
        }
    }
}

Renderings:

效果图

有一款 PPT 插件叫"iSlide",其中 Windows 客户端是用 WPF 开发的,有个功能是 PPT 拼图,其中的水印功能就是上面类似的代码,我们看看效果结束本文:

iSlide-PPT拼

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 9/13/2025

Migration from WPF to Avalonia series: Why I have to migrate WPF programs to Avalonia

In the past few years, our host computer software has been mainly developed using WPF and WinForm. These technologies are really easy to use on the Windows platform, and they have also accompanied us through the stage of small-scale trial production to today's large-scale delivery. However, with the development of business and changes in customer needs, the single Windows technology stack has gradually become a hurdle that we must overcome.

继续阅读
同分类 / 同标签 1/26/2025

WPF internationalizes with custom XML files

This article describes in detail the methods of using custom XML files to achieve internationalization in WPF programs, including installing the necessary NuGet package, dynamically obtaining language lists, dynamically switching languages, using translation strings in code and xaml interfaces, etc. It also provides source code links to help developers easily internationalize WPF applications.

继续阅读