Open dotMorten opened 3 months ago
You can do something like (GetByVisual returns null on my Windows 10 OS) :
Microsoft.UI.Composition.Visual visual = Microsoft.UI.Xaml.Hosting.ElementCompositionPreview.GetElementVisual(myButton);
var ci = Microsoft.UI.Content.ContentIsland.FindAllForCompositor(visual.Compositor);
//var ci = Microsoft.UI.Content.ContentIsland.GetByVisual(visual);
if (ci[0] != null)
{
IntPtr hWndHost = Win32Interop.GetWindowFromWindowId(ci[0].Environment.AppWindowId);
}
XamlRoot.ContentIslandEnvironment.AppWindowId can be also be used here. However, I think functionality should be added to get the XAML window from a UIElement.
XamlRoot.ContentIslandEnvironment.AppWindowId can be also be used here. However, I think functionality should be added to get the XAML window from a UIElement.
excelent, is there any limitation or Are there any special situation that lead to a null result? i want to use it in my class library
also there is another way if there is no uielement!
public static AppWindow GetCurrentAppWindow()
{
var tops = GetProcessWindowList();
var firstWinUI3 = tops.FirstOrDefault(w => w.ClassName == "WinUIDesktopWin32WindowClass");
var windowId = Win32Interop.GetWindowIdFromWindow(firstWinUI3.Handle);
return AppWindow.GetFromWindowId(windowId);
}
public static IReadOnlyList<Win32Window> GetProcessWindowList()
{
var process = Process.GetCurrentProcess();
var list = new List<Win32Window>();
NativeMethods.EnumWindows((h, l) =>
{
var window = new Win32Window(h);
if (window.ProcessId == process.Id)
{
list.Add(window);
}
return true;
}, IntPtr.Zero);
return list.AsReadOnly();
}
public class Win32Window
{
public Win32Window(IntPtr handle)
{
Handle = handle;
ThreadId = NativeMethods.GetWindowThreadProcessId(handle, out var processId);
ProcessId = processId;
}
public IntPtr Handle { get; }
public int ThreadId { get; }
public int ProcessId { get; }
public string ClassName => WindowHelper.GetClassName(Handle);
public string Text => WindowHelper.GetWindowText(Handle);
public bool IsEnabled => NativeMethods.IsWindowEnabled(Handle);
public override string ToString()
{
var s = ClassName;
var text = Text;
if (text != null)
{
s += " '" + text + "'";
}
return s;
}
}
public static string GetWindowText(IntPtr hwnd)
{
var sb = new StringBuilder(1024);
NativeMethods.GetWindowText(hwnd, sb, sb.Capacity - 1);
return sb.ToString();
}
public static string GetClassName(IntPtr hwnd)
{
var sb = new StringBuilder(256);
NativeMethods.GetClassName(hwnd, sb, sb.Capacity - 1);
return sb.ToString();
}
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport(ExternDll.User32, SetLastError = true)]
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport(ExternDll.User32)]
public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
[DllImport(ExternDll.User32)]
public static extern bool IsWindowEnabled(IntPtr hwnd);
[DllImport(ExternDll.User32, CharSet = CharSet.Unicode)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport(ExternDll.User32, CharSet = CharSet.Unicode)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
Proposal: Add ability to get hosting Window from XamlRoot or UIElement
Summary
Several APIs requires access to the Window handle, but it's currently impossible to get the window that is hosting a visual element. We need an API to make it easier to get the handle.
Rationale
In order to use various pickers like OpenFilePicker, SaveFilePicker etc, you need to first get the Window handle before you can use them as described here.
The approach discussed here for getting that handle requires you to create a MainWindow property on your Application instance in order to do that.
This presents some problems:
One example would be a 3rd party control that includes a save or open button. These buttons would not be able to load or save to/from files, since they have no way of using the file pickers and make them modal to the current window (current as "in the context of that button").
Scope
'Must' implies that the feature should not ship without this capability.