1. Wahl des passenden Basisrenderers,

Je nach Funktionsumfang des zu migrierenden Renderers muss die Basisklasse auf einer der folgenden abgeändert werden

  • ControlDefaultRendererBase: Renderer OHNE Unterteilung in Read- und EditMode-Methoden und OHNE Bind-Unterstützung
  • ControlDefaultBindableRendererBase: Renderer OHNE Unterteilung in Read- und EditMode aber MIT Bind-Unterstützung
  • ControlDefaultReadEditRendererBase: Renderer MIT Unterteilung in Read- und EditMode-Methoden und OHNE Bind-Unterstützung
  • ControlDefaultReadEditBindableRendererBase: Renderer MIT Unterteilung in Read- und EditMode-Methoden und MIT Bind-Unterstützung

Renderer, die migriert werden müssen, erben allerdings schon oftmals von einem anderen Core- oder CRM-Renderer, so dass sich diese Frage nicht stellt und die Migration entsprechend der übergeordneten Basisklassen erfolgen muss.

2. Renderer ohne Unterteilung in Read- und Edit-Methoden

Häufig gibt es in den alten Implementierungen Stellen, an denen die ReadMode-Methode einfach per => auf die EditMode-Methode verweist (oder umgekehrt). Gerade für dieses frühere Vorgehen ist die Nutzung dieser Basisklassen sinnvoll.

Die implementierende Methode solte in Render() umbenannt und eine möglicherweise verweisende Methode gelöscht werden.

Dieser Schritt kann auch nötig sein, wenn der abgeleitete Core-/CRM-Renderer nun eine entsprechende Basisklasse verwendet.

3. Entfernen der Übergabeparameter von Read-/EditMode und Bind

Renderer sind nun stateful und die Übergabeparameter an ReadMode(), EditMode() und Bind() entfallen.

Das Äquivalent von “parametersUi” ist das Instanz-Property “RenderParameters”.

4. Entfernen der Variableninitialisierung

Viele Renderer-Methoden haben direkt am Anfang folgende Initialisierung:

FormControlRendererUiParameterModel parametersUi = (FormControlRendererUiParameterModel)parameters;
FormRenderingContextUi renderingContext = (FormRenderingContextUi)parametersUi.RenderingContext;
string namePrefix = RenderingUtils.GetUIReadyContextPrefix(renderingContext);

Eigentlich können diese Zeilen einfach weg genommen und die Zugriffe entsprechend auf die RenderProperties umgebogen werden.
Um Kompatibilität zum Rest der Implementierung zu wahren, kann der Block allerdings auch mit leichten Änderungen stehen bleiben:

FormControlRendererParameterModel parametersUi = RenderParameters;
FormRenderingContext renderingContext = parametersUi.RenderingContext;
string namePrefix = parametersUi.NamePrefix;

Konkrete Änderungen sind:

  • parametersUi -> RenderParameters
  • renderingContext -> RenderParameters.RenderingContext
  • namePrefix -> RenderParameters.NamePrefix

5. Optional: Abruf der Property-Inhalte in den Render-Methoden nach Konvention

Der Zugriff auf das Property, muss in jedem Fall über die Deskriptoren erfolgen. Ein direktes Auslesen beispielsweise über GetPropertyValue() ist untersagt.

Für den Feldnamen des Controls auf dem Bind-Objekt wird garantiert, dass beim Abrufen von Daten zunächst GetValueFromModel() und anschließend ConvertFromModelPropertyType() aufgerufen wird.

Sollten weitere Werte aus dem Bind-Objekt benötigt werden, so werden diese ebenfalls immer unter Verwendung von PrepareBindProperty() und GetValueFromModel() abgerufen; der Aufruf auf ConvertFromModelPropertyType() ist in diesem Fall optional und muss von der Methode unterstützt werden.

Hieraus folgt:
Direkte Zugriffe auf RenderParameters.BindData sind immer kritisch zu betrachten. Hier ist man nur richtig, wenn tatsächlich der top-level-Datensatz verwendet werden soll. Das gültige Bind-Objekt für das aktuell angesteuerte Property ist BindObject! Was beispielsweise auch ein Teil-Datensatz sein kann.

Beispiel:
Der Feldname des Controls ist "Addresses[1].AddressType", BindData ist OrmCRMCompany.
Durch den Aufruf PrepareBindProperty("Addresses[1].AddressType") passiert folgendes:

  • RenderParameters.BindData bleibt wie es war eine Instanz von OrmCRMCompany
  • BindObject wird auf den angesteuerten Teildatensatz verschoben
  • PreparedProperty beinhaltet nun den PropertyDescriptor des Feldes "AddressType" für den Typen des Teildatensatzes

6. Pflege des ViewStates

Der ViewState ist neu und enthält wichtige Informationen für die Steuerelemente und insbesondere für das Binding. Folgendes muss gepflegt werden.

  • Ist ein Client-Control aus irgendeinem Grund für den Benutzer nicht editierbar, so muss ViewState.ShouldBind = false gesetzt werden.
  • ViewState.ViewStateData muss, wenn möglich, unter dem Schlüssel BindKeys.ModelProperty den serverseitig verwendeten Feldnamen (Property-Namen auf dem BindObject) enhalten. Ist das Control ein IPropertyControl mit der Methode GetFieldName(), so wird dies vom Framework automatisch gemacht.
  • ViewState.ViewStateData sollte unter dem Schlüssel BindKeys.ClientProperty den clientseitig verwendeten Feldnamen (inkl. RenderParameter.NamePrefix, also z.B. “DLG1.SearchNames”) enthalten.
  • Das zugehörige Control kann während des Bind-Vorgangs nicht verlässlich ermittelt werden; daher sollten Werte, die im Binding benötigt werden, ebenfalls unter eigenen Schlüsseln in ViewStateData abgelegt und während des Bindens wieder ausgelesen werden.

Erweitert der eigene Renderer nicht die Basisklassen, sondern einen Renderer vorhandener Steuerelementen, wird die Pflege schon durchgeführt, falls man gesichert die base.Render(), base.ReadMode() oder base.EditMode() aufruft.

7. Optional: Eigenes Binden von Werten nach Konvention

Für den Feldnamen des Controls auf dem Bind-Objekt wird garantiert, dass beim Binden von Daten zunächst GetValueFromRequest(), dann ConvertToModelPropertyType() und schließlich WriteValueToModel() aufgerufen wird.

Sollten weitere Werte in das Bind-Objekt geschrieben werden, so werden diese ebenfalls immer unter Verwendung von GetValueFromRequest(), PrepareBindProperty() und WriteValueToModel() verarbeitet; der Aufruf auf ConvertToModelPropertyType() ist in diesem Fall optional und muss von der Methode unterstützt werden.

Ansonsten gilt alles weitere aus Punkt 5.

8. Optional: Übertrag und ggf Anpassung von ICustomBindingControl.AssignValue nach ConvertFromModelPropertyType

Die Methode ICustomBindingControl.AssignValue diente früher dazu, Werte auf eine angepasste Weise aus dem Orm in das FormModel zu übertragen. Diese Methode wird innerhalb der Renderer durch ConvertFromModelPropertyType() abgelöst. Damit also beispielsweise der Renderer generisch mit einer List<Guid> arbeiten kann, muss ConvertFromModelPropertyType() dafür sorgen, dass der Wert, der hineingegeben wird, auch als List<Guid> zurückgegeben wird, egal, ob das nun eine einzelne Guid, ein String mit oder ohne Kommas oder ein GuitSet war.

9. Übertrag und ggf Anpassung von ICustomBindingControl.Bind nach ConvertToModelPropertyType

Wie Punkt 7 nur in Gegenrichtung.

Die Implementierung der Bind-Methode in den Basisrenderern ist für den Normalgebrauch vollkommen ausreichend. Es ist üblich, für einen eigenen Renderer die Convert-Methoden zu überschreiben, um Beispielsweise einen string in eine Guid oder eine Guid in einen ValueEnum umzuwandeln und damit weiterzuarbeiten. Weniger üblich – wenn auch dennoch valide – ist es, die WriteValueToModel-Methode zu überschreiben, wenn ein Wert beispielsweise gar nicht über ein einfaches SetValue auf dem PropertyDescriptor schreibbar ist, sondern vorher noch verarbeitet oder in mehrere Felder geschrieben werden muss. Am seltensten muss die Bind-Methode selbst überschrieben werden, und das auch nur, wenn zum Beispiel mehrere Werte zu einem Steuerelement gebunden werden müssen.