Freigeben über


Gründe für Remote-UI

Eines der Hauptziele des VisualStudio.Extensibility-Modells besteht darin, Erweiterungen außerhalb des Visual Studio-Prozesses auszuführen. Diese Entscheidung führt zu einem Hindernis für das Hinzufügen der UI-Unterstützung zu Erweiterungen, da die meisten Benutzeroberflächenframeworks in Bearbeitung sind.

Remote-UI ist eine Reihe von Klassen, mit denen Sie WPF-Steuerelemente (Windows Presentation Foundation) in einer out-of-process-Erweiterung definieren und als Teil der Visual Studio-Benutzeroberfläche anzeigen können.

Die Remotebenutzeroberfläche lehnt sich stark dem Entwurfsmuster "Model-View-ViewModel " zu, das sich auf XAML (Extensible Application Markup Language) und Datenbindung, Befehle (anstelle von Ereignissen) und Triggern stützt (anstatt mit der logischen Struktur aus CodeBehind zu interagieren).

Während Remote-UI entwickelt wurde, um Out-of-Process-Erweiterungen zu unterstützen, verwenden VisualStudio.Extensibility-APIs, die auf Remote-UI basieren, wie ToolWindow, Remote-UI auch für In-Process-Erweiterungen.

Die wichtigsten Unterschiede zwischen Remote-UI und normaler WPF-Entwicklung sind:

  • Die meisten Remote-UI-Vorgänge, einschließlich der Bindung an den Datenkontext und die Ausführung von Befehlen, sind asynchron.
  • Beim Definieren von Datentypen, die in Remote-UI-Datenkontexten verwendet werden sollen, müssen sie mit den DataContract Attributen und DataMember Attributen versehen werden, und ihr Typ muss von der Remote-UI serialisierbar sein (weitere Informationen finden Sie hier ).
  • Die Remote-UI erlaubt keinen Verweis auf Ihre eigenen benutzerdefinierten Steuerelemente.
  • Ein Remotebenutzersteuerelement ist in einer einzelnen XAML-Datei vollständig definiert, die auf ein einzelnes (aber potenziell komplexes und geschachteltes) Datenkontextobjekt verweist.
  • Die Remotebenutzeroberfläche unterstützt codeBehind oder Ereignishandler nicht (Problemumgehungen werden im Dokument für erweiterte Remote-UI-Konzepte beschrieben).
  • Ein Remotebenutzersteuerelement wird im Visual Studio-Prozess instanziiert, nicht der Prozess, der die Erweiterung hostet: Der XAML-Code kann nicht auf Typen und Assemblys aus der Erweiterung verweisen, sondern auf Typen und Assemblys aus dem Visual Studio-Prozess verweisen.

Erstellen einer Remote-UI Hello World-Erweiterung

Erstellen Sie zunächst die einfachste Remote-UI-Erweiterung. Befolgen Sie die Anweisungen zum Erstellen Ihrer ersten out-of-process Visual Studio-Erweiterung.

Sie sollten jetzt über eine funktionierende Erweiterung mit einem einzigen Befehl verfügen. Der nächste Schritt besteht darin, ein ToolWindow sowie ein RemoteUserControl hinzuzufügen. Die RemoteUserControl ist das Remote UI-Äquivalent eines WPF-Benutzersteuerelements.

Sie enden mit vier Dateien:

  1. eine .cs Datei für den Befehl, der das Toolfenster öffnet,
  2. eine .cs Datei für das ToolWindow, das RemoteUserControl für Visual Studio bereitstellt,
  3. eine .cs Datei für das RemoteUserControl, das auf seine XAML-Definition verweist,
  4. eine .xaml Datei für die RemoteUserControl.

Später fügen Sie einen Datenkontext für das RemoteUserControl hinzu, das das ViewModel im MVVM-Muster (Model-View-ViewModel) darstellt.

Aktualisieren des Befehls

Aktualisieren Sie den Code des Befehls, um das Toolfenster mithilfe von ShowToolWindowAsync anzuzeigen.

public override Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
    return Extensibility.Shell().ShowToolWindowAsync<MyToolWindow>(activate: true, cancellationToken);
}

Sie können auch die Änderung von CommandConfiguration und eine geeignetere Anzeigemeldung und Platzierung von string-resources.json in Betracht ziehen.

public override CommandConfiguration CommandConfiguration => new("%MyToolWindowCommand.DisplayName%")
{
    Placements = new[] { CommandPlacement.KnownPlacements.ViewOtherWindowsMenu },
};
{
  "MyToolWindowCommand.DisplayName": "My Tool Window"
}

Erstellen des Toolfensters

Erstellen Sie eine neue MyToolWindow.cs Datei und definieren Sie eine MyToolWindow Klasse, die ToolWindow erweitert.

Die GetContentAsync Methode sollte einen IRemoteUserControl Wert zurückgeben, den Sie im nächsten Schritt definieren werden. Da die Fernsteuerung entsorgt werden muss, entsorgen Sie sie, indem Sie die Dispose(bool) Methode überschreiben.

namespace MyToolWindowExtension;

using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.ToolWindows;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;

[VisualStudioContribution]
internal class MyToolWindow : ToolWindow
{
    private readonly MyToolWindowContent content = new();

    public MyToolWindow(VisualStudioExtensibility extensibility)
        : base(extensibility)
    {
        Title = "My Tool Window";
    }

    public override ToolWindowConfiguration ToolWindowConfiguration => new()
    {
        Placement = ToolWindowPlacement.DocumentWell,
    };

    public override async Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
        => content;

    public override Task InitializeAsync(CancellationToken cancellationToken)
        => Task.CompletedTask;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            content.Dispose();

        base.Dispose(disposing);
    }
}

Erstellen der Remotebenutzersteuerung

Führen Sie diese Aktion in drei Dateien aus:

Klasse für Fernsteuerung von Benutzern

Die Remote-Benutzersteuerungsklasse namens MyToolWindowContent ist einfach:

namespace MyToolWindowExtension;

using Microsoft.VisualStudio.Extensibility.UI;

internal class MyToolWindowContent : RemoteUserControl
{
    public MyToolWindowContent()
        : base(dataContext: null)
    {
    }
}

Sie benötigen noch keinen Datenkontext, sodass Sie ihn vorerst auf null festlegen können.

Eine Klasse, die RemoteUserControl erweitert, verwendet automatisch die eingebettete XAML-Ressource mit demselben Namen. Wenn Sie dieses Verhalten ändern möchten, setzen Sie die GetXamlAsync Methode außer Kraft.

XAML-Definition

Erstellen Sie als Nächstes eine Datei mit dem Namen MyToolWindowContent.xaml:

<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml">
    <Label>Hello World</Label>
</DataTemplate>

Die XAML-Definition der Remote-Benutzersteuerung ist ein normaler WPF-XAML-Code, der ein DataTemplate darstellt. Dieser XAML-Code wird an Visual Studio gesendet und zum Ausfüllen des Toolfensterinhalts verwendet. Wir verwenden einen speziellen Namespace (xmlns Attribut) für Remote-UI-XAML: http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml.

Festlegen von XAML als eingebettete Ressource

Öffnen Sie schließlich die .csproj Datei, und stellen Sie sicher, dass die XAML-Datei als eingebettete Ressource behandelt wird:

<ItemGroup>
  <EmbeddedResource Include="MyToolWindowContent.xaml" />
  <Page Remove="MyToolWindowContent.xaml" />
</ItemGroup>

Wie zuvor beschrieben, muss die XAML-Datei denselben Namen wie die Remotebenutzersteuerungsklasse haben. Um genau zu sein, muss der vollständige Name der Klassenerweiterung RemoteUserControl mit dem Namen der eingebetteten Ressource übereinstimmen. Wenn beispielsweise der vollständige Name der Remotebenutzersteuerungsklasse lautet MyToolWindowExtension.MyToolWindowContent, sollte der name der eingebetteten Ressource sein MyToolWindowExtension.MyToolWindowContent.xaml. Standardmäßig werden eingebettete Ressourcen einem Namen zugewiesen, der aus dem Stammnamespace für das Projekt besteht, alle Unterordnerpfade, unter denen sie stehen können, und deren Dateiname. Dies kann Probleme verursachen, wenn ihre Remotebenutzersteuerungsklasse einen Namespace verwendet, der sich vom Stammnamespace des Projekts unterscheidet oder wenn sich die XAML-Datei nicht im Stammordner des Projekts befindet. Bei Bedarf können Sie mithilfe des LogicalName Tags einen Namen für die eingebettete Ressource erzwingen:

<ItemGroup>
  <EmbeddedResource Include="MyToolWindowContent.xaml" LogicalName="MyToolWindowExtension.MyToolWindowContent.xaml" />
  <Page Remove="MyToolWindowContent.xaml" />
</ItemGroup>

Testen der Erweiterung

Sie sollten jetzt F5 drücken können, um die Erweiterung zu debuggen.

Screenshot des Menü- und Toolfensters.

Hinzufügen von Unterstützung für Designs

Es ist ratsam, die Benutzeroberfläche so zu gestalten, dass Visual Studio thematisiert werden kann, wodurch unterschiedliche Farben verwendet werden.

Aktualisieren Sie den XAML-Code, um die formatvorlagen und Farben zu verwenden, die in Visual Studio verwendet werden:

<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml"
              xmlns:styles="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
              xmlns:colors="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0">
    <Grid>
        <Grid.Resources>
            <Style TargetType="Label" BasedOn="{StaticResource {x:Static styles:VsResourceKeys.ThemedDialogLabelStyleKey}}" />
        </Grid.Resources>
        <Label>Hello World</Label>
    </Grid>
</DataTemplate>

Die Beschriftung verwendet jetzt das gleiche Design wie die restliche Visual Studio-Benutzeroberfläche und ändert die Farbe automatisch, wenn der Benutzer in den dunklen Modus wechselt:

Ein Screenshot zeigt ein thematisiertes Toolfenster.

Hier verweist das xmlns Attribut auf die Microsoft.VisualStudio.Shell.15.0-Assembly , die keine der Erweiterungsabhängigkeiten ist. Dies ist in Ordnung, da dieser XAML-Code vom Visual Studio-Prozess verwendet wird, der eine Abhängigkeit von Shell.15 hat, nicht von der Erweiterung selbst.

Um ein besseres XAML-Bearbeitungserlebnis zu erzielen, können Sie dem Erweiterungsprojekt vorübergehend ein PackageReference zu Microsoft.VisualStudio.Shell.15.0 hinzufügen. Vergessen Sie nicht, es später zu entfernen , da eine out-of-process VisualStudio.Extensibility-Erweiterung nicht auf dieses Paket verweisen sollte!

Hinzufügen eines Datenkontexts

Fügen Sie eine Datenkontextklasse für die Remotebenutzersteuerung hinzu:

using System.Runtime.Serialization;

namespace MyToolWindowExtension;

[DataContract]
internal class MyToolWindowData
{
    [DataMember]
    public string? LabelText { get; init; }
}

Aktualisieren Sie MyToolWindowContent.cs und MyToolWindowContent.xaml, um sie zu verwenden:

internal class MyToolWindowContent : RemoteUserControl
{
    public MyToolWindowContent()
        : base(dataContext: new MyToolWindowData { LabelText = "Hello Binding!"})
    {
    }
<Label Content="{Binding LabelText}" />

Der Inhalt der Bezeichnung wird jetzt über die Datenbindung festgelegt:

Screenshot des Toolfensters mit Datenbindung.

Der hier aufgeführte Datentyp ist mit DataContract und DataMember Attributen gekennzeichnet. Dies liegt daran, dass die MyToolWindowData Instanz im Erweiterungshostprozess vorhanden ist, während das aus dem Visual Studio-Prozess erstellte MyToolWindowContent.xaml WPF-Steuerelement vorhanden ist. Damit die Datenbindung funktioniert, generiert die Remote-UI-Infrastruktur einen Proxy des MyToolWindowData Objekts im Visual Studio-Prozess. Die Attribute DataContract und DataMember geben an, welche Typen und Eigenschaften für die Datenbindung relevant sind und im Proxy repliziert werden sollen.

Der Datenkontext der Remotebenutzersteuerung wird als Konstruktorparameter der RemoteUserControl Klasse übergeben: Die RemoteUserControl.DataContext Eigenschaft ist schreibgeschützt. Dies bedeutet nicht, dass der gesamte Datenkontext unveränderlich ist, aber das Stammdatenkontextobjekt einer Remotebenutzersteuerung kann nicht ersetzt werden. Im nächsten Abschnitt werden MyToolWindowData wir änderbar und feststellbar machen.

Serialisierbare Typen und Remote-UI-Datenkontext

Ein Remote-UI-Datenkontext kann nur serialisierbare Typen enthalten, oder, um präziser zu sein, können nur DataMember Eigenschaften eines serialisierbaren Typs datengebunden sein.

Nur die folgenden Typen können von der Remote-UI serialisiert werden:

  • Primitivdatentypen (die meisten numerischen .NET-Typen, Enumerationen, bool, string, DateTime)
  • erweiterungsdefinierte Typen, die mit DataContract und DataMember Attributen gekennzeichnet sind, und deren alle Datenmitglieder ebenfalls serialisierbar sind
  • Objekte, die IAsyncCommand implementieren
  • XamlFragment- und SolidColorBrush-Objekte und Farbwerte
  • Nullable<> Werte für einen serialisierbaren Typ
  • Auflistungen serialisierbarer Typen, einschließlich beobachtbarer Auflistungen.

Lebenszyklus einer Remotebenutzersteuerung

Sie können die ControlLoadedAsync-Methode überschreiben, um benachrichtigt zu werden, wenn das Steuerelement zum ersten Mal in einem WPF-Container geladen wird. Wenn sich der Status des Datenkontexts in Ihrer Implementierung unabhängig von UI-Ereignissen ändern kann, ist die ControlLoadedAsync Methode der richtige Ort, um den Inhalt des Datenkontexts zu initialisieren und änderungen darauf anzuwenden.

Sie können die Dispose Methode auch überschreiben, um benachrichtigt zu werden, wenn die Steuerung zerstört wird und nicht mehr verwendet wird.

internal class MyToolWindowContent : RemoteUserControl
{
    public MyToolWindowContent()
        : base(dataContext: new MyToolWindowData())
    {
    }

    public override async Task ControlLoadedAsync(CancellationToken cancellationToken)
    {
        await base.ControlLoadedAsync(cancellationToken);
        // Your code here
    }

    protected override void Dispose(bool disposing)
    {
        // Your code here
        base.Dispose(disposing);
    }
}

Befehle, Beobachtbarkeit und bidirektionale Datenbindung

Als Nächstes machen wir den Datenkontext feststellbar und fügen der Toolbox eine Schaltfläche hinzu.

Der Datenkontext kann durch die Implementierung von INotifyPropertyChanged beobachtet werden. Alternativ bietet remote UI eine bequeme abstrakte Klasse, NotifyPropertyChangedObjectdie wir erweitern können, um Codebausteine zu reduzieren.

Ein Datenkontext verfügt in der Regel über eine Mischung aus readonly-Eigenschaften und feststellbaren Eigenschaften. Der Datenkontext kann ein komplexer Graph von Objekten sein, solange sie mit den DataContract und DataMember Attributen gekennzeichnet sind und INotifyPropertyChanged wie erforderlich implementieren. Es ist auch möglich, beobachtbare Sammlungen oder eine ObservableList<T> zu haben, das eine Erweiterung von ObservableCollection<T> ist, die von Remote UI bereitgestellt wird, um auch Bereichsoperationen zu unterstützen und eine bessere Leistung zu ermöglichen.

Außerdem müssen wir dem Datenkontext einen Befehl hinzufügen. In der Remote-Benutzeroberfläche implementiert IAsyncCommand Befehle, aber häufig ist es einfacher, eine Instanz der AsyncCommand Klasse zu erstellen.

IAsyncCommand unterscheidet sich von ICommand in zwei Punkten:

  • Die Execute Methode wird durch ExecuteAsync ersetzt, da alles in der Remote-UI asynchron ist!
  • Die CanExecute(object) Methode wird durch eine CanExecute Eigenschaft ersetzt. Die AsyncCommand-Klasse kümmert sich darum, CanExecute observierbar zu machen.

Es ist wichtig zu beachten, dass die Remote-Benutzeroberfläche keine Ereignishandler unterstützt. Daher müssen alle Benachrichtigungen von der Benutzeroberfläche an die Erweiterung über Datenbindung und Befehle implementiert werden.

Dies ist der resultierende Code für MyToolWindowData:

[DataContract]
internal class MyToolWindowData : NotifyPropertyChangedObject
{
    public MyToolWindowData()
    {
        HelloCommand = new((parameter, cancellationToken) =>
        {
            Text = $"Hello {Name}!";
            return Task.CompletedTask;
        });
    }

    private string _name = string.Empty;
    [DataMember]
    public string Name
    {
        get => _name;
        set => SetProperty(ref this._name, value);
    }

    private string _text = string.Empty;
    [DataMember]
    public string Text
    {
        get => _text;
        set => SetProperty(ref this._text, value);
    }

    [DataMember]
    public AsyncCommand HelloCommand { get; }
}

Korrigieren Sie den MyToolWindowContent Konstruktor:

public MyToolWindowContent()
    : base(dataContext: new MyToolWindowData())
{
}

Aktualisieren MyToolWindowContent.xaml, um die neuen Eigenschaften im Datenkontext zu verwenden. Dies ist alles normale WPF-XAML. Auch auf das IAsyncCommand Objekt wird über einen Proxy, der im Visual Studio-Prozess ICommand genannt wird, zugegriffen, sodass es wie gewohnt datengebunden werden kann.

<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml"
              xmlns:styles="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
              xmlns:colors="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0">
    <Grid>
        <Grid.Resources>
            <Style TargetType="Label" BasedOn="{StaticResource {x:Static styles:VsResourceKeys.ThemedDialogLabelStyleKey}}" />
            <Style TargetType="TextBox" BasedOn="{StaticResource {x:Static styles:VsResourceKeys.TextBoxStyleKey}}" />
            <Style TargetType="Button" BasedOn="{StaticResource {x:Static styles:VsResourceKeys.ButtonStyleKey}}" />
            <Style TargetType="TextBlock">
                <Setter Property="Foreground" Value="{DynamicResource {x:Static styles:VsBrushes.WindowTextKey}}" />
            </Style>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Label Content="Name:" />
        <TextBox Text="{Binding Name}" Grid.Column="1" />
        <Button Content="Say Hello" Command="{Binding HelloCommand}" Grid.Column="2" />
        <TextBlock Text="{Binding Text}" Grid.ColumnSpan="2" Grid.Row="1" />
    </Grid>
</DataTemplate>

Diagramm des Toolfensters mit bidirektionale Bindung und einem Befehl.

Grundlegendes zur Asynchronität in der Remote-UI

Die gesamte Remote-UI-Kommunikation für dieses Toolfenster führt die folgenden Schritte aus:

  1. Auf den Datenkontext wird über einen Proxy innerhalb des Visual Studio-Prozesses mit seinem ursprünglichen Inhalt zugegriffen,

  2. Das MyToolWindowContent.xaml erstellte Steuerelement ist an den Datenkontextproxy gebunden.

  3. Der Benutzer gibt Text in das Textfeld ein, der der Name Eigenschaft des Datenkontextproxys über die Datenbindung zugewiesen ist. Der neue Wert von Name wird auf das MyToolWindowData Objekt übertragen.

  4. Der Benutzer klickt auf die Schaltfläche, was eine Kaskade von Effekten auslöst.

    • Der HelloCommand wird im Datenkontext-Proxy ausgeführt.
    • Die asynchrone Ausführung des Erweiterungscodes AsyncCommand wird gestartet.
    • Der asynchrone Rückruf für HelloCommand aktualisiert den Wert der beobachtbaren Eigenschaft Text.
    • der neue Wert von Text wird an den Datenkontext-Proxy übermittelt
    • Der Textblock im Toolfenster wird durch die Datenbindung auf den neuen Wert von Text aktualisiert.

Diagramm der bidirektionale Bindung und Kommunikation von Befehlen im Toolfenster.

Verwenden von Befehlsparametern, um Rennbedingungen zu vermeiden

Alle Vorgänge, die die Kommunikation zwischen Visual Studio und der Erweiterung (blaue Pfeile im Diagramm) umfassen, sind asynchron. Es ist wichtig, diesen Aspekt im Gesamtentwurf der Erweiterung zu berücksichtigen.

Aus diesem Grund ist es besser, Befehlsparameter anstelle von bidirektionalem Binden zu verwenden, um den Datenkontextstatus zum Zeitpunkt der Ausführung eines Befehls abzurufen, wenn die Konsistenz wichtig ist.

Nehmen Sie diese Änderung vor, indem Sie die Schaltfläche CommandParameter an Namefolgendes binden:

<Button Content="Say Hello" Command="{Binding HelloCommand}" CommandParameter="{Binding Name}" Grid.Column="2" />

Ändern Sie dann den Rückruf des Befehls so, dass er den Parameter verwendet:

HelloCommand = new AsyncCommand((parameter, cancellationToken) =>
{
    Text = $"Hello {(string)parameter!}!";
    return Task.CompletedTask;
});

Bei diesem Ansatz wird der Wert der Name Eigenschaft synchron vom Datenkontextproxy zum Zeitpunkt des Klickens auf die Schaltfläche abgerufen und an die Erweiterung gesendet. Dies vermeidet rennbezogene Bedingungen, insbesondere, wenn der HelloCommand Rückruf in Zukunft geändert wird, um Ausbeute zu erzielen (ausdrücke haben await ).

Asynchrone Befehle nutzen Daten aus mehreren Eigenschaften

Die Verwendung eines Befehlsparameters ist keine Option, wenn der Befehl mehrere Eigenschaften nutzen muss, die vom Benutzer festgelegt werden können. Wenn beispielsweise auf der Benutzeroberfläche zwei Textfelder vorhanden sind: "Vorname" und "Nachname".

Die Lösung in diesem Fall besteht darin, im asynchronen Befehlsrückruf den Wert aller Eigenschaften aus dem Datenkontext abzurufen, bevor sie zurückgegeben werden.

Unten sehen Sie ein Beispiel, in dem die Werte der Eigenschaften FirstName und LastName abgerufen werden, bevor der Befehl ausgeführt wird, um sicherzustellen, dass der Wert zum Zeitpunkt des Befehlsaufrufs verwendet wird.

HelloCommand = new(async (parameter, cancellationToken) =>
{
    string firstName = FirstName;
    string lastName = LastName;
    await Task.Delay(TimeSpan.FromSeconds(1));
    Text = $"Hello {firstName} {lastName}!";
});

Es ist auch wichtig zu vermeiden, dass die Erweiterung den Wert der Eigenschaften asynchron aktualisiert, die Benutzer ebenfalls aktualisieren können. Mit anderen Worten: Vermeiden Sie die TwoWay-Datenbindung .

Die hier aufgeführten Informationen sollten ausreichen, um einfache Remote-UI-Komponenten zu erstellen. Weitere Themen zum Arbeiten mit dem Remote-UI-Modell finden Sie unter "Weitere Konzepte der Remote-UI". Weitere erweiterte Szenarien finden Sie unter Advanced Remote UI concepts.