Skip to content

Commit 10ca3c9

Browse files
ryalanmsrjmurillo
authored andcommitted
API updates #2
1 parent 7a6d3f4 commit 10ca3c9

File tree

8 files changed

+156
-90
lines changed

8 files changed

+156
-90
lines changed

Microsoft.Toolkit.Win32/Microsoft.Toolkit.Forms.UI.XamlHost/Interop/Win32/NativeDefines.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,9 @@ internal static partial class NativeDefines
2424
public static IntPtr HWND_TOP { get; } = IntPtr.Zero;
2525

2626
public static IntPtr HWND_TOPMOST { get; } = IntPtr.Zero - 1;
27+
28+
public const int GWL_STYLE = -16;
29+
30+
public const uint WS_EX_CONTROLPARENT = 0x00010000;
2731
}
2832
}

Microsoft.Toolkit.Win32/Microsoft.Toolkit.Forms.UI.XamlHost/Interop/Win32/UnsafeNativeMethods.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,16 @@ internal static partial class UnsafeNativeMethods
3939
[SecurityCritical]
4040
[DllImport(ExternDll.User32, EntryPoint = "EnableWindow", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
4141
public static extern bool IntEnableWindow(HandleRef hWnd, bool enable);
42+
43+
/// <summary>
44+
/// Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
45+
/// </summary>
46+
/// <param name="hWnd">Target window</param>
47+
/// <param name="nIndex">Zero-based offset</param>
48+
/// <param name="dwNewLong">The replacement value</param>
49+
/// <returns>A positive integer indicates success; zero indicates failure</returns>
50+
[SecurityCritical]
51+
[DllImport("user32.dll", SetLastError = true)]
52+
internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
4253
}
4354
}

Microsoft.Toolkit.Win32/Microsoft.Toolkit.Forms.UI.XamlHost/WindowsXamlHostBase.KeyboardFocus.cs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Windows.Forms;
67
using Microsoft.Toolkit.Forms.UI.XamlHost.Interop.Win32;
78

89
namespace Microsoft.Toolkit.Forms.UI.XamlHost
@@ -48,41 +49,40 @@ protected override void Select(bool directed, bool forward)
4849
base.Select(directed, true);
4950
}
5051

52+
protected override void OnGotFocus(EventArgs e)
53+
{
54+
base.OnGotFocus(e);
55+
}
56+
57+
protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
58+
{
59+
base.OnPreviewKeyDown(e);
60+
}
61+
5162
/// <summary>
52-
/// Processes a command key, ensuring that Xaml has an opportunity
63+
/// Processes a tab key, ensuring that Xaml has an opportunity
5364
/// to handle the command before normal Windows Forms processing.
5465
/// (Xaml must be notified of keys that invoke focus navigation.)
5566
/// </summary>
5667
/// <returns>true if the command was processed</returns>
57-
protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData)
68+
protected override bool ProcessTabKey(bool forward)
5869
{
5970
if (DesignMode)
6071
{
6172
return false;
6273
}
6374

64-
Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationReason? xamlSourceFocusNavigationReason;
65-
switch (keyData)
66-
{
67-
// BUGBUG: Bug 18356717: DesktopWindowXamlSource.NavigateFocus non-directional Focus not
68-
// moving Focus, not responding to keyboard input. Until then, use Next/Previous only.
69-
case System.Windows.Forms.Keys.Tab | System.Windows.Forms.Keys.Shift:
70-
xamlSourceFocusNavigationReason = Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationReason.Left;
71-
break;
72-
case System.Windows.Forms.Keys.Tab:
73-
xamlSourceFocusNavigationReason = Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationReason.Right;
74-
break;
75+
Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationReason? xamlSourceFocusNavigationReason = null;
7576

76-
default:
77-
xamlSourceFocusNavigationReason = null;
78-
break;
77+
// BUGBUG: Bug 18356717: DesktopWindowXamlSource.NavigateFocus non-directional Focus not
78+
// moving Focus, not responding to keyboard input. Until then, use Next/Previous only.
79+
if (forward == true)
80+
{
81+
xamlSourceFocusNavigationReason = Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationReason.Right;
7982
}
80-
81-
// The key send to this Control instance is not one of the navigation keys handled
82-
// by global::Windows.UI.Xaml. Allow the base class to handle the key press.
83-
if (xamlSourceFocusNavigationReason == null)
83+
else
8484
{
85-
return base.ProcessCmdKey(ref msg, keyData);
85+
xamlSourceFocusNavigationReason = Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationReason.Left;
8686
}
8787

8888
// Determine if the currently focused element is the last element for the requested
@@ -99,7 +99,7 @@ protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, Syst
9999
return focusResult.WasFocusMoved;
100100
}
101101

102-
return false;
102+
return base.ProcessTabKey(forward);
103103
}
104104
}
105105
}

Microsoft.Toolkit.Win32/Microsoft.Toolkit.Forms.UI.XamlHost/WindowsXamlHostBase.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,15 @@ public abstract partial class WindowsXamlHostBase : ContainerControl
6363
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
6464
public WindowsXamlHostBase()
6565
{
66+
SetStyle(ControlStyles.ContainerControl, true);
6667
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
6768
SetStyle(ControlStyles.UserPaint, true);
6869
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
6970

71+
// Must be a container control with TabStop == false to allow nested UWP XAML Focus
72+
// BUGBUG: Uncomment when nested Focus is available
73+
// TabStop = false;
74+
7075
// Respond to size changes on this Control
7176
SizeChanged += OnWindowXamlHostSizeChanged;
7277

@@ -160,6 +165,12 @@ protected override void OnHandleCreated(EventArgs e)
160165
var desktopWindowXamlSourceNative = xamlSource.GetInterop();
161166
desktopWindowXamlSourceNative.AttachToWindow(Handle);
162167
_xamlIslandWindowHandle = desktopWindowXamlSourceNative.WindowHandle;
168+
169+
// Set window style required by container control to support Focus
170+
if (Interop.Win32.UnsafeNativeMethods.SetWindowLong(Handle, Interop.Win32.NativeDefines.GWL_STYLE, Interop.Win32.NativeDefines.WS_EX_CONTROLPARENT) == 0)
171+
{
172+
throw new InvalidOperationException("WindowsXamlHostBase::OnHandleCreated: Failed to set WS_EX_CONTROLPARENT on control window.");
173+
}
163174
}
164175

165176
base.OnHandleCreated(e);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Reflection;
8+
9+
namespace Microsoft.Toolkit.Win32.UI.XamlHost
10+
{
11+
/// <summary>
12+
/// MetadataProviderDiscovery has the responsibility of loading all other metadata providers for custom UWP XAML
13+
/// types. In this implementation, reflection is used at runtime to probe for metadata providers in
14+
/// the working directory, allowing any type that includes metadata (compiled in to a .NET framework
15+
/// assembly) to be used without explicit metadata handling by the developer.
16+
/// </summary>
17+
internal static class MetadataProviderDiscovery
18+
{
19+
/// <summary>
20+
/// Probes working directory for all available metadata providers
21+
/// </summary>
22+
/// <param name="filteredTypes">Types to ignore</param>
23+
/// <returns>List of UWP XAML metadata providers</returns>
24+
internal static List<Windows.UI.Xaml.Markup.IXamlMetadataProvider> DiscoverMetadataProviders(List<Type> filteredTypes)
25+
{
26+
// List of discovered UWP XAML metadata providers
27+
List<Windows.UI.Xaml.Markup.IXamlMetadataProvider> metadataProviders = null;
28+
metadataProviders = new List<Windows.UI.Xaml.Markup.IXamlMetadataProvider>();
29+
30+
// Reflection-based runtime metadata probing
31+
var currentDirectory = System.IO.Directory.GetCurrentDirectory();
32+
var exes = System.IO.Directory.GetFiles(currentDirectory, "*.exe");
33+
var dlls = System.IO.Directory.GetFiles(currentDirectory, "*.dll");
34+
35+
var files = new string[exes.Length + dlls.Length];
36+
Array.Copy(exes, files, exes.Length);
37+
Array.Copy(dlls, 0, files, exes.Length, dlls.Length);
38+
39+
foreach (var file in files)
40+
{
41+
try
42+
{
43+
var assembly = Assembly.LoadFrom(file);
44+
45+
LoadTypesFromAssembly(assembly, ref metadataProviders, ref filteredTypes);
46+
}
47+
catch (System.IO.FileLoadException)
48+
{
49+
// These exceptions are expected
50+
}
51+
}
52+
53+
// Load any types from this assembly
54+
LoadTypesFromAssembly(Assembly.GetExecutingAssembly(), ref metadataProviders, ref filteredTypes);
55+
56+
return metadataProviders;
57+
}
58+
59+
/// <summary>
60+
/// Loads all types from the specified assembly and caches metadata providers
61+
/// </summary>
62+
/// <param name="assembly">Target assembly to load types from</param>
63+
/// <param name="metadataProviders">List of metadata providers</param>
64+
/// <param name="filteredTypes">List of types to ignore</param>
65+
private static void LoadTypesFromAssembly(Assembly assembly, ref List<Windows.UI.Xaml.Markup.IXamlMetadataProvider> metadataProviders, ref List<Type> filteredTypes)
66+
{
67+
// Load types inside the executing assembly
68+
Type thisType = typeof(Windows.UI.Xaml.Application);
69+
70+
foreach (var type in assembly.GetTypes())
71+
{
72+
if (filteredTypes.Contains(type))
73+
{
74+
continue;
75+
}
76+
77+
if (typeof(Windows.UI.Xaml.Markup.IXamlMetadataProvider).IsAssignableFrom(type))
78+
{
79+
var provider = (Windows.UI.Xaml.Markup.IXamlMetadataProvider)Activator.CreateInstance(type);
80+
metadataProviders.Add(provider);
81+
}
82+
}
83+
}
84+
}
85+
}

Microsoft.Toolkit.Win32/Microsoft.Toolkit.Win32.UI.XamlHost/XamlApplication.cs

Lines changed: 16 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -19,70 +19,7 @@ namespace Microsoft.Toolkit.Win32.UI.XamlHost
1919
internal class XamlApplication : Windows.UI.Xaml.Application, Windows.UI.Xaml.Markup.IXamlMetadataProvider
2020
{
2121
// Metadata provider identified by the root metadata provider
22-
private List<Windows.UI.Xaml.Markup.IXamlMetadataProvider> _metadataProviders;
23-
24-
/// <summary>
25-
/// Loads all types from the specified assembly and caches metadata providers
26-
/// </summary>
27-
/// <param name="assembly">Target assembly to load types from</param>
28-
private void LoadTypesFromAssembly(Assembly assembly)
29-
{
30-
// Load types inside the executing assembly
31-
var thisType = GetType();
32-
33-
foreach (var type in assembly.GetTypes())
34-
{
35-
if (type == thisType)
36-
{
37-
continue;
38-
}
39-
40-
if (typeof(Windows.UI.Xaml.Markup.IXamlMetadataProvider).IsAssignableFrom(type))
41-
{
42-
var provider = (Windows.UI.Xaml.Markup.IXamlMetadataProvider)Activator.CreateInstance(type);
43-
_metadataProviders.Add(provider);
44-
}
45-
}
46-
}
47-
48-
/// <summary>
49-
/// Probes working directory for all available metadata providers
50-
/// </summary>
51-
private void EnsureMetadataProviders()
52-
{
53-
if (_metadataProviders != null)
54-
{
55-
return;
56-
}
57-
58-
_metadataProviders = new List<Windows.UI.Xaml.Markup.IXamlMetadataProvider>();
59-
60-
// Reflection-based runtime metadata probing
61-
var currentDirectory = System.IO.Directory.GetCurrentDirectory();
62-
var exes = System.IO.Directory.GetFiles(currentDirectory, "*.exe");
63-
var dlls = System.IO.Directory.GetFiles(currentDirectory, "*.dll");
64-
65-
var files = new string[exes.Length + dlls.Length];
66-
Array.Copy(exes, files, exes.Length);
67-
Array.Copy(dlls, 0, files, exes.Length, dlls.Length);
68-
69-
foreach (var file in files)
70-
{
71-
try
72-
{
73-
var assembly = Assembly.Load(file);
74-
75-
LoadTypesFromAssembly(assembly);
76-
}
77-
catch
78-
{
79-
// XamlApplication::EnsureMetadataProviders: Non-.NET assembly. Expected.
80-
}
81-
}
82-
83-
// Load any types from this assembly
84-
LoadTypesFromAssembly(Assembly.GetExecutingAssembly());
85-
}
22+
private List<Windows.UI.Xaml.Markup.IXamlMetadataProvider> _metadataProviders = null;
8623

8724
/// <summary>
8825
/// Gets XAML IXamlType interface from all cached metadata providers by Type
@@ -92,6 +29,7 @@ private void EnsureMetadataProviders()
9229
public Windows.UI.Xaml.Markup.IXamlType GetXamlType(Type type)
9330
{
9431
EnsureMetadataProviders();
32+
9533
foreach (var provider in _metadataProviders)
9634
{
9735
var result = provider.GetXamlType(type);
@@ -112,6 +50,7 @@ public Windows.UI.Xaml.Markup.IXamlType GetXamlType(Type type)
11250
public Windows.UI.Xaml.Markup.IXamlType GetXamlType(string fullName)
11351
{
11452
EnsureMetadataProviders();
53+
11554
foreach (var provider in _metadataProviders)
11655
{
11756
var result = provider.GetXamlType(fullName);
@@ -131,6 +70,7 @@ public Windows.UI.Xaml.Markup.IXamlType GetXamlType(string fullName)
13170
public Windows.UI.Xaml.Markup.XmlnsDefinition[] GetXmlnsDefinitions()
13271
{
13372
EnsureMetadataProviders();
73+
13474
var definitions = new List<Windows.UI.Xaml.Markup.XmlnsDefinition>();
13575
foreach (var provider in _metadataProviders)
13676
{
@@ -140,6 +80,17 @@ public Windows.UI.Xaml.Markup.XmlnsDefinition[] GetXmlnsDefinitions()
14080
return definitions.ToArray();
14181
}
14282

83+
/// <summary>
84+
/// Probes file system for UWP XAML metadata providers
85+
/// </summary>
86+
private void EnsureMetadataProviders()
87+
{
88+
if (_metadataProviders == null)
89+
{
90+
_metadataProviders = MetadataProviderDiscovery.DiscoverMetadataProviders(new List<Type> { typeof(XamlApplication) });
91+
}
92+
}
93+
14394
/// <summary>
14495
/// Gets and returns the current UWP XAML Application instance in a reference parameter.
14596
/// If the current XAML Application instance has not been created for the process (is null),
@@ -149,7 +100,7 @@ internal static void GetOrCreateXamlApplicationInstance(ref Windows.UI.Xaml.Appl
149100
{
150101
// Instantiation of the application object must occur before creating the DesktopWindowXamlSource instance.
151102
// DesktopWindowXamlSource will create a generic Application object unable to load custom UWP XAML metadata.
152-
if (application != null)
103+
if (application == null)
153104
{
154105
try
155106
{

Microsoft.Toolkit.Win32/Microsoft.Toolkit.Wpf.UI.XamlHost/WindowsXamlHost.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,12 @@ protected override void Dispose(bool disposing)
7575
{
7676
if (disposing && !IsDisposed)
7777
{
78-
base.Dispose(disposing);
7978
if (Child is Windows.UI.Xaml.FrameworkElement frameworkElement)
8079
{
8180
frameworkElement.SizeChanged -= XamlContentSizeChanged;
8281
}
82+
83+
base.Dispose(disposing);
8384
}
8485
}
8586
}

Microsoft.Toolkit.Win32/Microsoft.Toolkit.Wpf.UI.XamlHost/WindowsXamlHostBase.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public Windows.UI.Xaml.UIElement ChildInternal
7575
{
7676
get
7777
{
78-
return xamlSource.Content;
78+
return xamlSource?.Content;
7979
}
8080

8181
set
@@ -168,10 +168,13 @@ protected override void Dispose(bool disposing)
168168
if (disposing && !IsDisposed)
169169
{
170170
IsDisposed = true;
171+
171172
xamlSource.TakeFocusRequested -= OnTakeFocusRequested;
172173
ChildInternal = null;
173-
xamlSource?.Dispose();
174+
174175
_windowsXamlManager?.Dispose();
176+
177+
// xamlSource.Dispose();
175178
}
176179
}
177180
}

0 commit comments

Comments
 (0)