Open tibitoth opened 1 year ago
@castorix I've been very deep in the "WinUI 3 / WinAppSDK transparent click through Window" rabbit hole and I've just found your sample which is really great. Thank you! Only one thing not working right now which I cannot figure out why.
If you set the presenter's
IsAlwaysOnTop
property totrue
, click through will stop working.
The problem is that the window get focus back after SwitchToThisWindow because it is TopMost If I test with Explorer, it can keep focus with the following update, but of course the main window, TopMost, stays in foreground :
if (bOK && tsClickThrough.IsOn)
{
System.Threading.Thread.Sleep(100);
SwitchToThisWindow(hWnd, true);
System.Threading.Thread.Sleep(100);
}
It's not a perfect click through because the underlying windows does not get the mouse event.
It's not a perfect click through because the underlying windows does not get the mouse event.
Yes, I just set the focus You can add the mouse event with SendInput or mouse_event
It's not a perfect click through because the underlying windows does not get the mouse event.
I added SendInput (I let the test for TopMost in comments _//presenter.IsAlwaysOnTop = true; )
I got working click through using this code:
[DllImport("user32.dll")]
private static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
private static extern IntPtr IntSetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
private static extern Int32 IntSetWindowLong(IntPtr hWnd, int nIndex, Int32 dwNewLong);
private static int IntPtrToInt32(IntPtr intPtr)
{
return unchecked((int)intPtr.ToInt64());
}
[DllImport("kernel32.dll", EntryPoint = "SetLastError")]
public static extern void SetLastError(int dwErrorCode);
[Flags]
private enum ExtendedWindowStyles
{
// ...
WS_EX_TOOLWINDOW = 0x00000080,
WS_EX_TRANSPARENT = 32
// ...
}
private enum GetWindowLongFields
{
// ...
GWL_EXSTYLE = (-20),
// ...
}
private static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
int error = 0;
IntPtr result = IntPtr.Zero;
// Win32 SetWindowLong doesn't clear error on success
SetLastError(0);
if (IntPtr.Size == 4)
{
// use SetWindowLong
Int32 tempResult = IntSetWindowLong(hWnd, nIndex, IntPtrToInt32(dwNewLong));
error = Marshal.GetLastWin32Error();
result = new IntPtr(tempResult);
}
else
{
// use SetWindowLongPtr
result = IntSetWindowLongPtr(hWnd, nIndex, dwNewLong);
error = Marshal.GetLastWin32Error();
}
if ((result == IntPtr.Zero) && (error != 0))
{
throw new System.ComponentModel.Win32Exception(error);
}
return result;
}
// This changes clickability
public static void CaptureMouseClick(this Window window, bool condition)
{
try
{
var hwnd = WindowNative.GetWindowHandle(window);
if (condition)
{
int windowLong = (int)WindowInteropHelper.GetWindowLong(hwnd, (int)GetWindowLongFields.GWL_EXSTYLE);
WindowInteropHelper.SetWindowLong(hwnd, (int)GetWindowLongFields.GWL_EXSTYLE, (IntPtr)(windowLong & -33));
}
else
{
int windowLong = (int)WindowInteropHelper.GetWindowLong(hwnd, (int)GetWindowLongFields.GWL_EXSTYLE);
WindowInteropHelper.SetWindowLong(hwnd, (int)GetWindowLongFields.GWL_EXSTYLE, (IntPtr)(windowLong | (int)ExtendedWindowStyles.WS_EX_TRANSPARENT));
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
Hi @Pietro228, where do you put this function CaptureMouseClick(this Window window, bool condition)
? In WinUI3 I'm only aware of listening to events in a element of the page. Can this work on a FullscreenPresenter
?
The demo you did in the video is a private project? It would really help to see how it's done. Thanks!
Hey @Slals ! You can declare it anywhere you want. I suggest you to make a WindowExtensions.cs
file.
This project is private, but this shouldn't be that hard to implement
Thanks for your reply. Which window
is expected as parameter?
Actually I'm doing this using the code you provided
private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
try
{
var hwnd = WindowNative.GetWindowHandle(this);
Debug.WriteLine(hwnd);
if (this.AppState.IsRecording)
{
int windowLong = (int)GetWindowLong(hwnd, (-20));
SetWindowLong(hwnd, (-20), (IntPtr)(windowLong & -33));
}
else
{
int windowLong = (int)GetWindowLong(hwnd, (-20));
SetWindowLong(hwnd, (-20), (IntPtr)(windowLong | 32));
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
And the event is bind to
// MainWindow.xaml.cs
this.Content.PointerPressed += this.OnPointerPressed;
But doesn't seem to get expected results, the main window of the app is always capturing the mouse pressed. I dont have extensive experience in Windows dev, so I don't really get what does SetWindowLong(hwnd, (-20), (IntPtr)(windowLong & -33));
and the other
I got it working, it only works with the approach in this project using SwapChainPanel
, I was using one approach with DWM API. The solution provided by @castorix (https://github.com/castorix/WinUI3_SwapChainPanel_Layered/blob/master/MainWindow.xaml.cs#L721) is quite heavy because for each event it has to loop through all elements. Plus, since it needs overlapped presenter it creates a blink for each events (even without thread sleeping). It's caused by defocusing the main app Window and focusing it again thanks to AlwaysOnTop
.
I need to register all events (without capturing them) and send them through the window below the App window.
Your approach @Pietro228 is nice because it doesn't go through SendInput
creating this blinking issue. But there is no way - that I can think of - for registering all inputs (even pointer moves) and letting those events go to the window below the App window.
I created a topic about it here https://learn.microsoft.com/en-us/answers/questions/1418063/(winui3)-semi-transparent-window-click-through-win
@castorix I've been very deep in the "WinUI 3 / WinAppSDK transparent click through Window" rabbit hole and I've just found your sample which is really great. Thank you! Only one thing not working right now which I cannot figure out why.
If you set the presenter's
IsAlwaysOnTop
property totrue
, click through will stop working.