Mai 2018

Volume 33, numéro 5

Cet article a fait l'objet d'une traduction automatique.

Plateforme Windows universelle - Créer des applications connectées à l'aide de la plateforme Windows universelle et de Project Rome

Par spécialiste de de

Aujourd'hui, la création d’une application réussie signifie au-delà d’une seule unité. Les utilisateurs souhaitent des applications qui s’étendent sur leurs appareils et même vous connecter avec d’autres utilisateurs. Ce type d’expérience peut être un défi, pour n’en citer. Pour répondre à ce besoin croissant de l’écosystème, Microsoft a introduit Rome du projet. Projet Rome vise à créer un système d’exploitation plus personnalisé qui s’étend sur des applications, des périphériques et des utilisateurs. Alors que Rome de projet a disponibles pour la plupart des principales plateformes kits de développement logiciel, dans cet article je vais Explorer à l’aide de Rome de projet pour créer une équipe de messagerie d’application de plateforme Windows universelle (UWP).

Découvrez Rome de projet

Project Rome est une initiative conçue pour vous aider à stimuler l'implication des utilisateurs sur tous types d'applications et d'appareils. C’est un ensemble d’API qui font partie de Microsoft Graph et peut être classés en deux zones : maintenant et poursuivre ultérieurement.

L’API du système distant activer interrompent au-delà des limites de l’appareil en cours de l’utilisateur, une application permettant une continuer-maintenant, consultez-les. Si autoriser l’utilisateur à utiliser deux unités pour une expérience unique, comme avec un accompagnement ou d’une application de contrôle à distance, ou autoriser plusieurs utilisateurs à se connecter et partager une expérience unique, ces API fournissent une vue développée de l’engagement de l’utilisateur actuel. L’équipe de messagerie application générée dans cet article créera une expérience utilisateur partagé.

L’autre moitié de projet Rome, les API d’activités, se concentre sur la poursuite de l’expérience utilisateur à une date ultérieure. Ces API permettre d’enregistrement et la récupération des actions spécifiques au sein de votre application, les utilisateurs peuvent continuer à partir de n’importe quel appareil. Bien que je ne les présentés dans cet article, ces valent la peine dans la recherche.

Mise en route

Avant de plonger dans la génération de l’application, vous devez d’abord configurer mon environnement. Alors que la première version de projet Rome a été pour un certain temps, certaines des fonctionnalités utilisées dans cet article ont été publiés uniquement pendant la mise à jour de créateurs récents comprises. Par conséquent, votre ordinateur doit être en cours d’exécution numéro de build 16299 ou supérieur. À ce stade, cette version est disponible dans l’anneau lent pour les mises à jour et la plupart des ordinateurs doivent être mis à jour correctement.

Exécution d’applications avec l’API du système distant avec d’autres utilisateurs requiert que les expériences partagées sont activés sur l’ordinateur. Cela est possible dans les paramètres système, dans paramètres | Système | Expériences partagées. Pour le scénario de messagerie de l’équipe, vous devez permettre aux utilisateurs de différents communiquer avec votre appareil, ce qui signifie que vous devez vous assurer partagé expériences est activé et que vous pouvez partager ou de réception à partir de « Tout le monde proche » comme indiqué dans Figure 1.

L’activation des expériences partagées
L’activation de la figure 1 partagé expériences

La spécification finale est que votre appareil soit détectable en ayant un certain degré de connectivité. L’API du système à distance détecte les autres ordinateurs sur le même réseau, ainsi que ceux à proximité à l’aide de Bluetooth. Bluetooth peut être activée dans la page « Bluetooth et d’autres paramètres de l’appareil » dans les paramètres de votre système.

Avec l’ordinateur est configuré, commençons par créer une application Visual c# à l’aide du modèle application vide (Windows universel) dans Visual Studio 2017. Appeler l’application « TeamMessenger ». Comme mentionné précédemment, ce projet requiert la mise à jour de créateurs automne, par conséquent, définissez les versions minimale et la cible de l’application vers « Build 16299 » ou supérieur, comme indiqué dans Figure 2. Cela empêche l’application prenant en charge les versions antérieures de Windows 10, mais il n’est nécessaire pour certaines des fonctionnalités abordées dans cet article.

Définir des Versions cible de l’application
Figure 2 paramètres ciblent des Versions de l’application

Notez que si vous n’avez pas les créateurs font partie de mise à jour des kits de développement logiciel sur votre appareil, le moyen le plus simple pour obtenir les est mise à jour de Visual Studio 2017 vers la dernière version.

Les API de Rome projet font partie du SDK Windows 10, ce qui signifie qu’il n’existe aucun SDK supplémentaires à télécharger, ou les packages NuGet pour installer pour pouvoir créer cette application. Toutefois, il existe quelques fonctionnalités qui doivent être ajoutées à l’application afin que la session à distance API fonctionne correctement. Cela est possible en ouvrant le fichier package.appxmanifest et en sélectionnant l’onglet capacités. Dans la liste des fonctionnalités disponibles, assurez-vous que les éléments suivants sont vérifiés : Bluetooth, la Internet (Client & serveur) et le système distant.

Création de la connexion de Session

Cette application se compose de deux pages, avec la première page responsable de la création ou de rejoindre une session à distance à l’aide de l’API du système distant. Par souci de simplicité, je vais créer cette page à l’aide de MainPage.xaml qui a été créé avec la solution est déjà câblé dans l’application soit la première page chargée. L’interface utilisateur comporte deux modes : création ou d’hébergement d’une session et de rejoindre une session existante. Création d’une session requiert un nom de session qui sera publique aux utilisateurs souhaitant pour participer. Rejoindre une session existante doit afficher une liste de disponibles à proximité de sessions. Les deux modes besoin d’un nom à afficher à l’utilisateur. Figure 3 montre ce que l’interface utilisateur qui en résulte doit ressembler à des MainPage et le code XAML pour générer cette page se trouvent dans Figure 4.

L’interface utilisateur MainPage
Figure 3 l’interface utilisateur MainPage

Figure 4 MainPage XAML

<Page
  x:Class="TeamMessenger.MainPage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:remotesystems="using:Windows.System.RemoteSystems"
  mc:Ignorable="d">
  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel Width="400"
                HorizontalAlignment="Center"
                BorderBrush="Gray"
                BorderThickness="1"
                MaxHeight="600"
                VerticalAlignment="Center"
                Padding="10">
    <RadioButton x:Name="rbCreate"
                GroupName="options"
                IsChecked="True"
                Checked="{x:Bind ViewModel.CreateSession}"
                Content="Create a New Session"/>
    <StackPanel Orientation="Horizontal" Margin="30,10,20,30">
      <TextBlock VerticalAlignment="Center">Session Name :</TextBlock>
      <TextBox Text="{x:Bind ViewModel.SessionName, Mode=TwoWay}"
               Width="200"
               Margin="20,0,0,0"/>
    </StackPanel>
    <RadioButton x:Name="rbJoin"
                GroupName="options"
                Checked="{x:Bind ViewModel.JoinSession}"
                Content="Join Session"/>
    <ListView ItemsSource="{x:Bind ViewModel.Sessions}"
              SelectedItem="{x:Bind ViewModel.SelectedSession, Mode=TwoWay}"
              IsItemClickEnabled="True"
              Height="200"
              BorderBrush="LightGray"
              BorderThickness="1"
              Margin="30,10,20,30">
      <ListView.ItemTemplate>
        <DataTemplate x:DataType="remotesystems:RemoteSystemSessionInfo">
          <TextBlock Text="{x:Bind DisplayName}"/>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
    <StackPanel Orientation="Horizontal">
      <TextBlock VerticalAlignment="Center">Name : </TextBlock>
      <TextBox Text="{x:Bind ViewModel.JoinName, Mode=TwoWay}"
               Width="200"
               Margin="20,0,0,0"/>
    </StackPanel>
    <Button Content="Start"
            Margin="0,30,0,0"
            Click="{x:Bind ViewModel.Start}"/>
    </StackPanel>
  </Grid>
</Page>

Après avoir créé le code XAML pour la page, créez un dossier ViewModel dans l’application, puis ajoutez une classe publique dans le dossier appelé MainViewModel.cs. Ce modèle d’affichage est associée dans la vue pour gérer les fonctionnalités.

La première partie du modèle de vue gère la gestion de l’état des boutons radio qui déterminent si l’utilisateur est en train de créer une nouvelle session ou joindre un existant. L’état est conservé dans une valeur booléenne appelée IsNewSession. Deux méthodes sont utilisées pour passer l’état de cette bool, CreateSession et JoinSession :

public bool IsNewSession { get; set; } = true;
public void CreateSession()
{
  IsNewSession = true;
}
public void JoinSession()
{
  IsNewSession = false;
}

L’événement activé pour chaque case d’option est lié à une de ces méthodes.

Les autres éléments d’interface utilisateur sont suivies avec des propriétés simples. Le nom d’utilisateur et le nom de la session sont liés aux propriétés nom de session et JoinName. La propriété SelectedSession lie à la propriété SelectedItem dans ListView et ItemsSource est liée à la propriété de Sessions :

public string JoinName { get; set; }
public string SessionName { get; set; }
public object SelectedSession { get; set; }
public ObservableCollection<
  RemoteSystemSessionInfo> Sessions { get; } =
  new —ObservableCollection<
  RemoteSystemSessionInfo>();

Le modèle d’affichage comporte deux événements qui seront utilisés pour indiquer la vue si une connexion à une session a réussi ou non :

public event EventHandler SessionConnected =
  delegate { };
public event EventHandler<SessionCreationResult> ErrorConnecting = delegate { };

Enfin, le bouton Démarrer est lié à une méthode de démarrage. Cette méthode peut être laissée vide pour l’instant.

Une fois le modèle terminé, le code-behind pour MainPage doit créer une propriété publique qui est une instance de la MainViewModel. C’est ce qui permet de x : Bind générer les liaisons de la compilation. En outre, il doit s’abonner aux deux événements créés dans le modèle d’affichage. Si une connexion est établie avec succès, vous allez accéder à une page nouvelle, MessagePage. Si la connexion échoue, un MessageDialog s’affichera, indiquant à l’utilisateur de la connexion a échoué. Figure 5 contient le code pour le MainPage.xaml.cs.

Le code-behind de la figure 5 MainPage.xaml

public sealed partial class MainPage : Page
{
  public MainPage()
  {
    this.InitializeComponent();
    ViewModel.SessionConnected += OnSessionConnected;
    ViewModel.ErrorConnecting += OnErrorConnecting;
  }
  private async void OnErrorConnecting(object sender, SessionCreationResult e)
  {
    var dialog = new MessageDialog("Error connecting to a session");
    await dialog.ShowAsync();
  }
  private void OnSessionConnected(object sender, EventArgs e)
  {
    Frame.Navigate(typeof(MessagePage));
  }
  public MainViewModel ViewModel { get; } = new MainViewModel();
}

Définition des modèles de données

Avant d’examiner le cœur de l’application, vous devez définir deux modèles de données qui sera utilisé dans l’application. Créez un dossier de modèles dans l’application, puis créez deux classes qu’il contient : L’utilisateur et UserMessage. Comme son nom l’indique, le modèle utilisateur effectuera le suivi des informations sur les utilisateurs connectés à l’application :

public class User
{
  public string Id { get; set; }
  public string DisplayName { get; set; }
}

La classe UserMessage contiendra le message contenu, l’utilisateur qui crée le contenu et lorsque le message a été créé :

public class UserMessage
{
  public User User { get; set; }
  public string Message { get; set; }
  public DateTime DateTimeStamp { get; set; }
}

Création d’une Session

Avec le code de la page principale relativement complet, commencer la création de RemoteSessionManager, qui sera utilisé pour encapsuler l’API de systèmes à distance. Ajoutez une classe publique nommée RemoteSessionManager dans le répertoire racine de l’application. L’application utilise une seule instance partagée de la RemoteSessionManager, vous pouvez donc ajouter une propriété statique pour la classe App dans App.xaml.cs :

public static RemoteSessionManager SessionManager { get; } = new RemoteSessionManager();

Avant de l’application peut accéder à toutes les API de systèmes à distance, il doit tout d’abord obtenir l’autorisation de l’utilisateur. Cette autorisation est obtenue en appelant la méthode statique RemoteSystem.RequestAccessAsync :

RemoteSystemAccessStatus accessStatus = 
  await RemoteSystem.RequestAccessAsync();
if (accessStatus != RemoteSystemAccessStatus.Allowed)
{
  // Access is denied, shortcut workflow
}

La méthode retourne une énumération RemoteSystemAccessStatus qui peut être utilisée pour déterminer si l’accès a été accordé. Cette méthode doit être appelée à partir du thread d’interface utilisateur afin qu’il peut demander avec succès de l’utilisateur. Une fois que l’utilisateur a accordé ou refusé des autorisations à l’application, tous les appels suivants retourne automatiquement les préférences de l’utilisateur. Pour cette application, cette autorisation sera ajoutée à la découverte de la session, car elle est appelée en premier dans le flux de travail.

Notez que toutes les API du système à distance peuvent être trouvés dans l’espace de noms Windows.System.RemoteSystem.

La première méthode pour ajouter à la classe RemoteSessionManager est la méthode CreateSession. Étant donné que plusieurs résultats qui peuvent être retournées à partir de cette méthode, allez conclure ceux dans un nouvel enum : SessionCreationResult. SessionCreationResult a quatre valeurs possibles : succès et échecs différents trois. Une session peut échouer créer, car l’utilisateur n’a pas accordé l’accès à l’application ; l’application n’a actuellement trop de sessions en cours d’exécution ; ou une erreur système Impossible de créer la session :

public enum SessionCreationResult
{
  Success,
  PermissionError,
  TooManySessions,
  Failure
}

Sessions à distance sont gérées par un RemoteSystemSessionController. Lorsque vous créez une nouvelle instance de RemoteSystemSessionController, vous devez passer un nom qui sera affiché pour les appareils tentent de rejoindre la session.

Une fois que le contrôleur est demandé, une session peut être démarrée en appelant la méthode CreateSession. Cette méthode retourne un RemoteSystemSessionCreationResult contenant un état et la nouvelle instance de la session si elle a réussi. La RemoteSessionManager stocke le nouveau contrôleur et la session dans des variables privées.

Une nouvelle propriété publique, IsHost, doit être ajoutée au gestionnaire, ainsi que pour déterminer le flux de travail. Au cours de la méthode CreateSession, cette valeur est définie sur true, identifier cette application en tant que l’ordinateur hôte. Une autre propriété publique, CurrentUser, fournit une instance de l’utilisateur sur l’ordinateur et sera utilisée pour la messagerie. Le Gestionnaire de session gère également ObservableCollection d’utilisateurs dans la session active. Cette collection est initialisée avec l’utilisateur nouvellement créé. Pour l’hôte de la session, cette instance est créée dans la méthode CreateSession. Les ajouts résultants de la RemoteSessionManager figurent dans Figure 6.

Figure 6 le CreateSession (méthode)

private RemoteSystemSessionController _controller;
private RemoteSystemSession _currentSession;
public bool IsHost { get; private set; }
public User CurrentUser { get; private set; }
public ObservableCollection<User> Users { get; } =
  new ObservableCollection<User>();
public async Task<SessionCreationResult> CreateSession(
  string sessionName, string displayName)
{
  SessionCreationResult status = SessionCreationResult.Success;
  RemoteSystemAccessStatus accessStatus = await RemoteSystem.RequestAccessAsync();
  if (accessStatus != RemoteSystemAccessStatus.Allowed)
  {
    return SessionCreationResult.PermissionError;
  }
  if (_controller == null)
  {
    _controller = new RemoteSystemSessionController(sessionName);
    _controller.JoinRequested += OnJoinRequested;
  }
  RemoteSystemSessionCreationResult createResult =
    await _controller.CreateSessionAsync();
  if (createResult.Status == RemoteSystemSessionCreationStatus.Success)
  {
    _currentSession = createResult.Session;
    InitParticipantWatcher();
    CurrentUser = new User() { Id = _currentSession.ControllerDisplayName,
      DisplayName = displayName };
    Users.Add(CurrentUser);
    IsHost = true;
  }
  else if(createResult.Status ==
    RemoteSystemSessionCreationStatus.SessionLimitsExceeded)
  {
    status = SessionCreationResult.TooManySessions;
  } else
  {
    status = SessionCreationResult.Failure;
  }
  return status;
}

Il existe trois éléments supplémentaires qui doivent être ajoutés à RemoteSessionManager pour compléter la méthode CreateSession. Le premier est un gestionnaire d’événements lorsqu’un utilisateur tente de rejoindre une session et l’événement JoinRequested est déclenché sur la session. La méthode OnJoinRequested accepte automatiquement tout utilisateur qui tente de joindre. Cela peut être étendue pour demander à l’hôte de réception avant que l’utilisateur est joint à la session. Les informations de demande sont fournies comme un RemoteSystemSessionJoinRequest inclus dans le paramètre RemoteSystemSessionJoinRequestedEventArgs du Gestionnaire d’événements. Appel de la méthode Accept ajoutera l’utilisateur à la session. Le code suivant inclut le nouvel événement à ajouter à RemoteSessionManager, ainsi que la méthode OnJoinRequested exécutée :

private void OnJoinRequested(RemoteSystemSessionController sender,
  RemoteSystemSessionJoinRequestedEventArgs args)
{
  var deferral = args.GetDeferral();
  args.JoinRequest.Accept();
  deferral.Complete();
}

Le Gestionnaire de session peut surveiller lorsque les participants sont ajoutés ou supprimés de la session actuelle via la RemoteSystemSessionParticipantWatcher. Cette classe surveille les participants et déclenche une ajouté ou supprimé des événements si nécessaire. Lorsqu’un utilisateur joint une session déjà en cours d’exécution, chaque participant déjà dans la session active reçoit un événement ajouté. L’application prend cette série d’événements et déterminer quel participant est l’ordinateur hôte en mettant en correspondance par rapport à la session ControllerDisplayName DisplayName. Cela permettra aux participants de communiquer directement avec l’hôte. Le Gestionnaire de session conserve un observateur participant comme une variable privée qui est initialisée dans le InitParticipantWatcher. Cette méthode est appelée si la création d’une session ou de rejoindre une session existante. Figure 7 contient les nouveaux ajouts. Vous remarquerez que pour ce flux de travail que vous devez savoir lorsqu’un participant est supprimé uniquement si vous êtes l’hôte, et si un participant est ajouté si vous êtes rejoindre une session. En tant qu’hôte, le RemoteSessionManager concerne uniquement lorsqu’un participant quitte la session. L’hôte sera notifié directement par le participant lorsqu’ils se connectent, comme vous le verrez plus loin dans l’article. Les participants doivent uniquement déterminer le compte d’ordinateur hôte en cours.

Figure 7 InitParticipantWatcher

private RemoteSystemSessionParticipantWatcher _participantWatcher;
private void InitParticipantWatcher()
{
  _participantWatcher = _currentSession.CreateParticipantWatcher();
  if (IsHost)
  {
    _participantWatcher.Removed += OnParticipantRemoved;
  }
  else
  {
    _participantWatcher.Added += OnParticipantAdded;
  }
  _participantWatcher.Start();
}
private void OnParticipantAdded(RemoteSystemSessionParticipantWatcher watcher,
  RemoteSystemSessionParticipantAddedEventArgs args)
{
  if(args.Participant.RemoteSystem.DisplayName ==
    _currentSession.ControllerDisplayName)
  {
    Host = args.Participant;
  }
}
private async void OnParticipantRemoved(RemoteSystemSessionParticipantWatcher watcher,
  RemoteSystemSessionParticipantRemovedEventArgs args)
{
  var qry = Users.Where(u => u.Id == args.Participant.RemoteSystem.DisplayName);
  if (qry.Count() > 0)
  {
    var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
    await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High,
      () => { Users.Remove(qry.First()); });
    await BroadCastMessage("users", Users);
  }
}

La dernière chose que la méthode CreateSession doit ajoutée à la classe est un événement d’informer les consommateurs si et quand la session est déconnectée. Le nouvel événement SessionDisconnected peut être défini comme suit :

public event EventHandler<RemoteSystemSessionDisconnectedEventArgs> SessionDisconnected =
  delegate { };

Rejoindre une Session

Maintenant que l’application est en mesure de diffuser une nouvelle session à distance, le prochain élément à implémenter est la possibilité de joindre cette session à partir d’un autre ordinateur. Rejoindre une session à distance en deux étapes : découverte et la connexion à la session.

Détection d’une Session une application est en mesure de détecter les proches des sessions à distance via le RemoteSystemSessionWatcher, qui est créé à partir de la méthode CreateWatcher statique. Cette classe déclenche des événements chaque fois qu’une session est ajoutée ou supprimée. Ajouter une nouvelle méthode — DiscoverSessions — à le RemoteSessionManager. Cette méthode crée un RemoteSystemSessionWatcher comme une variable privée à la classe et gérer les événements ajouté et supprimé. Ces événements sont encapsulés par deux événements de nouveau ajoutés à RemoteSessionManager : SessionAdded et SessionRemoved. Car il s’agit d’un autre point d’entrée pour les utilisateurs de l’initialisation des sessions à distance, vous devez vous assurer d’ajouter un appel à RemoteSystem.RequestAccessAsync. Figure 8 contient la variable privée, les deux événements et la méthode DiscoverSessions complète.

Figure 8 de la découverte des Sessions

private RemoteSystemSessionWatcher _watcher;
public event EventHandler<RemoteSystemSessionInfo> SessionAdded = delegate { };
public event EventHandler<RemoteSystemSessionInfo> SessionRemoved = delegate { };
public async Task<bool> DiscoverSessions()
{
  RemoteSystemAccessStatus status = await RemoteSystem.RequestAccessAsync();
  if (status != RemoteSystemAccessStatus.Allowed)
  {
    return false;
  }
  _watcher = RemoteSystemSession.CreateWatcher();
  _watcher.Added += (sender, args) =>
  {
    SessionAdded(sender, args.SessionInfo);
  };
  _watcher.Removed += (sender, args) =>
  {
    SessionRemoved(sender, args.SessionInfo);
  };
  _watcher.Start();
  return true;
}

Il est désormais possible d’ajouter le MainViewModel pour mettre à jour la propriété de Sessions avec les sessions disponibles localement. Étant donné que la méthode DiscoverSessions est asynchrone, le constructeur de la MainViewModel doit initialiser une tâche pour l’invoquer. La méthode d’initialisation doit également inscrire et gérer les événements SessionAdded et SessionRemoved. Car ces événements ne se déclencher sur le thread d’interface utilisateur lors de la mise à jour de la propriété Sessions, il est important d’utiliser un CoreDispatcher. Les mises à jour MainViewModel se trouvent dans Figure 9.

Figure 9 Ajout d’une détection de sessions

public MainViewModel()
{
  _initSessionManager = InitSessionManager();
}
private Task _initSessionManager;
private async Task InitSessionManager()
{
  App.SessionManager.SessionAdded += OnSessionAdded;
  App.SessionManager.SessionRemoved += OnSessionRemoved;
  await App.SessionManager.DiscoverSessions();
}
private async void OnSessionAdded(object sender, RemoteSystemSessionInfo e)
{
  var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
  await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High,
    () => { Sessions.Add(e); });
}
private async void OnSessionRemoved(object sender, RemoteSystemSessionInfo e)
{
  if (Sessions.Contains(e))
  {
    var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
    await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High,
      () => { Sessions.Remove(e); });
  }
}

Connexion à la Session une fois qu’un utilisateur a identifié la session de leur choix joindre et fournit un nom, la RemoteSessionManager doit être en mesure de les connecter à la session sélectionnée. Ceci sera traité par une nouvelle méthode JoinSession sur RemoteSessionManager, ce qui prend la session sélectionnée et le nom d’affichage entré en tant que paramètres.

La méthode JoinSession commence en appelant la méthode JoinAsync sur la session fournie. En retour, cela déclenche l’événement JoinRequested sur la session hôte. Si l’hôte approuve la demande, un état de réussite est retourné et le CurrentUser est défini à l’aide du nom d’affichage. Comme avec la méthode CreateSession, la méthode InitParticipantWatcher est appelée pour inscrire un gestionnaire d’événements lorsque les participants sont ajoutés à la session. La méthode JoinSession est affichée en Figure 10.

Figure 10 la méthode JoinSession

public async Task<bool> JoinSession(RemoteSystemSessionInfo session, string name)
{
  bool status = true;
  RemoteSystemSessionJoinResult joinResult = await session.JoinAsync();
  if (joinResult.Status == RemoteSystemSessionJoinStatus.Success)
  {
    _currentSession = joinResult.Session;
    CurrentUser = new User() { DisplayName = name };
  }
  else
  {
    status = false;
  }
  InitParticipantWatcher();
  return status;
}

La dernière étape de rejoindre une session doit utiliser la fonctionnalité créée dans le RemoteSessionManager pour créer ou rejoindre une session. Figure 11 montre la méthode Start dans le MainViewModel est lié sur le bouton Démarrer dans MainPage. Le flux de travail de la méthode est simple. Selon IsNewSession, il appelle la méthode CreateSession ou la méthode JoinSession. Les résultats sont retournés par le déclenchement d’événements ErrorConnecting ou SessionConnected. Si la session a réussi, l’application accède à la MessagePage, lequel je vais créer dans la section suivante.

Figure 11 démarrage d’une Session

public async void Start()
{
  if(IsNewSession)
  {
    var result = await App.SessionManager.CreateSession(SessionName, JoinName);
    if(result == SessionCreationResult.Success)
    {
      SessionConnected(this, null);
    } else
    {
      ErrorConnecting(this, result);
    }
  } else
  {
    if(SelectedSession != null)
    {
      var result = await App.SessionManager.JoinSession(
        SelectedSession as RemoteSystemSessionInfo, JoinName);
      if(result)
      {
        SessionConnected(this, null);
      } else
      {
        ErrorConnecting(this, SessionCreationResult.Failure);
      }
    }
  }
}

Conserver les applications de communiquer avec

À ce stade, l’application peut créer ou joindre une session avec succès et a une interface utilisateur de messagerie est prête à être utilisée. La seule étape restante consiste à permettre les périphériques à communiquer entre eux. Cela est accompli en utilisant l’API du système distant pour envoyer des instances ValueSet entre les ordinateurs. Chaque ValueSet est un ensemble de paires clé/valeur de charges utiles sérialisés.

Réception de Messages les Messages sont transmis au sein d’une session via un RemoteSystemSessionMessageChannel. Une session peut avoir plusieurs canaux ; Toutefois, cette application a besoin d’une seule couche. Dans la RemoteSessionManager, je vais ajouter une méthode StartReceivingMessages. Cette méthode crée un nouveau canal de message qui est stocké dans une variable privée et ajoutez un gestionnaire pour l’événement ValueSetReceived.

Les messages sont envoyés sous forme de texte, et parce que l’application est à l’aide de classes en tant que messages, j’ai besoin sérialiser les données. Lorsque le ValueSet est reçu du canal, un DataContractJsonSerializer est utilisé pour réalimenter les classes de message dans la classe DeserializeMessage. Étant donné que je suis incapable d’indiquer le type de message est sérialisé, l’application envoie chaque type de message comme une valeur différente dans le ValueSet. La classe DeserializeMessage se déterminer quelle clé est utilisée et la classe appropriée.

Une fois que la classe de message est prête, la classe de gestionnaire agira sur le message en fonction de son type. Comme vous le verrez, participants annoncer eux-mêmes à l’hôte en envoyant leur instance CurrentUser. En réponse, l’hôte est diffusés à la liste des utilisateurs mis à jour à tous les participants. Si le Gestionnaire de session reçoit la liste des participants, il met à jour les regroupements d’utilisateurs avec les données mises à jour. La dernière option, un UserMessage, génère un nouvel événement MessageReceived qui transmet le message et le participant qui a envoyé le message. Vous trouverez ces ajouts à la RemoteSessionManager dans Figure 12.

Figure 12 réception de Messages

private RemoteSystemSessionMessageChannel _messageChannel;
public event EventHandler<MessageReceivedEventArgs> MessageReceived = delegate { };
public void StartReceivingMessages()
{
  _messageChannel = new RemoteSystemSessionMessageChannel(_currentSession, "OpenChannel");
  _messageChannel.ValueSetReceived += OnValueSetReceived;
}
private object DeserializeMessage(ValueSet valueSet)
{
  Type serialType;
  object data;
   if(valueSet.ContainsKey("user"))
   {
    serialType = typeof(User);
    data = valueSet["user"];
  } else if (valueSet.ContainsKey("users"))
  {
    serialType = typeof(List<User>);
    data = valueSet["users"];
  } else
  {
    serialType = typeof(UserMessage);
    data = valueSet["message"];
  }
  object value;
  using (var stream = new MemoryStream((byte[])data))
  {
    value = new DataContractJsonSerializer(serialType).ReadObject(stream);
  }
  return value;
}
private async void OnValueSetReceived(RemoteSystemSessionMessageChannel sender,
  RemoteSystemSessionValueSetReceivedEventArgs args)
{
  var data = DeserializeMessage(args.Message);
  if (data is User)
  {
    var user = data as User;
    user.Id = args.Sender.RemoteSystem.DisplayName;
    if (!Users.Contains(user))
    {
      var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
      await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High,
        () => { Users.Add(user); });
    }
    await BroadcastMessage("users", Users.ToList());
  }
  else if (data is List<User>)
  {
    var users = data as List<User>;
    Users.Clear();
    foreach(var user in users)
    {
      Users.Add(user);
    }
  }
  else
  {
    MessageReceived(this, new MessageReceivedEventArgs()
    {
      Participant = args.Sender,
      Message = data
    });
  }
}

Figure 12 inclut une nouvelle classe gestionnaire d’événements, MessageReceivedEventArgs, qui doit également être créé. Cette classe contient deux propriétés : l’expéditeur et le message :

public class MessageReceivedEventArgs
{
  public RemoteSystemSessionParticipant Participant { get; set; }
  public object Message { get; set; }
}

Envoi de Messages l’API de systèmes à distance propose deux méthodes pour la remise des messages à d’autres utilisateurs. La première consiste à diffuser un message à tous les utilisateurs dans la session. Cette approche est utilisée pour deux de ses types de messages, le UserMessage et la liste des utilisateurs. Nous allons créer une nouvelle méthode, BroadcastMessage, dans le RemoteSystemManager. Cette méthode prend une clé et le message en tant que paramètres. À l’aide d’un DataContractJsonSerializer, sérialiser les données et utilisez la méthode BroadcastValueSetAsync pour envoyer le message à tous les utilisateurs, comme dans Figure 13.

Figure 13 diffuse un Message

public async Task<bool> BroadcastMessage(string key, object message)
{
  using (var stream = new MemoryStream())
  {
    new DataContractJsonSerializer(message.GetType()).WriteObject(stream, message);
    byte[] data = stream.ToArray();
    ValueSet msg = new ValueSet();
    msg.Add(key, data);
    await _messageChannel.BroadcastValueSetAsync(msg);
  }
  return true;
}

La deuxième consiste à envoyer un message à un seul participant. Cette approche est semblable à la diffusion d’un message, sauf qu’elle utilise la méthode SendValueSetAsync à un participant de message directement. Cette méthode finale à la RemoteSystemManager, SendMessage, peut être trouvée dans Figure 14.

La figure 14 envoi d’un Message Direct

public async Task<bool> SendMessage(string key, 
  object message, 
  RemoteSystemSessionParticipant participant)
{
  using (var stream = new MemoryStream())
  {
    new DataContractJsonSerializer(message.GetType()).WriteObject(stream, message);
    byte[] data = stream.ToArray();
    ValueSet msg = new ValueSet();
    msg.Add(key, data);
    await _messageChannel.SendValueSetAsync(msg, participant);
  }
  return true;
}

Construction de la Page de messagerie

Avec la messagerie maintenant en place, il est temps de le placer à utiliser et la fin de l’application. Ajoutez une nouvelle Page vierge à l’application, MessagePage.xaml. Cette page se compose d’une liste d’utilisateurs, une fenêtre de message et des champs d’entrée pour l’ajout d’un message. Le code XAML complète se trouvent dans Figure 15.

Figure 15 MessagePage XAML

<Page
  x:Class="TeamMessenger.MessagePage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:TeamMessenger"
  xmlns:models="using:TeamMessenger.Models"
  xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:remotesystems="using:Windows.System.RemoteSystems"
  mc:Ignorable="d">
  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.ColumnDefinitions>
      <ColumnDefinition MinWidth="200" Width="Auto"/>
      <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid VerticalAlignment="Stretch"
          BorderBrush="Gray" BorderThickness="0,0,1,0">
      <ListView ItemsSource="{x:Bind ViewModel.Users}">
        <ListView.ItemTemplate>
          <DataTemplate x:DataType="models:User">
            <TextBlock Height="25"
                       FontSize="16"
                       Text="{x:Bind DisplayName}"/>
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
    </Grid>
    <Grid Grid.Column="1" Margin="10,0,10,0">
      <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
      </Grid.RowDefinitions>
      <ListView x:Name="lvMessages" ItemsSource="{x:Bind ViewModel.Messages}">
        <ListView.ItemTemplate>
          <DataTemplate x:DataType="models:UserMessage">
            <StackPanel Orientation="Vertical"
                        Margin="10,20,10,5">
              <TextBlock TextWrapping="WrapWholeWords"
                         Height="Auto"
                         Text="{x:Bind Message}"/>
              <StackPanel Orientation="Horizontal"
                          Margin="20,5,0,0">
                <TextBlock Text="{x:Bind User.DisplayName}"
                           FontSize="12"
                           Foreground="Gray"/>
                <TextBlock Text="{x:Bind DateTimeStamp}"
                           Margin="20,0,0,0"
                           FontSize="12"
                           Foreground="Gray"/>
              </StackPanel>
            </StackPanel>
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
      <Grid Grid.Row="1" Height="60"
            Background="LightGray">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="*"/>
          <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <TextBox Text="{x:Bind ViewModel.NewMessage, Mode=TwoWay}"
                 Margin="10"/>
        <Button Grid.Column="1" Content="Send"
                Click="{x:Bind ViewModel.SubmitMessage}"
                Margin="10"/>
      </Grid>
    </Grid>
  </Grid>
</Page>

MessagePage devez comme MainPage, un modèle d’affichage. Ajoutez une nouvelle classe, MessageViewModel, dans le dossier ViewModel. Ce modèle d’affichage doit prendre en charge INotifyPropertyChanged pour permettre la liaison bidirectionnelle fonctionner correctement. Ce modèle d’affichage contient trois propriétés : Les utilisateurs, des Messages et NewMessage. Les utilisateurs exposera simplement regroupement d’utilisateurs de la RemoteSessionManager à la vue. Les messages seront ObservableCollection d’objets UserMessage reçu et une chaîne de NewMessage contenant le texte à envoyer en tant que nouveau message. Il existe également un événement unique, MessageAdded, qui sera utilisé par le code-behind dans MessagePage. Dans le constructeur du modèle de vue, ai-je besoin mapper la propriété d’utilisateurs, appeler la méthode StartReceivingMessages en RemoteSessionManager et inscrire pour l’événement MessageReceived, comme indiqué dans Figure 16. Le constructeur inclut également l’implémentation de INotifiyPropertyChanged.

Figure 16 MessageViewModel constructeur

public event PropertyChangedEventHandler PropertyChanged = delegate { };
public event EventHandler MessageAdded = delegate { };
public ObservableCollection<UserMessage> Messages { get; private set; }
public ObservableCollection<User> Users { get; private set; }
private string _newMessage;
public string NewMessage {
  get { return _newMessage; }
  set
  {
    _newMessage = value;
    PropertyChanged(this, new
    PropertyChangedEventArgs(nameof(NewMessage)));
  }
}
public MessageViewModel()
{
  Users = App.SessionManager.Users;
  Messages = new ObservableCollection<UserMessage>();
  App.SessionManager.StartReceivingMessages();
  App.SessionManager.MessageReceived += OnMessageRecieved;
  RegisterUser();
}

Un appel à RegisterUser est dans le constructeur. Cette méthode envoie le CurrentUser a été créé lors de la jointure d’une session à l’ordinateur hôte. Cette annonce à l’hôte un nouvel utilisateur a joint et le nom d’affichage. En réponse, l’hôte envoie la liste actuelle des utilisateurs à afficher dans l’application :

private async void RegisterUser()
{
  if(!App.SessionManager.IsHost)
    await App.SessionManager.SendMessage("user", App.SessionManager.CurrentUser,
                                                 App.SessionManager.Host);
}

La dernière partie du modèle d’affichage est pour diffuser un message à partir de l’utilisateur. La méthode SubmitMessage construit une nouvelle UserMessage et appelle la méthode BroadcastMessage sur le RemoteSessionManager. Il supprime la valeur NewMessage et déclenche l’événement MessageAdded, comme indiqué dans Figure 17.

Figure 17 soumission d’un Message

public async void SubmitMessage()
{
  var msg = new UserMessage()
  {
    User = App.SessionManager.CurrentUser,
    DateTimeStamp = DateTime.Now,
    Message = NewMessage
  };
  await App.SessionManager.BroadcastMessage("message", msg);
  Messages.Add(msg);
  NewMessage = "";
  MessageAdded(this, null);
}

Dans le code-behind pour MessagePage, illustré Figure 18, j’ai besoin de faire deux choses : créer une instance de la MessageViewModel pour le code XAML référencer et gérer l’événement MessageAdded. Dans l’événement Gestionnaire I, demandez à ListView pour faire défiler vers le bas de la liste où le dernier message est visible.

Le code-behind de la figure 18 MessagePage

public sealed partial class MessagePage : Page
{
  public MessagePage()
  {
    this.InitializeComponent();
    ViewModel.MessageAdded += OnMessageAdded;
  }
  private void OnMessageAdded(object sender, EventArgs e)
  {
    lvMessages.ScrollIntoView(ViewModel.Messages.Last());
  }
  public MessageViewModel ViewModel { get; } = new MessageViewModel();
}

L’application de messagerie de l’équipe doit maintenant être prête à s’exécuter. Sur un ordinateur, exécutez l’application et créer une nouvelle session. Puis lancez l’application sur un deuxième ordinateur, ce qui doit s’afficher le message nouvellement créé. Une fois que vous rejoindre la session que vous allez dirigé vers la nouvelle page de message où vous pouvez commencer la conversation avec d’autres utilisateurs dans la session, comme indiqué dans Figure 19. Vous venez de créer une application multi-utilisateur à l’aide de l’API du système distant.

Messagerie multi-utilisateur
Figure 19 multi-utilisateur messagerie

Pour résumer

Création d’expériences réussie de l’utilisateur dans les applications souvent requiert au-delà d’une seule unité ou la plateforme ou le même utilisateur. Microsoft a développé Rome de projet pour permettre aux développeurs de fournir ce niveau d’expérience dans leurs applications. Dans cet article, j’ai créé une application UWP à l’aide de l’API de systèmes à distance ; Toutefois, en utilisant le projet Rome kits de développement logiciel disponible pour d’autres plateformes, vous pouviez étendre cette application pour travailler sur plusieurs plateformes. Lors de la création de l’expérience très suivant pour vos utilisateurs, n’oubliez pas à prendre en compte les comment Rome de projet peut vous aider à rendre votre application plus personnel. Le code source pour cet article, consultez bit.ly/2FWtCc5.


Tony Championest architecte logiciel avec plus de 20 ans d’expérience en développement avec les technologies Microsoft. En tant que directeur de spécialiste DS et son architecte responsable, il reste actif dans les tendances et les dernières technologies, en créer des solutions personnalisées sur les plateformes Microsoft. Sa liste de clients sont réparties entre plusieurs secteurs d’activité et inclut les sociétés telles que : Schlumberger, Microsoft, Boeing, MLB et Chevron/Philips. Champion est un participant actif dans la Communauté comme blogueur, haut-parleur international, auteur publié et six ans MVP Microsoft.

Grâce à l’expert technique Microsoft suivant ayant révisé cet article : Shawn Henry


Discussion sur cet article sur le forum MSDN Magazine