Skip to content

Commit 011d2b9

Browse files
committed
Introduce AppBuilder.UseHotReload()
1 parent adb9aca commit 011d2b9

File tree

6 files changed

+328
-208
lines changed

6 files changed

+328
-208
lines changed

samples/HotReloadDemo/App.axaml.cs

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
using Avalonia;
22
using Avalonia.Controls.ApplicationLifetimes;
33
using Avalonia.Markup.Xaml;
4-
using HotAvalonia;
54
using HotReloadDemo.ViewModels;
65
using HotReloadDemo.Views;
76

87
namespace HotReloadDemo;
98

109
public partial class App : Application
1110
{
12-
public override void Initialize()
13-
{
14-
this.EnableHotReload();
15-
AvaloniaXamlLoader.Load(this);
16-
}
11+
public override void Initialize() => AvaloniaXamlLoader.Load(this);
1712

1813
public override void OnFrameworkInitializationCompleted()
1914
{

samples/HotReloadDemo/Program.cs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Avalonia;
22
using Avalonia.ReactiveUI;
3+
using HotAvalonia;
34

45
namespace HotReloadDemo;
56

@@ -14,5 +15,6 @@ public static AppBuilder BuildAvaloniaApp()
1415
.UsePlatformDetect()
1516
.WithInterFont()
1617
.LogToTrace()
18+
.UseHotReload()
1719
.UseReactiveUI();
1820
}

src/HotAvalonia.Extensions/AvaloniaHotReloadExtensions.cs

+87-87
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@
3434

3535
namespace HotAvalonia
3636
{
37+
using global::System;
38+
using global::System.Diagnostics;
39+
using global::System.Diagnostics.CodeAnalysis;
40+
using global::System.IO;
41+
using global::System.Reflection;
42+
using global::System.Runtime.CompilerServices;
43+
using global::Avalonia;
44+
3745
/// <summary>
3846
/// Indicates that the decorated method should be called whenever the associated Avalonia control is hot reloaded.
3947
/// </summary>
@@ -60,56 +68,75 @@ namespace HotAvalonia
6068
/// }
6169
/// </code>
6270
/// </remarks>
63-
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
64-
[global::System.Diagnostics.Conditional("ENABLE_XAML_HOT_RELOAD")]
65-
[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
66-
internal sealed class AvaloniaHotReloadAttribute : global::System.Attribute
71+
[ExcludeFromCodeCoverage]
72+
[Conditional("ENABLE_XAML_HOT_RELOAD")]
73+
[AttributeUsage(AttributeTargets.Method)]
74+
internal sealed class AvaloniaHotReloadAttribute : Attribute
6775
{
6876
}
69-
}
70-
71-
#if ENABLE_XAML_HOT_RELOAD && !DISABLE_XAML_HOT_RELOAD
72-
namespace HotAvalonia
73-
{
74-
using global::System;
75-
using global::System.Diagnostics;
76-
using global::System.Diagnostics.CodeAnalysis;
77-
using global::System.IO;
78-
using global::System.Reflection;
79-
using global::System.Runtime.CompilerServices;
80-
using global::Avalonia;
8177

8278
/// <summary>
8379
/// Provides extension methods for enabling and disabling hot reload functionality for Avalonia applications.
8480
/// </summary>
8581
[ExcludeFromCodeCoverage]
8682
internal static class AvaloniaHotReloadExtensions
8783
{
84+
#if ENABLE_XAML_HOT_RELOAD && !DISABLE_XAML_HOT_RELOAD
85+
/// <summary>
86+
/// Creates a factory method for generating an <see cref="IHotReloadContext"/>
87+
/// using the specified control type and its XAML file path.
88+
/// </summary>
89+
/// <param name="controlType">The control type.</param>
90+
/// <param name="controlFilePath">The file path to the associated XAML file.</param>
91+
/// <returns>A factory method for creating an <see cref="IHotReloadContext"/> instance.</returns>
92+
[DebuggerStepThrough]
93+
private static Func<IHotReloadContext> CreateHotReloadContextFactory(Type controlType, string? controlFilePath)
94+
{
95+
return new Func<IHotReloadContext>(() =>
96+
{
97+
if (!string.IsNullOrEmpty(controlFilePath) && !File.Exists(controlFilePath))
98+
throw new FileNotFoundException("The corresponding XAML file could not be found.", controlFilePath);
99+
100+
AvaloniaProjectLocator projectLocator = CreateAvaloniaProjectLocator();
101+
if (!string.IsNullOrEmpty(controlFilePath))
102+
projectLocator.AddHint(controlType, controlFilePath);
103+
104+
return CreateHotReloadContext(projectLocator);
105+
});
106+
}
107+
88108
/// <summary>
89-
/// A mapping between Avalonia <see cref="Application"/> instances and their associated hot reload context.
109+
/// Creates a factory method for generating an <see cref="IHotReloadContext"/>
110+
/// using a custom project path resolver.
90111
/// </summary>
91-
private static readonly ConditionalWeakTable<Application, IHotReloadContext> s_apps =
92-
new ConditionalWeakTable<Application, IHotReloadContext>();
112+
/// <param name="projectPathResolver">The callback function capable of resolving a project path for a given assembly.</param>
113+
/// <returns>A factory method for creating an <see cref="IHotReloadContext"/> instance.</returns>
114+
[DebuggerStepThrough]
115+
private static Func<IHotReloadContext> CreateHotReloadContextFactory(Func<Assembly, string?>? projectPathResolver)
116+
{
117+
return new Func<IHotReloadContext>(() =>
118+
{
119+
AvaloniaProjectLocator projectLocator = CreateAvaloniaProjectLocator();
120+
if ((object?)projectPathResolver != null)
121+
projectLocator.AddHint(projectPathResolver);
122+
123+
return CreateHotReloadContext(projectLocator);
124+
});
125+
}
93126

94127
/// <summary>
95-
/// Enables hot reload functionality for the given Avalonia application.
128+
/// Creates a hot reload context for the current environment.
96129
/// </summary>
97-
/// <param name="app">The Avalonia application instance for which hot reload should be enabled.</param>
98130
/// <param name="projectLocator">The project locator used to find source directories of assemblies.</param>
131+
/// <returns>A hot reload context for the current environment.</returns>
99132
[DebuggerStepThrough]
100-
private static void EnableHotReload(Application app, AvaloniaProjectLocator projectLocator)
133+
private static IHotReloadContext CreateHotReloadContext(AvaloniaProjectLocator projectLocator)
101134
{
102-
if (!s_apps.TryGetValue(app, out IHotReloadContext? context))
103-
{
104135
#if ENABLE_LITE_XAML_HOT_RELOAD
105-
context = AvaloniaHotReloadContext.CreateLite(projectLocator);
136+
return AvaloniaHotReloadContext.CreateLite(projectLocator);
106137
#else
107-
context = AvaloniaHotReloadContext.Create(projectLocator);
138+
return AvaloniaHotReloadContext.Create(projectLocator);
108139
#endif
109-
s_apps.Add(app, context);
110-
}
111-
112-
context.EnableHotReload();
113140
}
114141

115142
/// <summary>
@@ -121,106 +148,79 @@ private static AvaloniaProjectLocator CreateAvaloniaProjectLocator()
121148
{
122149
return new AvaloniaProjectLocator();
123150
}
151+
#endif
124152

125153
/// <summary>
126-
/// Enables hot reload functionality for the given Avalonia application.
154+
/// Enables hot reload functionality for the specified <see cref="AppBuilder"/> instance.
127155
/// </summary>
128-
/// <param name="app">The Avalonia application instance for which hot reload should be enabled.</param>
129-
/// <param name="appFilePath">The file path of the application's main source file. Optional if the method called within the file of interest.</param>
156+
/// <param name="builder">The app builder instance.</param>
157+
/// <returns>The app builder instance.</returns>
130158
[DebuggerStepThrough]
131-
public static void EnableHotReload(this Application app, [CallerFilePath] string? appFilePath = null)
159+
public static AppBuilder UseHotReload(this AppBuilder builder)
132160
{
133-
_ = app ?? throw new ArgumentNullException(nameof(app));
134-
135-
if (!string.IsNullOrEmpty(appFilePath) && !File.Exists(appFilePath))
136-
throw new FileNotFoundException("The corresponding XAML file could not be found.", appFilePath);
137-
138-
AvaloniaProjectLocator projectLocator = CreateAvaloniaProjectLocator();
139-
if (!string.IsNullOrEmpty(appFilePath))
140-
projectLocator.AddHint(app.GetType(), appFilePath);
141-
142-
EnableHotReload(app, projectLocator);
161+
#if ENABLE_XAML_HOT_RELOAD && !DISABLE_XAML_HOT_RELOAD
162+
AvaloniaHotReload.Enable(builder, CreateHotReloadContextFactory(null));
163+
#endif
164+
return builder;
143165
}
144166

145167
/// <summary>
146-
/// Enables hot reload functionality for the given Avalonia application.
168+
/// Enables hot reload functionality for the specified <see cref="AppBuilder"/> instance.
147169
/// </summary>
148-
/// <param name="app">The Avalonia application instance for which hot reload should be enabled.</param>
170+
/// <param name="builder">The app builder instance.</param>
149171
/// <param name="projectPathResolver">The callback function capable of resolving a project path for a given assembly.</param>
172+
/// <returns>The app builder instance.</returns>
150173
[DebuggerStepThrough]
151-
public static void EnableHotReload(this Application app, Func<Assembly, string?> projectPathResolver)
174+
public static AppBuilder UseHotReload(this AppBuilder builder, Func<Assembly, string?> projectPathResolver)
152175
{
153-
_ = app ?? throw new ArgumentNullException(nameof(app));
154-
155-
AvaloniaProjectLocator projectLocator = CreateAvaloniaProjectLocator();
156-
projectLocator.AddHint(projectPathResolver);
157-
158-
EnableHotReload(app, projectLocator);
159-
}
160-
161-
/// <summary>
162-
/// Disables hot reload functionality for the given Avalonia application.
163-
/// </summary>
164-
/// <param name="app">The Avalonia application instance for which hot reload should be disabled.</param>
165-
[DebuggerStepThrough]
166-
public static void DisableHotReload(this Application app)
167-
{
168-
_ = app ?? throw new ArgumentNullException(nameof(app));
169-
170-
if (s_apps.TryGetValue(app, out IHotReloadContext? context))
171-
context.DisableHotReload();
176+
#if ENABLE_XAML_HOT_RELOAD && !DISABLE_XAML_HOT_RELOAD
177+
AvaloniaHotReload.Enable(builder, CreateHotReloadContextFactory(projectPathResolver));
178+
#endif
179+
return builder;
172180
}
173-
}
174-
}
175-
#else
176-
namespace HotAvalonia
177-
{
178-
using global::System;
179-
using global::System.Diagnostics;
180-
using global::System.Diagnostics.CodeAnalysis;
181-
using global::System.Reflection;
182-
using global::Avalonia;
183181

184-
/// <summary>
185-
/// Provides extension methods for enabling and disabling hot reload functionality for Avalonia applications.
186-
/// </summary>
187-
[ExcludeFromCodeCoverage]
188-
internal static class AvaloniaHotReloadExtensions
189-
{
190182
/// <summary>
191183
/// Enables hot reload functionality for the given Avalonia application.
192184
/// </summary>
193185
/// <param name="app">The Avalonia application instance for which hot reload should be enabled.</param>
194186
/// <param name="appFilePath">The file path of the application's main source file. Optional if the method called within the file of interest.</param>
195-
[Conditional("DEBUG")]
187+
[Conditional("ENABLE_XAML_HOT_RELOAD")]
196188
[DebuggerStepThrough]
197-
public static void EnableHotReload(this Application app, string? appFilePath = null)
189+
public static void EnableHotReload(this Application app, [CallerFilePath] string? appFilePath = null)
198190
{
191+
#if ENABLE_XAML_HOT_RELOAD && !DISABLE_XAML_HOT_RELOAD
192+
AvaloniaHotReload.Enable(app, CreateHotReloadContextFactory(app?.GetType(), appFilePath));
193+
#endif
199194
}
200195

201196
/// <summary>
202197
/// Enables hot reload functionality for the given Avalonia application.
203198
/// </summary>
204199
/// <param name="app">The Avalonia application instance for which hot reload should be enabled.</param>
205200
/// <param name="projectPathResolver">The callback function capable of resolving a project path for a given assembly.</param>
206-
[Conditional("DEBUG")]
201+
[Conditional("ENABLE_XAML_HOT_RELOAD")]
207202
[DebuggerStepThrough]
208203
public static void EnableHotReload(this Application app, Func<Assembly, string?> projectPathResolver)
209204
{
205+
#if ENABLE_XAML_HOT_RELOAD && !DISABLE_XAML_HOT_RELOAD
206+
AvaloniaHotReload.Enable(app, CreateHotReloadContextFactory(projectPathResolver));
207+
#endif
210208
}
211209

212210
/// <summary>
213211
/// Disables hot reload functionality for the given Avalonia application.
214212
/// </summary>
215213
/// <param name="app">The Avalonia application instance for which hot reload should be disabled.</param>
216-
[Conditional("DEBUG")]
214+
[Conditional("ENABLE_XAML_HOT_RELOAD")]
217215
[DebuggerStepThrough]
218216
public static void DisableHotReload(this Application app)
219217
{
218+
#if ENABLE_XAML_HOT_RELOAD && !DISABLE_XAML_HOT_RELOAD
219+
AvaloniaHotReload.Disable(app);
220+
#endif
220221
}
221222
}
222223
}
223-
#endif
224224

225225
#nullable restore
226226
#pragma warning restore

0 commit comments

Comments
 (0)