Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/EasyWindowsApplication/Core/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal sealed class Application :
private readonly List<WindowModel> _windows = new();
private MasterRouter _router = null!;
private readonly HandleRegistry _registry = new();
private Action<IBehaviorBuilder>? _pendingBehavior;

public IApplicationLayoutPhase Resources(Action<IResourcesDictionary> configure)
{
Expand All @@ -37,9 +38,7 @@ public IApplicationPostLayoutPhase Layout(Action<ILayoutBuilder> configure)

public IApplicationPostBehaviorPhase Behavior(Action<IBehaviorBuilder> configure)
{
BehaviorBuilder.Registry = _registry;
ControlAccess.SetController(BehaviorBuilder);
configure(BehaviorBuilder);
_pendingBehavior += configure;
return this;
}

Expand Down Expand Up @@ -70,6 +69,13 @@ public void Initialize()
RegisterMain(window);
}

if (_pendingBehavior is not null)
{
BehaviorBuilder.Registry = _registry;
ControlAccess.SetController(BehaviorBuilder);
_pendingBehavior(BehaviorBuilder);
}

Procedures.RunMessageLoop();
}

Expand Down
36 changes: 36 additions & 0 deletions src/EasyWindowsApplication/Core/HandleRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal sealed class HandleRegistry
private readonly Dictionary<string, WeakReference<IControl>> _nameToControl = new(StringComparer.Ordinal);
private readonly Dictionary<string, IBaseWindow> _windowByName = new(StringComparer.Ordinal);
private readonly Dictionary<nint, IBaseWindow> _hwndToWindow = new();
private readonly Dictionary<nint, List<nint>> _windowToControls = new();

internal void Register(nint hwnd, IControl control)
{
Expand All @@ -42,6 +43,41 @@ internal void Unregister(nint hwnd)
}
}

internal void ClearAllControls()
{
_hwndToControl.Clear();
_nameToControl.Clear();
_windowToControls.Clear();
}

internal void TrackChildWindow(nint parentHwnd, nint childHwnd)
{
if (!_windowToControls.TryGetValue(parentHwnd, out var list))
{
list = new List<nint>();
_windowToControls[parentHwnd] = list;
}
list.Add(childHwnd);
}

internal void UnregisterWindowControls(nint parentHwnd)
{
if (!_windowToControls.TryGetValue(parentHwnd, out var direct))
return;
var stack = new Stack<nint>(direct);
_windowToControls.Remove(parentHwnd);
while (stack.Count > 0)
{
var h = stack.Pop();
Unregister(h);
if (_windowToControls.TryGetValue(h, out var sub))
{
foreach (var ch in sub) stack.Push(ch);
_windowToControls.Remove(h);
}
}
}

internal void RegisterWindow(IBaseWindow window)
{
if (!string.IsNullOrEmpty(window.Name))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@ internal sealed class ChildrenBuilderImpl : IChildrenBuilder

public IChildrenBuilder View<T>(Action<View<T>> configure) where T : class, IControl
{
var control = ControlActivatorRegistry.Shared.Create<T>();
var view = new View<T>(control);
var view = new View<T>();
configure(view);

_content.Children.Add(new ViewModel
{
Name = view.Instance.Name,
Control = view.Instance
Name = view.PendingName ?? "",
ControlType = typeof(T),
Configure = view.BuildConfigure()
});
return this;
}

public IChildrenBuilder View<T>(Func<View<T>, View<T>> configure) where T : class, IControl
{
var control = ControlActivatorRegistry.Shared.Create<T>();
var view = new View<T>(control);
var view = new View<T>();
var result = configure(view);

_content.Children.Add(new ViewModel
{
Name = result.Instance.Name,
Control = result.Instance
Name = result.PendingName ?? "",
ControlType = typeof(T),
Configure = result.BuildConfigure()
});
return this;
}
Expand Down
2 changes: 2 additions & 0 deletions src/EasyWindowsApplication/Core/LayoutModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ internal sealed class ViewModel : IViewModel
internal string Name { get; set; } = "";
internal IControl? Control { get; set; }
internal ContentModel? SubContent { get; set; }
internal Type? ControlType { get; set; }
internal Action<IControl>? Configure { get; set; }
}
3 changes: 2 additions & 1 deletion src/EasyWindowsApplication/Core/MasterRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,9 @@ internal nint WndProc(nint hwnd, uint msg, nint wParam, nint lParam)
else if (win is AlternativeWindowImpl aw) aw.RaiseClosed();
}

// Limpieza determinística del registry (Fase 1: evita memory leak)
// Limpieza determinística del registry (Fase 5: evita memory leak de controles)
_registry.Unregister(hwnd);
_registry.UnregisterWindowControls(hwnd);
_registry.UnregisterWindowByHwnd(hwnd);
HandleRegistry.UnregisterRouter(hwnd);
_windowBrushes.Remove(hwnd);
Expand Down
21 changes: 19 additions & 2 deletions src/EasyWindowsApplication/Core/Windowing/AlternativeWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using EasyWindowsApplication.Core;
using EasyWindowsApplication.Core.LayoutEngine;
using EasyWindowsApplication.Share;
using EasyWindowsApplication.Win32ControlsModule.Frontend;

namespace EasyWindowsApplication.Core.Windowing;

Expand Down Expand Up @@ -154,10 +155,26 @@ private static void MaterializeChildren(ContentModel model, nint parentHwnd, Han
{
if (vmBase is not ViewModel vm) continue;
var control = vm.Control;
if (control is null) continue;
if (control is null)
{
if (vm.ControlType is null) continue;
control = (IControl)ControlActivatorRegistry.Shared.CreateFor(vm.ControlType);
if (control is ControlBase cbPre)
{
cbPre.Router = router;
cbPre.Registry = registry;
}
vm.Configure?.Invoke(control);
vm.Control = control;
}

if (!ControlActivatorRegistry.Shared.TryGetFactoryForControl(control, out var factory) || factory is null)
throw new InvalidOperationException($"No handle factory registered for control '{control.GetType().Name}' (name='{control.Name}').");
{
if (vm.ControlType is not null && ControlActivatorRegistry.Shared.TryGetFactory(vm.ControlType) is { } fallback)
factory = fallback;
else
throw new InvalidOperationException($"No handle factory registered for control '{control.GetType().Name}' (name='{control.Name}').");
}

nint hwnd = factory.CreateHandle(parentHwnd, control, registry);
if (hwnd == 0)
Expand Down
22 changes: 20 additions & 2 deletions src/EasyWindowsApplication/Core/Windowing/WindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using EasyWindowsApplication.Core;
using EasyWindowsApplication.Core.LayoutEngine;
using EasyWindowsApplication.Share;
using EasyWindowsApplication.Win32ControlsModule.Frontend;

namespace EasyWindowsApplication.Core.Windowing;

Expand Down Expand Up @@ -429,11 +430,28 @@ private static void MaterializeChildren(ContentModel model, nint parentHwnd, Han
{
if (vmBase is not ViewModel vm) continue;
var control = vm.Control;
if (control is null) continue;
if (control is null)
{
if (vm.ControlType is null) continue;
control = (IControl)ControlActivatorRegistry.Shared.CreateFor(vm.ControlType);
// Asignar Router/Registry antes de Configure para que OnMessage tenga contexto (mejora sobre referencia del plan)
if (control is ControlBase cbPre)
{
cbPre.Router = router;
cbPre.Registry = registry;
}
vm.Configure?.Invoke(control);
vm.Control = control;
}

// Resolver factory por tipo concreto/interfaz
if (!ControlActivatorRegistry.Shared.TryGetFactoryForControl(control, out var factory) || factory is null)
throw new InvalidOperationException($"No handle factory registered for control '{control.GetType().Name}' (name='{control.Name}'). Registre un INativeHandleFactory para ese tipo.");
{
if (vm.ControlType is not null && ControlActivatorRegistry.Shared.TryGetFactory(vm.ControlType) is { } fallback && fallback is not null)
factory = fallback;
else
throw new InvalidOperationException($"No handle factory registered for control '{control.GetType().Name}' (name='{control.Name}'). Registre un INativeHandleFactory para ese tipo.");
}

nint hwnd = factory.CreateHandle(parentHwnd, control, registry);
if (hwnd == 0)
Expand Down
Loading
Loading