2011-03-01 8 views
8

Tengo algunos objetos comerciales escritos en Delphi con un esquema personalizado de persistencia de base de datos que finalmente está funcionando para mis necesidades. Vale genial. Ahora es el momento de las implementaciones de GUI. Y aquí comienza el problema.Controles GUI "Object Aware"

Cómo enlazar mi objeto a la GUI correctamente?

No puedo usar controles Data Aware ya que aislé todos los componentes de acceso a datos en la capa ORM, así que empiezo a escribir algunos controles "Object Aware" usando la unidad RTTI (estoy trabajando con Delphi 2010), pero tengo la sensación de que estoy yendo por el camino equivocado ...

Algunas ideas sobre cómo resolver esto usando solo los controles VCL?

Respuesta

6

Tiene varios patrones para vincular ORM con la interfaz de usuario.

Véase, por ejemplo, el patrón Model GUI Mediator. En resumen, usted escribe un observador que reflejará el contenido de ORM en los componentes de la interfaz de usuario, y viceversa. Esto se ha implementado, por ejemplo, en el tiOpf framework for Delphi (este enlace tiene videos).

Otro enfoque es para mapear los datos en tiempo de ejecución: el diseño de su forma como de costumbre, a continuación, se llena el contenido en caso OnShow, entonces el "Guardar" o "OK" botón validará a continuación, guardar el contenido en el registro ORM. Esto es lo que se hace en el main Sample application of our framework. Fácil de codificar en esta muestra simple, pero podría llevar al código de spaghetti si tiene muchos campos y validación para operar.

El último enfoque es permitir que su ORM cree el formulario.

En nuestro marco, puede definir algunas propiedades de IU sobre cada tabla, en una estructura dedicada. Luego, a single unit will create a form with all editable fields de su objeto ORM. Los enlaces a otros registros se mostrarán como un cuadro combinado, booleanos como casillas de verificación, conjuntos como casillas de radio, y así sucesivamente. A continuación, se trata el filtrado (por ejemplo, recortar un campo de texto de espacios en el lado izquierdo o derecho) y la validación (por ejemplo, asegurar que un valor de campo es único o una dirección IP válida) not in the UI part, but in the business logic itself, i.e. the ORM.

En mi humilde opinión, es obligatorio mantener una verdadera arquitectura de varios niveles. Es decir, la IU debe depender principalmente de la lógica comercial. Por ejemplo, la validación de datos debe ser parte del ORM, no de la UI. Por ejemplo, si decide agregar un cliente web a su aplicación cliente Delphi, no tendrá que codificar la validación en otro momento: será común para ambos clientes, separado de los detalles de implementación de la interfaz de usuario.

+0

Aunque el segundo enfoque podría implementarse más fácilmente en mi caso particular, creo que tomaré el primero, porque será más fácil crear y mantener formas complejas a largo plazo. Leí el documento en el enlace y el patrón de MGM parece encajar como guante para mí. Muchas gracias. –

0

Actualmente no hay forma de hacerlo con solo los controles VCL. Sé que Lazarus tiene un conjunto de controles de datos basados ​​en RTTI; Es posible que desee buscar algunas ideas básicas. Pero es más difícil de lo que piensas al principio. Por ejemplo, a diferencia de un conjunto de datos, un objeto no tiene un mecanismo de señalización incorporado cuando los valores de sus miembros cambian. Lo que significa que a menos que sus controles de enlace de datos posean el objeto por completo y que nada más tenga acceso a él, es posible que otro código cambie algún valor y luego ese cambio no se refleje en la UI.

He escuchado varias cosas del equipo Delphi en los últimos años acerca de la ampliación del modelo de objetos o el modelo RTTI para permitir una mejor unión de datos, pero sea lo que sea, todavía faltan algunos años.

+2

Desarrollé un marco completo orientado a objetos alrededor de estos principios en 1998 (Delphi 4), y también una capa de asignación de datos equivalente. Sin embargo, la cantidad de trabajo involucrado para hacer esto de manera adecuada es mucho más de lo que se podría pensar: obtenga unos buenos 6-12 meses para desarrollar algo que esté listo para usar en una aplicación comercial no trivial. Si bien puede prototipar estas cosas en un día más o menos, esto no es en absoluto una indicación de la cantidad de trabajo requerido para implementar el diseño correctamente. – Misha

2

lo que podría hacer (aunque no tengo ejemplos de código) es utilizar una combinación de

  • ayudantes de clase o clases de interceptores
  • interfaces de unión para los objetos de dominio único y/o listas de objetos de dominio

Los ayudantes de clase tienen la desventaja de que no son compatibles oficialmente y no puede agregar ningún campo a la clase a la que está ayudando.

clases Interceptor son simplemente clases descendientes con el mismo nombre que su antepasado:

uses 
    stdctrls; 

type 
    TButton = class(stdctrls.TButton) 
    end; 

Usted puede poner clases de interceptores en su propia unidad y el uso que dondequiera que usted desee. Solo asegúrese de que estas unidades estén incluidas DESPUÉS de la unidad estándar, de modo que su descendiente se utilice en tiempo de ejecución.

beneficio de las clases interceptores:

  • Puede seguir el diseño de su interfaz de usuario mediante VCL estándar o controles de terceros.
  • Obtienes todas las ventajas de los descendientes.
  • No necesita crear o instalar sus propios controles.
  • No hay necesidad de clases de asignador especiales o el uso de RTTI.
  • fácilmente (así, con relativa facilidad) integrado en una interfaz de usuario comprobable (DUnit-) a lo largo de las líneas del artículo de Julian Bucknall sobre esto en la (distinta) Delphi Magazine como se hace referencia en esta pregunta/respuesta: Unit-testing mouse event handlers

Pseudo muestra de control de interceptor con la unión interfaz de interfaz/comando:

uses 
    stdctrls; 

type 
    ICommandAction = interface(IInterface) 
    function IsEnabled: Boolean; 
    procedure Execute; 
    procedure Update; 
    end; 

    IBindSingle = interface(IInterface) 
    function GetValueFromControl: string; 
    procedure LoadValueIntoControl(const aValue: string); 
    end; 

    TButton = class(stdctrls.TButton, ICommandAction) 
    protected 
    function IsEnabled: Boolean; 
    procedure Execute; 
    procedure Update; 
    end; 

    TEdit = class(stdctrls.TEdit, IBindSingle) 
    function GetValueFromControl: string; 
    procedure LoadValueIntoControl(const aValue: string); 
    end; 

aplicación podría ser a lo largo de las líneas de:

function TButton.IsEnabled: Boolean; 
    begin 
    Result := Self.Enabled; 
    end; 

    procedure TButton.Execute; 
    begin 
    Self.Action.Execute; 
    end; 

    procedure TButton.Update; 
    begin 
    Self.Action.Update; 
    end; 

    function TEdit.GetValueFromControl: string; 
    begin 
    Result := Self.Text; 
    end; 

    procedure LoadValueIntoControl(const aValue: string); 
    begin 
    Self.Text := aValue; 
    end; 
1

Mi cliente actual ha hecho sus propias clases de "asignador" en el pasado (antes de venir). Sus objetos de datos tienen campos (que son objetos), y puede asignar estos campos a un control. Extendí el marco mediante el uso de un enfoque MVC-como:

edtTarraCode: TAdvEdit; 

procedure TframTarraTab.InitMapping; 
begin 
    ... 
    Mapper.AddMapping(edtTarraCode, Controller.DataModel.tarID); 
    ... 
end; 

por el control se crea una clase simple "mapeo":

TMappingAdvEdit = class(TBaseEditMapping) 
protected 
    procedure InitControl; override; 

    procedure AppData2Control; override; 
    procedure Control2AppData; override; 
end; 

Sin sience cohete, y tal vez mejores soluciones están disponibles en la media hora (esto funcionó en D6 y menos :-)) pero funciona lo suficientemente bien para el cliente.

Btw: también se utiliza un generador de objetos de datos. Entonces, si un campo cambia en la base de datos (por ejemplo tarra.tarid se cambia a tareID) obtenemos un error de compilación porque "tarid" ya no existe. Esto funciona mucho mejor que la asignación de "cadena fija" (errores de tiempo de ejecución).

+0

Esta es una solución interesante, pero requeriría un rediseño completo de mis objetos comerciales ... Será más compleja, pero creo que probaré el patrón MediaInd de GUI modelo como lo sugirió A. Bouchwz. Muchos thaks, hombre. –

0

Eche un vistazo a EverClassy Dataset en http://www.inovativa.com.br. Puede satisfacer sus necesidades. EverClassy Dataset es un conjunto de datos Delphi diseñado para ser poblado por objetos en lugar de registros de un sistema de base de datos.

Con este componente tendrá la posibilidad de interoperar los objetos de su dominio con los componentes del dataware, lo que le dará un gran poder para construir su GUI.

Cuestiones relacionadas