Этот код является переводом (с небольшими интерпретациями) кода, найденного здесь:
Windows Форма Прозрачное фоновое изображение .
Первоначально из примеров кода Microsoft (по крайней мере, было, до того как их убили).
Когда форма становится прозрачной, задайте для ее TransparencyKey
тот же цвет, что и для BackGroundColor
, а затем нарисуйте полупрозрачный Растровое изображение на прозрачной поверхности формы, сглаженные части растрового изображения не смешиваются с тем, что находится за формой.
Цвет, используемый как TransparencyKey
, может повлиять на результат рендеринга, но полупрозрачные пиксели (особенно пиксели около краев растрового изображения) всегда будут видны на разных фонах, поскольку blending отсутствует.
Чтобы решить эту проблему, мы можем построить Многослойное окно :
Система автоматически создает и перерисовывает многослойные windows и windows базовых приложений. В результате слоистые windows отображаются плавно, без мерцания, характерного для сложных областей окна. Кроме того, слоистый windows может быть частично полупрозрачным, то есть альфа-смешанным.
Чтобы создать слоистую форму, мы можем установить WS_EX_LAYERED
расширенный стиль, переопределяющий свойство CreateParams формы:
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
Dim parms As CreateParams = MyBase.CreateParams
parms.ExStyle = parms.ExStyle Or WS_EX_LAYERED
Return parms
End Get
End Property
Windows 8+: стиль WS_EX_LAYERED
поддерживается для верхнего уровня windows и дочернего элемента windows. Предыдущие Windows версии поддерживают этот стиль только для верхнего уровня windows.
Чтобы нарисовать растровое изображение, которое может сливаться с фоном, мы выбираем растровое изображение в контекст устройства окна, а затем вызываем UpdateLayeredWindow , определяющий тип рендеринга с использованием структуры BLENDFUNCTION
.
Эта структура позволяет определить (BlendOp
) как исходные и целевые растровые изображения смешиваются (на самом деле, единственная возможная операция - Source Over, AC_SRC_OVER
), уровень непрозрачности, примененный к исходному растровому изображению (SourceConstantAlpha
: 255
= непрозрачный, 0
= полностью прозрачный) и как интерпретируются цвета исходных и целевых растровых изображений (AlphaFormat
).
Здесь мы хотим смешать исходное растровое изображение с альфа-каналом (на пиксель альфа), поэтому оно полупрозрачно: мы указываем AC_SRC_ALPHA
как AlphaFormat
( см. Документы о том, как смешивание цветов интерпретируется на основе типа «Цвет» исходного bItmap).
Вот и все.
Чтобы создать многослойную форму, добавьте новую форму в проект, измените конструктор, как показано здесь, добавьте переопределение CreateParams
, переопределение WndProc
, если форму можно перемещать, перетаскивая ее и SelectBitmap()
вызов метода, который активирует альфа-смешение исходного растрового изображения (переданного в конструкторе) и формы D C.
. Кроме того, добавьте класс поддержки NativeMethods
в проект:
► Форму можно создать как обычно, в этом случае передавая объект-конструктор растровому изображению:
(формат растрового изображения должен быть 32-битным ARGB
- будет работать PNG
с альфа-каналом )
Dim layeredForm As New PerPixelAlphaLayeredForm(bitmap)
layeredForm.Show()
Public Class PerPixelAlphaLayeredForm
Public Sub New()
Me.New(Nothing)
End Sub
Public Sub New(bitmap As Bitmap)
InitializeComponent()
Me.LayerBitmap = bitmap
End Sub
Private ReadOnly Property LayerBitmap As Bitmap
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
If Me.LayerBitmap IsNot Nothing Then
Me.ClientSize = Me.LayerBitmap.Size
Dim screenSize = Screen.FromHandle(Me.Handle).Bounds.Size
Me.Location = New Point((screenSize.Width - Me.Width) \ 2, (screenSize.Height - Me.Height) \ 2)
SelectBitmap(Me.LayerBitmap)
' Or, call the SelectBitmapFadeOut() method
' Task.Run(Function() SelectBitmapFadeOut(Me.LayerBitmap))
End If
Me.TopMost = True
End Sub
Private Sub SelectBitmap(bitmap As Bitmap)
NativeMethods.SelectBitmapToLayeredWindow(Me, bitmap, 255)
End Sub
Private Async Function SelectBitmapFadeOut(bitmap As Bitmap) As Task
Dim fadeProgress As Integer = 255
For i = fadeProgress To 1 Step -1
BeginInvoke(New MethodInvoker(Sub() NativeMethods.SelectBitmapToLayeredWindow(Me, bitmap, fadeProgress)))
fadeProgress -= 1
Await Task.Delay(10)
Next
End Function
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
Dim parms As CreateParams = MyBase.CreateParams
If Not DesignMode Then parms.ExStyle = parms.ExStyle Or NativeMethods.WS_EX_LAYERED
Return parms
End Get
End Property
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = NativeMethods.WM_NCHITTEST Then
m.Result = New IntPtr(NativeMethods.HTCAPTION)
Else
MyBase.WndProc(m)
End If
End Sub
End Class
NativeMethods
класс поддержки:
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Friend Class NativeMethods
Public Const HTCAPTION As Integer = &H2
Public Const WM_PAINT = &HF
Public Const WM_NCHITTEST As Integer = &H84
Public Const WS_EX_LAYERED As Integer = &H80000
Public Const AC_SRC_OVER As Byte = 0
Public Const AC_SRC_ALPHA As Byte = 1
<Flags>
Friend Enum ULWFlags
ULW_COLORKEY = &H1
ULW_ALPHA = &H2
ULW_OPAQUE = &H4
ULW_EX_NORESIZE = &H8
End Enum
<StructLayout(LayoutKind.Sequential)>
Friend Structure POINT
Public x As Integer
Public y As Integer
Public Sub New(X As Integer, Y As Integer)
Me.x = X
Me.y = Y
End Sub
End Structure
<StructLayout(LayoutKind.Sequential)>
Friend Structure SIZE
Public cx As Integer
Public cy As Integer
Public Sub New(cX As Integer, cY As Integer)
Me.cx = cX
Me.cy = cY
End Sub
End Structure
<StructLayout(LayoutKind.Sequential, Pack:=1)>
Friend Structure ARGB
Public Blue As Byte
Public Green As Byte
Public Red As Byte
Public Alpha As Byte
End Structure
<StructLayout(LayoutKind.Sequential, Pack:=1)>
Friend Structure BLENDFUNCTION
Public BlendOp As Byte
Public BlendFlags As Byte
Public SourceConstantAlpha As Byte
Public AlphaFormat As Byte
End Structure
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function UpdateLayeredWindow(hWnd As IntPtr, hdcDst As IntPtr, ByRef pptDst As POINT,
ByRef psize As SIZE, hdcSrc As IntPtr, ByRef pprSrc As POINT, crKey As Integer,
ByRef pblend As BLENDFUNCTION, dwFlags As ULWFlags) As Boolean
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function SetLayeredWindowAttributes(hWnd As IntPtr, crKey As Integer,
bAlpha As Byte, dwFlags As ULWFlags) As Boolean
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function ReleaseDC(hWnd As IntPtr, hDC As IntPtr) As Integer
End Function
<DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function CreateCompatibleDC(hDC As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function GetDC(hWnd As IntPtr) As IntPtr
End Function
<DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function DeleteDC(hdc As IntPtr) As Boolean
End Function
<DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function SelectObject(hDC As IntPtr, hObject As IntPtr) As IntPtr
End Function
<DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function DeleteObject(hObject As IntPtr) As Boolean
End Function
Public Shared Sub SelectBitmapToLayeredWindow(form As Form, bitmap As Bitmap, opacity As Integer)
If bitmap.PixelFormat <> PixelFormat.Format32bppArgb Then
Throw New ApplicationException("The bitmap must be 32bpp with alpha-channel.")
End If
Dim screenDc As IntPtr = GetDC(IntPtr.Zero)
Dim sourceDc As IntPtr = CreateCompatibleDC(screenDc)
Dim hBitmap As IntPtr = IntPtr.Zero
Dim hOldBitmap As IntPtr = IntPtr.Zero
Try
' Get handle to the New bitmap and select it into the current device context.
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0))
hOldBitmap = SelectObject(sourceDc, hBitmap)
Dim windowLocation As New POINT(form.Left, form.Top)
Dim windowSize As New SIZE(bitmap.Width, bitmap.Height)
Dim sourceLocation As New POINT(0, 0)
Dim blend As New BLENDFUNCTION() With {
.BlendOp = AC_SRC_OVER,
.BlendFlags = 0,
.SourceConstantAlpha = CType(opacity, Byte),
.AlphaFormat = AC_SRC_ALPHA
}
' Update the window.
' Handle => Handle to the layered window
' screenDc => Handle to the screen DC
' windowLocation => Screen position of the layered window
' windowSize => SIZE of the layered window
' sourceDc => Handle to the layered window surface DC
' sourceLocation => Location of the layer in the DC
' 0 => Color key of the layered window
' blend => Transparency of the layered window
' ULW_ALPHA => Use blend as the blend function
UpdateLayeredWindow(form.Handle, screenDc, windowLocation, windowSize,
sourceDc, sourceLocation, 0, blend, ULWFlags.ULW_ALPHA)
Finally
' Release device context.
ReleaseDC(IntPtr.Zero, screenDc)
If hBitmap <> IntPtr.Zero Then
SelectObject(sourceDc, hOldBitmap)
DeleteObject(hBitmap)
End If
DeleteDC(sourceDc)
End Try
End Sub
End Class
Можно загрузить Пример проекта с Google Диска.
Построен с. Net Framework 4.7.2 - любой другой Framework, 4.5.2+, будет делать