WPF:AdornerDecoratorデコレータを使用して透かしを実装する

WPF:AdornerDecoratorデコレータを使用して透かしを実装する

ほぼコードのみ

最終更新 2021/09/09 23:55
秋荷雨翔
読了目安 7 分
カテゴリ
WPF
タグ
.NET WPF 透かし

ウォーターマークデコレータ WatermarkAdorner クラスのコード:

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">マージン</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

    }
}

使用方法:

フォームまたはコントロールの Loaded メソッド内で、次のコードを追加します:

UIElement uiElement = (UIElement)this.Content;
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(uiElement);
adornerLayer.Add(new WatermarkAdorner(uiElement, _watermarkText));

完全な MainWindow.xaml コード:

<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>

注意:フォームまたはコントロールで ControlTemplate を使用する場合、ControlTemplate には AdornerDecorator が含まれていないため、ControlTemplate に AdornerDecorator を追加する必要があります。

完全な MainWindow.xaml.cs コード:

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();
        }
    }
}

効果図:

効果図

iSlide」というPPTプラグインがあります。そのWindowsクライアントはWPFで開発されており、PPTパズル機能があります。その中のウォーターマーク機能は上記と同様のコードを使用しています。効果を見てこの記事を終えましょう:

iSlide-PPTパズル

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2025/09/13

WPF から Avalonia への移行シリーズ:なぜ WPF プログラムを Avalonia に移行しなければならないのか

過去数年間、当社の上位機ソフトウェアは主に WPF と WinForm で開発されてきました。これらの技術は Windows プラットフォームで非常に便利であり、小規模試作から現在の規模拡大による納品まで、私たちを支えてきました。しかし、ビジネスの発展や顧客ニーズの変化に伴い、単一の Windows テクノロジースタックは私たちが必ず乗り越えなければならない壁となってきました。

続きを読む
同じカテゴリ / 同じタグ 2025/01/26

WPF カスタムXMLファイルによる国際化

この記事では、WPFプログラムでカスタムXMLファイルを使用して国際化を実現する方法について詳しく説明します。必要なNuGetパッケージのインストール、言語リストの動的取得、言語の動的切り替え、コードおよびXAMLインターフェースでの翻訳文字列の使用などを含み、ソースコードのリンクも提供し、開発者がWPFアプリケーションの国際化を簡単に実装できるように支援します。

続きを読む