Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Borderless transparent window background support in apps using WinUI #1247

Open
hansmbakker opened this issue Aug 30, 2019 · 115 comments
Labels
area-AppWindow feature proposal New feature proposal team-CompInput Issue for IXP (Composition, Input) team wpf-vs-winui-mismatch

Comments

@hansmbakker
Copy link

Proposal: Transparent background support in apps using WinUI

Summary

Currently, WPF applications can have a transparent and borderless window while UWP applications cannot have that. Request is to support the creation of transparent borderless apps using WinUI.

Rationale

Scope

Capability Priority
This proposal will allow developers to accomplish transparent windows Must
This proposal will allow developers to accomplish borderless windows Must

Important Notes

This feature could enable designs as shown in this video made by @niels9001 (source tweet).

This is currently not possible, because:

  • to use acrylic, one needs to use UWP XAML
  • UWP XAML does not allow for transparent, borderless windows
  • WPF allows for transparent, borderless windows but when using UWP XAML inside WPF (via Xaml Islands) the rounded corners will become black

Open Questions

@hansmbakker
Copy link
Author

hansmbakker commented Aug 30, 2019

cc @marb2000 @niels9001

@marb2000
Copy link
Contributor

This is a very frequent scenario in Win32, We should consider this scenario at least for WinUI Desktop and WinUI XAML Islands.

@codendone codendone changed the title Proposal: Borderless transparent background support in apps using WinUI Proposal: Borderless transparent window background support in apps using WinUI Oct 23, 2019
@jevansaks jevansaks added the team-Rendering Issue for the Rendering team label Nov 7, 2019
@riverar
Copy link
Contributor

riverar commented Nov 27, 2019

Adding a data point, our app (EarTrumpet) requires this functionality.

@riverar
Copy link
Contributor

riverar commented Dec 5, 2019

Anything we can do to get this out of the freezer? https://github.com/microsoft/microsoft-ui-xaml/projects/4#card-25842420

I believe filling in functionality and API gaps should rank much higher. Frankly, I think it's critical to WinUI 3 XAML adoption.

@hansmbakker
Copy link
Author

@SavoySchuler @jevansaks ?

@shaheedmalik
Copy link

Those designs in the tweet look great.

@marb2000
Copy link
Contributor

@riverar this is WinUI 2 backlog, and for WinUI 2 is freezer. When WinUI 3 backlog will be active, we will unfreeze it again.

@riverar
Copy link
Contributor

riverar commented Jan 23, 2020

@marb2000 Thanks, it's very confusing how we all talk about WinUI 3 shipping soon yet the repository doesn't reflect WinUI 3 status very well. All we have are the community calls, tags, and word on the street right now.

@hansmbakker
Copy link
Author

Can you please give a status update on this?

@Alikont
Copy link

Alikont commented Jun 2, 2020

We'd also like to see transparent borderless windows in WinUI

We want to:

  1. Compose different windows of different processes on top of each other
  2. Apply custom styling to windows

In this picture you can see our product, which is a Windows 10 device with 3 running applications, 2 in yellow and one in red rectangles.

Right now it's achieved via hooking into native and undocumented places of ApplicationFrameHost.dll and Windows.UI.Xaml.dll, but we'd like to avoid that.

@Fedorov113
Copy link

This feature is needed for Windows Terminal. microsoft/terminal#603 Having transparent terminal is convenient and expected.

@jtbrower
Copy link

I became really excited once I realized that WinUI could be used in Desktop applications, though I figured I would run into an obstacle that made it impossible. It didn't take long to find the show stopper; this seems to at least one of them and I am sure there are more. @Alikont would you mind sharing the unpublished workaround you are using? Since the day UWP came out in 2012 I have wanted to migrate a boatload of WPF to a more performant XAML flavor. If it requires unpublished trickory I will do whatever it takes.

@jtbrower
Copy link

jtbrower commented Jul 3, 2020

For WinUI Desktop Apps

As simple as this looks, it wasn't as simple to figure out with the information available to us 7/3/2020. I tested this against both 32/64 bit platforms running WinUI Preview 1 with .Net 5.0 preview 6

Disclaimers

  • Few people write COM daily, that means I am no COM expert
  • If you are reading this and it is at or past the year 2021, then assure that this isn't built into the WinUI platform before you do this the hard way.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace WinUI_InDesktop
{
    public static class Interop
    {
        public static IntPtr SetWindow(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
        {
            return Environment.Is64BitProcess ?
                SetWindowLongPtr(hWnd, nIndex, dwNewLong) :
                SetWindowLong(hWnd, nIndex, dwNewLong);
        }

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

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

        //If you have WPF experience, this is similar to WindowStyle="None"
        public static void SetMainWindowBorderless()
        {
            const int GWL_STYLE = -16;

            //WS_VISIBLE | WS_POPUP
            var WS_VISIBLE_POPUP = new IntPtr(unchecked((int)0x90000000));

            using var process = Process.GetCurrentProcess();
            var success = WS_VISIBLE_POPUP != SetWindow(
                process.MainWindowHandle, 
                GWL_STYLE, 
                WS_VISIBLE_POPUP);

            //Helpful when your day goes south
            Debug.WriteLineIf(!success, new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()).Message);
        }
    }
}

@bpulliam bpulliam removed team-Reach Issue for the Reach team team-Rendering Issue for the Rendering team labels Aug 23, 2023
@microsoft-github-policy-service microsoft-github-policy-service bot added needs-triage Issue needs to be triaged by the area owners labels Aug 23, 2023
@tibitoth
Copy link

tibitoth commented Sep 1, 2023

Currently this is a blocker for my new client's project to use WinUI 3

@Pietro228
Copy link

Currently this is a blocker for my new client's project to use WinUI 3

@cnbluefire already sent here a solution for this problem and I have it working without any problems

@tibitoth
Copy link

tibitoth commented Sep 1, 2023

Fine, transparency works but click through not (or I don't know how to make it work)

Tried with a different workaround project and found this: castorix/WinUI3_SwapChainPanel_Layered#5

@Pietro228
Copy link

Pietro228 commented Sep 1, 2023

Fine, transparency works but click through not (or I don't know how to make it work)

Tried with a different workaround project and found this: castorix/WinUI3_SwapChainPanel_Layered#5

I have a working click through with topmost too, I can send you the source code tomorrow

@michalleptuch
Copy link

Fine, transparency works but click through not (or I don't know how to make it work)

Tried with a different workaround project and found this: castorix/WinUI3_SwapChainPanel_Layered#5

I have a working click through with topmost too, I can send you the source code tomorrow

And the last missing piece is working AcrylicBrush with HostBackground source applied to any control, not to whole window...

@Pietro228
Copy link

Fine, transparency works but click through not (or I don't know how to make it work)

Tried with a different workaround project and found this: castorix/WinUI3_SwapChainPanel_Layered#5

I have a working click through with topmost too, I can send you the source code tomorrow

And the last missing piece is working AcrylicBrush with HostBackground source applied to any control, not to whole window...

That's the last thing which I can't get to work :(

@tibitoth
Copy link

tibitoth commented Sep 1, 2023

@Pietro228

I have a working click through with topmost too, I can send you the source code tomorrow

Thank you! I'm looking forward to this!

@tibitoth
Copy link

tibitoth commented Sep 3, 2023

@Pietro228 Could you send me some guidance where should I start? Thank you very much!

@khoabui1412
Copy link

You could use CombineRgn and SetWndowRgn (set for intransparent region) for clickthrough workaround.

@castorix
Copy link

castorix commented Sep 3, 2023

I just added SendInput in the sample, to simulate a click (+ testing IsAlwaysOnTop)

@Pietro228
Copy link

Pietro228 commented Sep 3, 2023

@Pietro228 Could you send me some guidance where should I start? Thank you very much!

Hi, sorry, I didn't have time yesterday :(

For the transparency I used this project which @castorix sent here:
https://github.com/castorix/WinUI3_SwapChainPanel_Layered

Here's the code for unclickable window which works with @castorix 's project:

[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);
    }
}

@Pietro228
Copy link

Zaznam.2023-09-03.130725.mp4

@tibitoth
Copy link

tibitoth commented Sep 3, 2023

@Pietro228 That's really promising! When should I use the CaptureMouseClick method? I tried in PointerMoved but if I switch to false I cannot switch back to true because PointerMoved will not fire anymore. Also PointerPressed is too late I think because this window has already handled the event.

Edit: I see you make a switch for this settings.

@Pietro228
Copy link

Pietro228 commented Sep 3, 2023

@tibitoth Yes, I switch it in settings

@tibitoth
Copy link

tibitoth commented Sep 3, 2023

@Pietro228 @castorix I really appreciate your valuable help. One step is still missing for me. How can I create partially click through windows? WS_EX_TRANSPARENT make the whole window click through not just the fully transparent regions.

It's really frustrating (not because of you) that we need to deal with such a low level APIs. With WPF it would be 4 line of code:

<Window 
        ...
        Topmost="True"
        WindowStyle="None"
        AllowsTransparency="True"
        Background="Transparent">

@Pietro228
Copy link

@tibitoth Yeah in WPF it's easy, but I don't know how to do it partially because I don't really need this feature :/

@castorix
Copy link

castorix commented Sep 3, 2023

How can I create partially click through windows?

In the test sample, I have put a Butterfly which has "Click Through" and the other not (with IsHitTestVisible in XAML)

@luojunyuan
Copy link

In the test sample, I have put a Butterfly which has "Click Through" and the other not (with IsHitTestVisible in XAML)

Have you checked that on a tablet device. The touch input is not stable work with HitTest

@dotMorten
Copy link
Contributor

WinUIEx just released an update that makes this super easy using a custom backdrop: https://github.com/dotMorten/WinUIEx/releases/tag/v2.3
Doc: https://dotmorten.github.io/WinUIEx/concepts/CustomBackdrops.html#transparent-backdrop

@bpulliam bpulliam removed the needs-triage Issue needs to be triaged by the area owners label Oct 18, 2023
@AkazaRenn
Copy link

So it's been four years, has it gone out of the "we know it cannot do what WPF can but why don't you just use WPF" phase?

@Pietro228
Copy link

So it's been four years, has it gone out of the "we know it cannot do what WPF can but why don't you just use WPF" phase?

Read the previous comment

@AkazaRenn
Copy link

So it's been four years, has it gone out of the "we know it cannot do what WPF can but why don't you just use WPF" phase?

Read the previous comment

I don't understand what you mean. I'm looking for an official support, that's the purpose of creating a ticket here in microsoft-ui-xaml repo.

@AkazaRenn
Copy link

AkazaRenn commented Nov 16, 2023

Anyone wants to try the solution from PowerToys? Seems working on my window as a WindowEx subclass.
image

@sigmarsson
Copy link

For me beneath the call refusing to discard the border.

(Splash.AppWindow.Presenter as OverlappedPresenter).SetBorderAndTitleBar(false, false);

Is there any restriction as it must be invoked before/after a particular invocation ? Like as me , I emply this specific order;

            (Splash.AppWindow.Presenter as OverlappedPresenter).SetBorderAndTitleBar(false, false);

            Splash.SetWindowSize(100D, 100D);
            Splash.SetIsAlwaysOnTop(true);
            Splash.SetIsMaximizable(false);
            Splash.SetIsMinimizable(false);
            Splash.SetIsResizable(false);
            Splash.SetIsShownInSwitchers(false);
            Splash.SetForegroundWindow();
            Splash.CenterOnScreen();

            Splash.SystemBackdrop = new TransparentTintBackdrop();

            Splash.Activate();

@AlexanderBlackman
Copy link

AlexanderBlackman commented May 25, 2024

I've been playing around with other people's suggestions.

castorix - Your Swapchain solution works out of the box, though enabling _presenter.IsAlwaysOnTop prevents drag selecting content in below windows. Clicks still get through though.

Pietro228 I haven't managed to get your code to work, setting condition to true/false has no effect, clicks never pass through. That being said, I love the concept of it extending another window..

dotMorten - TransparentTintBackdrop works, though it doesn't let clicks pass through the window.

tibitoth - I feel your pain, WPF was so simple, it's a shame both Winui3 and Avalonia make this so hard to do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-AppWindow feature proposal New feature proposal team-CompInput Issue for IXP (Composition, Input) team wpf-vs-winui-mismatch
Projects
None yet
Development

No branches or pull requests