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
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* **UX simplificada** — 4 bloques secuenciales (`Resources → Layout → Behavior → Initialize`) que reducen el esfuerzo de cableado y aceleran la entrega temprana.
* **Fluent API declarativa** — El IntelliSense te guía de Resources a Layout a Behavior a Initialize, impidiendo equivocarte de orden.
* **Source Generators** — Acceso tipado a controles por su nombre. Si te equivocas, falla en compilación, no en runtime.
* **Válvula de escape Win32** — `UseWinApi()` + `WithWin32State` para acceso directo al HWND y WndProc cuando lo necesites.
* **Válvula de escape Win32** — `UseWinApi()` habilita el uso del módulo de controles Win32 nativos de alto rendimiento (`Win32ControlsModule`) cuando lo necesites.

## Un vistazo al código

Expand Down Expand Up @@ -159,19 +159,18 @@ EasyWindowsApplication/src/

El **Source Generator** analiza tu `Layout` en tiempo de compilación: cada `.Name("BtnGuardar")` dentro de un `View<T>` o `Window` genera una propiedad tipada (`bh.BtnGuardar`) en `Behavior`. Al compilar, ya no necesitas localizar controles por string — si el nombre no coincide, el compilador te lo dice. En runtime, el `MasterRouter` centraliza el bucle de mensajes de Win32 y despacha eventos tipados (`Click`, etc.) de forma automatica.

Para acceso total al HWND y WndProc, activa `UseWinApi()` en Resources y usa `WithWin32State`:
Para usar los controles nativos del módulo Win32, activa `UseWinApi()` en Resources:

```csharp
WindowsApplication
.Resources(r => r.Setting(s => s.UseWinApi()))
.Layout(...)
.Behavior(b => b.WithWin32State(ctrl => {
var btn = ctrl.Get<IButton>("BtnGuardar");
btn.OnMessage(WM.COMMAND, (w, l) => { /* tu código Win32 puro */ });
}))
.Behavior(bh => { ... })
.Initialize();
```

> **Nota**: el acceso de bajo nivel al HWND/WndProc (`WithWin32State`) es plomería interna del framework; se cableará automáticamente según el patrón MVU cuando `UseWinApi()` esté activo.

## ¿Desarrollas el framework?

Toda la información para compilar desde el código fuente, entender la arquitectura de módulos, el pipeline de generación de iconos y las decisiones de diseño se encuentran en [`CONTRIBUTING.md`](CONTRIBUTING.md) y en la **Wiki del repositorio**.
2 changes: 0 additions & 2 deletions src/EasyWindowsApplication/CoreModule/Backend/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ public void Initialize()
RegisterMain(window);
}

BehaviorBuilder.ApplyPending(_registry);

if (BehaviorBuilder.Win32Configurator is not null)
BehaviorBuilder.Win32Configurator(new Win32StateImpl(_registry));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,57 +1,18 @@
using EasyWindowsApplication.CoreModule.Backend;
using EasyWindowsApplication.Share.Infrastructure;
using EasyWindowsApplication.Win32ControlsModule.Frontend;
using EasyWindowsApplication.WindowingModule.Frontend;

namespace EasyWindowsApplication.CoreModule.Frontend;

internal sealed class BehaviorBuilderImpl : IBehaviorBuilder, ControlAccess.IBehaviorServicesController
{
internal Action<IWin32State>? Win32Configurator { get; private set; }
internal HandleRegistry? Registry { get; set; }
private readonly List<(string Name, Action Handler)> _pendingClicks = new();

public IBehaviorBuilder OnClick(string controlName, Action handler)
{
var control = Registry?.GetByName(controlName);
if (control is IClickEventSource clickable)
{
clickable.AddClickHandler(handler);
}
else
{
_pendingClicks.Add((controlName, handler));
}
return this;
}

public IBehaviorBuilder WithWin32State(Action<IWin32State> configure)
{
Win32Configurator = configure;
return this;
}

public T Get<T>(string name) where T : View<T>
{
var control = Registry?.GetByName(name);
if (control is null)
throw new InvalidOperationException($"Control '{name}' not found.");
return (T)control;
}
internal void SetWin32Configurator(Action<IWin32State> configure) => Win32Configurator = configure;

T ControlAccess.IBehaviorServicesController.Get<T>(string name)
=> (T)Registry!.GetByName(name)!;

T ControlAccess.IBehaviorServicesController.GetWindow<T>(string name)
=> (T)Registry!.GetWindow(name)!;

internal void ApplyPending(HandleRegistry registry)
{
foreach (var (name, handler) in _pendingClicks)
{
if (registry.GetByName(name) is IClickEventSource clickable)
clickable.AddClickHandler(handler);
}
_pendingClicks.Clear();
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
using System.ComponentModel;
using EasyWindowsApplication.Share.Infrastructure;

namespace EasyWindowsApplication.CoreModule.Frontend;

public interface IBehaviorBuilder
{
[EditorBrowsable(EditorBrowsableState.Never)]
IBehaviorBuilder OnClick(string controlName, Action handler);

[EditorBrowsable(EditorBrowsableState.Never)]
IBehaviorBuilder WithWin32State(Action<IWin32State> configure);

[EditorBrowsable(EditorBrowsableState.Never)]
T Get<T>(string name) where T : View<T>;
}
public interface IBehaviorBuilder { }
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace EasyWindowsApplication.CoreModule.Frontend;

public interface IWin32State
internal interface IWin32State
{
T Get<T>(string name) where T : View<T>;
}
9 changes: 5 additions & 4 deletions src/Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
)
)
)
.Behavior(bh => bh
.BtnIncrement.OnClick(() =>
.Behavior(bh =>
{
bh.BtnIncrement.OnClick(() =>
{
counter++;
bh.BtnIncrement.Text = $"Click: {counter}";
})
)
});
})
.Initialize();
2 changes: 1 addition & 1 deletion src/creators_vision.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@

- **`Behavior`**: Enrutamiento de eventos y lógica. Gracias a los Source Generators, los nombres dados en el `Layout` (ej. `BtnGuardar`) aparecen aquí fuertemente tipados, eliminando los *Magic Strings*.Soporta dos modalidades:
* Behavior<IMvuState> (Implícito/Por defecto): Para un flujo moderno basado en estados y señales, ideal para la mayoría de aplicaciones.
* Behavior<IWin32State>: Válvula de escape para desarrolladores avanzados. Expone el HWND y permite interceptar mensajes directamente en el WndProc (ej. WM_COMMAND, WM_PAINT) para cada control declarado en el Layout, ofreciendo el mismo nivel de control que C/C++ nativo.
* Behavior<IWin32State>: Válvula de escape para desarrolladores avanzados. Expone el HWND y permite interceptar mensajes directamente en el WndProc (ej. WM_COMMAND, WM_PAINT) para cada control declarado en el Layout, ofreciendo el mismo nivel de control que C/C++ nativo. Es plomería interna (`IWin32State` es `internal`), se cablea automáticamente al activar `UseWinApi()`.
- **`Initialize()`**: El nodo terminal que bloquea el hilo, cede el control al `CoreModule` y arranca el bucle de mensajes de Win32. En el flujo, es el nodo terminal de la Fluent API. No debe confundirse con un hook de ciclo de vida ni con las acciones del IDE (Build, Run). Su semántica es: en este punto la declaración está completa, todo ha sido definido y el sistema está habilitado para arrancar. Es el equivalente conceptual de `Build()` en el patrón Builder clásico, con la diferencia de que el nombre refleja que los Source Generators ya han actuado en tiempo de compilación, por lo que al llegar aquí no se construye nada, simplemente se lanza lo que ya está construido.
- Plantilla para Visual Studio, esta debe servir como un "esqueleto" (scaffolding) para crear las carpetas y poner los archivos placeholder.

Expand Down
Loading