Explorar el Código

Add support for ISupportInitialize/ISupportInitializeNotification for UIs (#261)

When building UIs, it's quite common to separate the UI/control building code
from the interaction logic by placing it all in a single method, usually
`InitializeComponent` or similar. This also allows other tooling to generate
such a method from potentially other UI languages (XML/Json/Yaml/whatever)
at build-time.

Even if no codegen is involved, having the UI building in a single place
allows for creating base views that provide some common boilerplate that
derived views can subsequently extend by overriding the base "initialize"
method.

The tricky part is that the base class cannot provide (at least that's
against the .NET guidelines and it certainly makes for some confusing and
potentially hard to reason bugs) a *virtual* method to do so and invoke
it from the constructor, since the base class constructor is called and
finishes *before* the derived class constructor does. This means that
fields that may be required by the derived view to build its UI in its
override of the "initialize" method would not be available by the time
the base class calls it.

Which is why the best place to handle such an approach is in the library
itself, which controls the exact time when a view is about to be shown.
Since no other method is provided for "initialize once before showing
for the first time" scenario, a good trade-off is to simply enable this
mechanism as totally opt-in by allowing views to implement `ISupportInitialize`
(which provides the `BeginInit`/`EndInit` pair) or `ISupportInitializeNotification`
(which adds `IsInitialized`/`Initialized` property/event). If the former
is implemented, the Begin/End will be called on every `Run`, since the
library has no way of knowing if the view has already been initialized
before. In these cases, the views could just clear all its controls in
the `BeginInit` and reconstruct the UI entirely in `EndInit`. The
`ISupportInitializeNotification` interface allows more control for views
that require that Begin/End init are called exactly once.
Daniel Cazzulino hace 5 años
padre
commit
ebb3c186ce
Se han modificado 1 ficheros con 9 adiciones y 0 borrados
  1. 9 0
      Terminal.Gui/Core.cs

+ 9 - 0
Terminal.Gui/Core.cs

@@ -18,6 +18,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Linq;
 using NStack;
+using System.ComponentModel;
 
 namespace Terminal.Gui {
 
@@ -1910,6 +1911,14 @@ namespace Terminal.Gui {
 			var rs = new RunState (toplevel);
 
 			Init ();
+			if (toplevel is ISupportInitializeNotification initializableNotification && 
+			    !initializableNotification.IsInitialized) {
+				initializableNotification.BeginInit();
+				initializableNotification.EndInit();
+			} else if (toplevel is ISupportInitialize initializable) {
+				initializable.BeginInit();
+				initializable.EndInit();
+			}
 			toplevels.Push (toplevel);
 			Current = toplevel;
 			Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessMouseEvent);