我想要类似这样的屏幕录制软件。
<Window x:Class="WpfTestApp.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:WpfTestApp"
mc:Ignorable="d"
ShowInTaskbar="False" WindowStyle="None" ResizeMode="NoResize"
AllowsTransparency="True"
UseLayoutRounding="True"
Opacity="1"
Cursor="ScrollAll"
Topmost="True"
WindowState="Maximized"
>
<Window.Background>
<SolidColorBrush Color="#01ffffff" Opacity="0" />
</Window.Background>
<Grid>
<Canvas x:Name="canvas1">
<Path Fill="#CC000000" Cursor="Cross" x:Name="backgroundPath">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,1440,810"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="300,200,800,300" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
</Grid>
Run Code Online (Sandbox Code Playgroud)
现在的问题是我无法使半透明区域backgroundPath
单击。我将其IsHitTestVisible
属性设置为false,但仍然没有更改。我曾经SetWindowLong
使整个窗口透明,这使我可以单击窗口,但是那样我的窗口及其中的所有控件均无法正常工作。
有人可以建议我如何实现这一目标吗?
我实际上对此很好奇,看起来确实没有一种“正确”或“官方”的方法可以仅在窗口而不是控件上实现透明度。
作为替代,我想出了一个功能有效的解决方案:
MainWindow XAML(我刚刚添加了一个按钮)
<Window x:Class="test.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:test"
mc:Ignorable="d"
Title="MainWindow"
WindowStyle="None"
AllowsTransparency="True"
ShowInTaskbar="False"
ResizeMode="NoResize"
UseLayoutRounding="True"
Opacity="1"
Cursor="ScrollAll"
Topmost="True"
WindowState="Maximized">
<Window.Background>
<SolidColorBrush Color="#01ffffff" Opacity="0" />
</Window.Background>
<Grid>
<Canvas x:Name="canvas1">
<Path Fill="#CC000000" Cursor="Cross" x:Name="backgroundPath">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,1440,810"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="300,200,800,300" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
<Button x:Name="My_Button" Width="100" Height="50" Background="White" IsHitTestVisible="True" HorizontalAlignment="Center" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
主窗口 C#
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Threading;
namespace test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
const int WS_EX_TRANSPARENT = 0x00000020;
const int GWL_EXSTYLE = (-20);
public const uint WS_EX_LAYERED = 0x00080000;
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
private bool _isClickThrough = true;
public MainWindow()
{
InitializeComponent();
// List of controls to make clickable. I'm just adding my button.
List<System.Windows.Controls.Control> controls = new List<System.Windows.Controls.Control>();
controls.Add(My_Button);
Thread globalMouseListener = new Thread(() =>
{
while (true)
{
Point p1 = GetMousePosition();
bool mouseInControl = false;
for (int i = 0; i < controls.Count; i++)
{
Point p2 = new Point();
Rect r = new Rect();
System.Windows.Controls.Control iControl = controls[i];
Dispatcher.BeginInvoke(new Action(() =>
{
// Get control position relative to window
p2 = iControl.TransformToAncestor(this).Transform(new Point(0, 0));
// Add window position to get global control position
r.X = p2.X + this.Left;
r.Y = p2.Y + this.Top;
// Set control width/height
r.Width = iControl.Width;
r.Height = iControl.Height;
if (r.Contains(p1))
{
mouseInControl = true;
}
if (mouseInControl && _isClickThrough)
{
_isClickThrough = false;
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExNotTransparent(hwnd);
}
else if (!mouseInControl && !_isClickThrough)
{
_isClickThrough = true;
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExTransparent(hwnd);
}
}));
}
Thread.Sleep(15);
}
});
globalMouseListener.Start();
}
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
public static void SetWindowExTransparent(IntPtr hwnd)
{
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
}
public static void SetWindowExNotTransparent(IntPtr hwnd)
{
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle & ~WS_EX_TRANSPARENT);
}
private void Button_Click(object sender, EventArgs e)
{
System.Windows.Forms.MessageBox.Show("hey it worked");
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExTransparent(hwnd);
}
}
}
Run Code Online (Sandbox Code Playgroud)
基本上,如果鼠标位于控件上方,我会调用SetWindowExNotTransparent
将其转换为正常的非点击窗口。如果鼠标不在控件上,则会使用 将其切换回点击状态SetWindowExTransparent
。
我有一个正在运行的线程,它不断检查全局鼠标位置与全局控件位置(您在其中填写您希望能够单击的控件列表)。全局控制位置是通过获取相对于 的控制位置MainWindow
然后添加Top
和Left
属性来确定的MainWindow
。
当然,这是一个有点“hacky”的解决方案。但如果你能找到更好的,我就该死了!它似乎对我来说工作得很好。(尽管处理奇怪形状的控件可能会很奇怪。此代码仅处理矩形控件。)
另外,我只是很快地将其组合在一起,看看它是否有效,所以它不是很干净。如果你愿意的话,可以做一个概念证明。