single.php

C# WinUI3でウィンドウを半透明にする方法[SetLayeredWindowAttributes]

C# WinUI 3アプリを作っていく途中で、調べたことを忘録的に投稿します。今回はWinUI3プロジェクトで[SetLayeredWindowAttributes]を利用してウィンドウの背景を半透明にする手順です。

[SetLayeredWindowAttributes]を利用

昔ながらの方法でWin32 APIで用意[SetLayeredWindowAttributes]を利用してウィンドウを半透明にできます。

後で実行画面で確認できますが、この方法だと透過する対象がウィンドウ全体になり、ボタンなどのコントロールにも適用されます。

透過を切り替えるためのボタンなどを用意した画面を作成します。

<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
    <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
        <ToggleButton Name="myButton" Checked="myButton_Checked" Unchecked="myButton_Unchecked">透過</ToggleButton>
        <Slider Name="mySlider" ValueChanged="mySlider_ValueChanged" Minimum="10" Maximum="255" Value="128" />
        <Image Source="image.png" Width="80"/>
    </StackPanel>
</Grid>

SetLayeredWindowAttributes]を利用するためのコードを追加します。

bool IsTransparent = false;

[DllImport("user32.dll", EntryPoint = "GetWindowLong", SetLastError = true)]
private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", SetLastError = true)]
private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, COLORREF crKey, byte bAlpha, uint dwFlags);

private const int GWL_EXSTYLE = -20;
private const int WS_EX_LAYERED = 0x00080000;
private const uint TRANSPARENT_COLOR = 0x00000000;
private const uint LWA_COLORKEY = 0x00000001;
private const uint LWA_ALPHA = 0x00000002;

[StructLayout(LayoutKind.Sequential)]
internal struct COLORREF
{
  public byte R;
  public byte G;
  public byte B;

  public COLORREF(byte red, byte green, byte blue)
  {
    R = red; G = green; B = blue;
  }

  public static COLORREF FromColor(Windows.UI.Color color) => new(color.R, color.G, color.B);
}

private IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
{
  if (IntPtr.Size == 8)
  {
    return GetWindowLongPtr64(hWnd, nIndex);
  }
  else
  {
    return GetWindowLongPtr32(hWnd, nIndex);
  }
}

private IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
  if (IntPtr.Size == 8)
  {
    return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
  }
  else
  {
    return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
  }
}

bool AddExStyle(IntPtr handle, int style)
{
  int winFlags = (int)GetWindowLongPtr(handle, GWL_EXSTYLE);

  if (winFlags == 0)
  {
    int error = Marshal.GetLastWin32Error();
    if (error != 0)
    {
      return false;
    }
  }

  if ((winFlags & WS_EX_LAYERED) == 0)
  {
    winFlags |= WS_EX_LAYERED;
    SetWindowLongPtr(handle, GWL_EXSTYLE, new IntPtr(winFlags));
  }
  return true;
}

private bool SetOpacity(IntPtr handle, COLORREF colorkey, byte alpha, uint flag)
{
  if (!AddExStyle(handle, WS_EX_LAYERED))
  {
    return false;
  }
  if (!SetLayeredWindowAttributes(handle, colorkey, alpha, flag))
  {
    return false;
  }
  return true;
}

最後に、コントロールに追加したイベントを追加します。

private void mySlider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
  if(IsTransparent == true)
  {
    IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
    Windows.UI.Color color = new Windows.UI.Color();
    color.A = 0;
    color.R = 0;
    color.G = 0;
    color.B = 0;
    SetOpacity(hWnd, COLORREF.FromColor(color), (byte)mySlider.Value, LWA_ALPHA);
  }
}

private void myButton_Checked(object sender, RoutedEventArgs e)
{
  IsTransparent = true;
  ApplyTransparent(IsTransparent);
  myButton.Content = "非透過";
}

private void myButton_Unchecked(object sender, RoutedEventArgs e)
{
  IsTransparent = false;
  ApplyTransparent(IsTransparent);
  myButton.Content = "透過";
}

実行後[透過]ボタンを選択するとウィンドウが半透明になります。

SetLayeredWindowAttributes]を利用してウィンドウに透過処理を行うと、ボタンやスライダーなどのコントロールも透過されます。

そのため、背景だけ透過にしたい場合などには使えません。

まとめ

今回は、WinUI3プロジェクトで[SetLayeredWindowAttributes]を利用してウィンドウの背景を半透明にする手順手順について紹介しました。

Win32で用意されている[SetLayeredWindowAttributes]関数を利用すると、実行時にウィンドウを透過にできます。

しかし、ウィンドウ上に追加したボタンなどのコントロールも透過されるため背景透過などの用途では使えません。

WinUI 3で実行時にウィンドウを半透明にしたい人の参考になれば幸いです。

スポンサーリンク

最後までご覧いただき、ありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です