もっと詳しく

あなたは、任意の画像を半透明の状態で最前面に表示したいと思ったことはありませんか?

たとえば間違い探しするとき、2つの画像のうち1つを半透明にして重ねたら、どこが違うか一目瞭然ですよね。

この技術は、アプリ開発において修正箇所の確認や、修正したことにる画面への影響が無いことを確認する際にも使えます。

もしそのような用途があるなら、この記事は必見です。

サンプルプログラムのイメージ

まず初めに、今回のサンプルプログラムは @ITの「WPF:ウィンドウを透明にするには?[C#/VB]」の記事に掲載されたサンプルプログラムをベースに、今回の用途に合うように且つVisual Studio 2022 で動作するように、少しだけカスタマイズしています。

より詳しい内容をお知りになりたい場合は、是非上記のサイトを訪問下さい。

さて、今回紹介するサンプルプログラムは次の機能を持っています。

  • ドラッグ&ドロップで画像を表示
  • 表示した画像の透明度を無段階に変更が可能
  • 常に最前面に表示
  • クリップボードの画像を表示可能

ソースコード一式(Projectファイル)のダウンロード場所

このサンプルプログラムは Visual Studio 2022 で作成しています。

ソースコード(EXEファイルも含む)は下記からダウンロードできます。

仕組み

仕組みとしては、枠の無い透明なWindow上にViewBoxを貼り付け、そこにドラッグ&ドロップした画像を表示、ViewBox の透明度を変えることで画像を透明表示しています。

Windowは常に最上位に表示することが可能なので、画像も常に最上位に表示することができます。

画像を目いっぱい表示するためにWindow枠を非表示にしており、このままではウィンドウの移動やリサイズができません。

そのため、マウスの左ボタンが押されたイベントの中に、ドラッグされた時の表示処理が記載されています。

サンプルプログラムの解説

今回のサンプルプログラムで使っているポイントは次の通りです。

Windowを常に最上位表示する

常に最上位に表示するには、Topmost に True を設定してあげればOKです。

XAMLで指定する場合は、下記の通りWindowタグに記述します。

<!-- XAMLによる指定 -->
<Window x:Class="TransparentView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    ~ 省略 ~
        Topmost="True"
    ~ 省略 ~
        Title="MainWindow" Height="450" Width="800">

コードで指定する場合は、下記のコードをコンストラクタなどに記述します。

//コードによる指定
this.Topmost = true;

Windowを透明表示する

XAMLのWindowタグに下記の3つを追加することで、枠がなく透明なWindowを作成します。

WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"

次に、ViewBoxの Opacity プロパティに透明度を設定するのですが、今回はスライダーで変更した値が設定されています。

<Viewbox AllowDrop="True" x:Name="uxView" >
     <Image Source="{Binding}" Margin="16" Opacity="{Binding ElementName=uxSlider, Path=Value}" />
     </Viewbox>

実際にはBorderも画面で使っているので、こちらも Opacity の値をスライダーと連動させて透明化しています。

クリップボードにある画像を表示する

ドラッグ&ドロップによる画像の表示と処理を共通化するため、クリップボードの画像を取り込んだ画像は、一旦テンポラリーファイルに格納し、それをViewBoxに表示するようにしています。

var img = Clipboard.GetImage();
if (img != null)
{
    using (var fileStream = new FileStream(TempFile, FileMode.Create))
    {
        BitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(img));
        encoder.Save(fileStream);
    }
}
FileName = TempFile;
this.DataContext = FileName;

プログラムソース全体

XAMLは次のようになります。

<Window x:Class="TransparentView.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:TransparentView"
        mc:Ignorable="d"
        WindowStyle="None"
        AllowsTransparency="True"
        Background="Transparent"
        ResizeMode="CanResizeWithGrip"
        Title="MainWindow" Height="450" Width="800">

    <!-- https://atmarkit.itmedia.co.jp/ait/articles/1510/07/news025.html -->
    <Grid>
        <!--
        このBorderコントロール(青色の四角形)には、Opacityプロパティを設定する。
        Opacityプロパティの値は、下のSliderコントロールで調整できる
        -->
        <Border Grid.RowSpan="2" Background="#00a2e8" Opacity="{Binding ElementName=uxSlider, Path=Value}" AllowDrop="True"/>


        <!-- このViewboxコントロールは、上のBorderコントロールの子要素ではない -->
        <Viewbox AllowDrop="True" x:Name="uxView" >
            <Image Source="{Binding}" Margin="16" Opacity="{Binding ElementName=uxSlider, Path=Value}" />
        </Viewbox>

        <!--
        このBorderコントロールも、上のBorderコントロールの子要素ではない
        このBorderコントロールには、Opacityプロパティは設定していないが、
        BackgroundプロパティはTransparentにしてある(アルファ値は0)
        -->

        <!-- 親要素の背景色がアルファ値0(=透明)であっても、子要素には影響しない -->
        <Slider x:Name="uxSlider" Background="#80a0a0a0" Margin="10,2" VerticalAlignment="Bottom" Minimum="0" Value="0.6" Maximum="1.0" />


        <TextBlock Text="{Binding}" Height="24" Margin="10,3,70,0" VerticalAlignment="Top"/>

        <!-- [クリップボードからコピーボタン -->
        <Button Click="GetClipButton_Click"   Content="Clip" Width="40" Height="20" Margin="0,5,32,0" HorizontalAlignment="Right" VerticalAlignment="Top"/>
        <!-- [×](閉じる)ボタン -->
        <Button Click="Button_Click"   Content="X" Width="20" Height="20" Margin="0,5,8,0" HorizontalAlignment="Right" VerticalAlignment="Top"/>
    </Grid>
</Window>

C#のソースコードは次のようになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.Drawing;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using static System.Net.Mime.MediaTypeNames;
using System.IO;

namespace TransparentView
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// 表示したいイメージファイル名
        /// </summary>
        public string FileName { get; set; } = "/Images/default.jpg";
        /// <summary>
        /// クリップボードからのイメージ取得用一時ファイル
        /// </summary>
        public string TempFile { get; set; } = System.IO.Path.GetTempPath() + "/TransViewTemp.$$$";
        public MainWindow()
        {
            InitializeComponent();

            //ウィンドウを最上位に表示する
            this.Topmost = true;

            // ウィンドウをマウスのドラッグで移動できるようにする
            this.MouseLeftButtonDown += (sender, e) => { this.DragMove(); };

            //初期イメージを表示する
            DataContext = FileName;

            //ドラッグ&ドロップを有効にする
            EnableDragDrop(uxView);
        }

        /// <summary>
        /// Xボタンクリック処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //画面を閉じる
            this.Close();
        }

        /// <summary>
        /// Clip ボタンクリック処理
        /// クリップボードからイメージを取得し一時ファイルに保存後、それを表示
        /// </summary>
        public void GetClipButton_Click(object sender, RoutedEventArgs e)
        {
            var img = Clipboard.GetImage();
            if (img != null)
            {
                using (var fileStream = new FileStream(TempFile, FileMode.Create))
                {
                    BitmapEncoder encoder = new PngBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(img));
                    encoder.Save(fileStream);
                }
            }
            FileName = TempFile;
            this.DataContext = FileName;

        }
        /// <summary>
        /// ドラッグ&ドロップ登録処理
        /// </summary>
        /// <param name="control"></param>
        private void EnableDragDrop(Viewbox control)
        {
            //ドラッグ&ドロップを受け付けられるようにする
            control.AllowDrop = true;

            //ドラッグが開始された時のイベント処理(マウスカーソルをドラッグ中のアイコンに変更)
            control.PreviewDragOver += (s, e) =>
            {
                //ファイルがドラッグされたとき、カーソルをドラッグ中のアイコンに変更し、そうでない場合は何もしない。
                e.Effects = (e.Data.GetDataPresent(DataFormats.FileDrop)) ? DragDropEffects.Copy : e.Effects = DragDropEffects.None;
                e.Handled = true;
            };

            //ドラッグ&ドロップが完了した時の処理(ファイル名を取得し、ファイルの中身をTextプロパティに代入)
            control.PreviewDrop += (s, e) =>
            {
                if (e.Data.GetDataPresent(DataFormats.FileDrop)) // ドロップされたものがファイルかどうか確認する。
                {
                    string[] paths = ((string[])e.Data.GetData(DataFormats.FileDrop));
                    FileName = paths[0];
                    this.DataContext = FileName;
                }
            };

        }
    }
}

まとめ

今回は、画像ファイルをドラッグ&ドロップし、常に最上位表示させながら透明度を変化させるWindowの作り方を紹介しました。

@ITの「WPF:ウィンドウを透明にするには?[C#/VB]」に掲載されているソースコードをコピーして、Visual Studio 2022で動作するように、且つクリップボードからの画像表示に対応するように少しだけ手を加えています。

2つの画像を重ね合わせて違いを見るというケースはあまりないかもしれませんが、もしそのような機会があれば、是非この記事を思い出してください。