Maggio 2018

Volume 33 Numero 5

Il presente articolo è stato tradotto automaticamente.

Piattaforma UWP - Creazione di app connesse con la piattaforma UWP e Project Rome

Dal campione Tony

Oggi, la creazione di un'app di esito positivo indica invece il passaggio oltre un singolo dispositivo. Gli utenti desiderano App che si estendono su tutti i dispositivi e si connettono anche con altri utenti. Fornire questo tipo di esperienza può rivelarsi scoraggiante, a dir. Per facilitare indirizzo questa esigenza in crescita all'interno dell'ecosistema, Microsoft ha introdotto progetto Roma. Progetto Roma mira a creare un sistema operativo più personale che si estende su App, i dispositivi e utenti. Mentre Roma progetto ha SDK disponibile per le piattaforme dei principali, in questo articolo verrà per esplorare l'utilizzo di Roma progetto per creare un team messaggistica app della piattaforma UWP (Universal Windows).

Ecco Roma progetto

Project Rome è un'iniziativa utile per favorire il coinvolgimento degli utenti in app e dispositivi. È una raccolta di API che fanno parte di Microsoft Graph e possono essere suddivise in due aree: ora e continuare in un secondo momento.

Le API di sistema remoto abilita un'app interruzione oltre i limiti del dispositivo corrente dell'utente, l'abilitazione di una continuazione-ora esperienza. Se consentire all'utente di usare due dispositivi per un'esperienza unica, con un complementare o un'app di controllo remoto o da più utenti di connettersi e condividere un'esperienza unica, le API forniscono una visualizzazione espansa di engagement corrente dell'utente. Il team di messaggistica app creata in questo articolo verrà creata un'esperienza utente condiviso.

L'altra metà di Roma progetto, le API di attività, è incentrata sulla continua l'esperienza dell'utente in un secondo momento. Queste API consentono di registrare e recuperare le azioni specifiche all'interno dell'app che l'utente può continuare da qualsiasi dispositivo. Se non esaminarli in questo articolo, si tratta decisamente interessanti in.

Per iniziare

Prima di entrare nel compilazione dell'applicazione, è innanzitutto necessario impostare mio ambiente. Mentre la prima versione del progetto Roma è rimasto per qualche istante, alcune delle funzionalità usate in questo articolo sono state rilasciate solo durante l'aggiornamento di creatori autunno recente. Pertanto, il computer deve eseguire numero di build 16299 o superiore. A questo punto, questa versione è disponibile nell'anello lento per gli aggiornamenti e la maggior parte dei computer devono essere aggiornati correttamente.

Le applicazioni in esecuzione con le API di sistema remoto con altri utenti richiede l'abilitazione di esperienze condivise nel computer. Questa operazione può essere eseguita nelle impostazioni del sistema, nelle impostazioni | Sistema | Esperienze condivise. Per lo scenario di messaggistica team, è necessario consentire agli utenti diversi comunicare con il dispositivo, vale a dire che è necessario assicurarsi che condiviso esperienze è abilitato e che è possibile condividere o ricevere da "Everyone nelle vicinanze," come mostrato nel figura 1.

L'abilitazione di esperienze condivise
Figura 1 l'abilitazione di esperienze condivisi

Il requisito finale è che il dispositivo sia individuabile richiedendo a un certo livello di connettività. Le API di sistema remoto verranno individuati da altri computer nella stessa rete, nonché a quelle nelle vicinanze tramite Bluetooth. Bluetooth può essere abilitato nella pagina "Bluetooth e altre impostazioni del dispositivo" nelle impostazioni del sistema.

Con la macchina è impostata, si inizia creando una nuova app Visual c# usando il modello applicazione vuota (Windows universale) in Visual Studio 2017. Chiamare l'app "TeamMessenger". Come accennato in precedenza, questo progetto richiede l'aggiornamento di creatori autunno, quindi impostare le versioni minima e di destinazione dell'app a "Compilazione 16299" o versione successiva, come illustrato figura 2. Ciò impedirà l'app che supporta le versioni precedenti di Windows 10, ma è necessario per alcune delle funzionalità modificate questo articolo.

Impostazione destinate alle versioni per l'App
Figura 2 impostazione destinate alle versioni per l'App

Si noti che, se non si dispone i creatori autunno aggiornare SDK nel dispositivo, il modo più semplice per ottenerle consiste nell'aggiornare Visual Studio 2017 alla versione più recente.

Le API di Roma progetto fanno parte del Windows 10 SDK, che significa che non esistono Nessun SDK aggiuntivo per il download o pacchetti NuGet per l'installazione per compilare l'app. Vi sono, tuttavia, alcune funzionalità che deve essere aggiunto all'app in ordine per la sessione remota API per funzionare correttamente. Questa operazione può essere eseguita aprendo il file package appxmanifest e selezionando la scheda delle funzionalità. Nell'elenco delle funzionalità disponibili, verificare che siano selezionati le seguenti: Bluetooth, Internet (Client e Server) e sul sistema remoto.

La connessione della sessione di compilazione

Questa app sarà costituito da due pagine, con la prima pagina responsabile per la creazione o l'aggiunta di una sessione remota utilizzando le API di sistema remoto. Per semplicità, verrà creato questa pagina con il file MainPage. XAML è stato creato con la soluzione che è già collegato nell'applicazione per la prima pagina caricata. L'interfaccia utente è disponibili due modalità: la creazione o che ospita una sessione e join di una sessione esistente. Creazione di una sessione richiede un nome di sessione che sarà pubblico per gli utenti desiderano creare un join. Aggiunta di una sessione esistente deve visualizzare un elenco di disponibile nelle vicinanze di sessioni. Entrambe le modalità è necessario un nome da visualizzare all'utente. Figura 3 Mostra l'interfaccia utente risultante deve aspetto analogo al seguente per MainPage, nonché il codice XAML di compilare in questa pagina sono disponibili figura 4.

L'interfaccia utente MainPage
Figura 3 l'interfaccia utente MainPage

Figura 4 il codice XAML MainPage

<Page
  x:Class="TeamMessenger.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://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>

Dopo aver creato il codice XAML per la pagina, creare una nuova cartella ViewModel nell'app, quindi aggiungere una nuova classe pubblica in tale cartella denominata MainViewModel.cs. Questo modello di visualizzazione verrà collegato alla visualizzazione per gestire la funzionalità.

La prima parte del modello di visualizzazione gestirà la gestione dello stato per i pulsanti di opzione che determinano se l'utente crea una nuova sessione o aggiungendo uno esistente. Lo stato viene mantenuto attivo in un valore bool chiamato IsNewSession. Per cambiare lo stato di questo bool, CreateSession e JoinSession vengono utilizzati due metodi:

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

L'evento selezionato per ogni pulsante di opzione è associato a uno di questi metodi.

Gli elementi dell'interfaccia utente rimanenti vengono rilevati tramite le proprietà semplici. Il nome utente e il nome della sessione vengono associati alle proprietà SessionName e JoinName. La proprietà SelectedSession viene associata alla proprietà SelectedItem in ListView e relativo ItemsSource è associato alla proprietà sessioni:

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

Il modello di visualizzazione dispone di due eventi che verranno utilizzati per la visualizzazione di indicare se una connessione a una sessione di esito positivo o negativo:

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

Infine, il pulsante di avvio è associato a un metodo di avvio. Questo metodo può essere lasciato vuoto per il momento.

Una volta completato il modello di visualizzazione, deve creare una proprietà pubblica che è un'istanza del MainViewModel code-behind per MainPage. Questo è ciò che consente a X:Bind compilare le associazioni in fase di compilazione. Inoltre, è necessario sottoscrivere i due eventi creati nel modello di visualizzazione. Se una connessione viene stabilita correttamente, passerà a una nuova pagina, MessagePage. Se la connessione non riesce, un MessageDialog verrà visualizzato, informare gli utenti della connessione non riuscita. Figura 5 contiene il codice per il file MainPage.xaml.cs.

Figura 5 MainPage. XAML Codebehind

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();
}

Definire i modelli di Data

Prima di approfondisce nel nucleo dell'app, è necessario definire un paio di modelli di data che verrà utilizzato nell'app. Creare una cartella Models nell'app e quindi creare due classi in essa contenuti: L'utente e UserMessage. Come suggerisce il nome, il modello di utente sarà registrate informazioni sugli utenti connessi all'app:

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

La classe UserMessage conterrà il messaggio contenuto, l'utente che ha creato il contenuto e quando il messaggio è stato creato:

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

Creazione di una sessione

Con il codice per la pagina principale piuttosto completo, è possibile iniziare da compilare RemoteSessionManager, che verrà usato per eseguire il wrapping dell'API di sistemi remoti. Aggiungere una nuova classe pubblica denominata RemoteSessionManager nella directory radice dell'app. L'app userà una singola istanza condivisa di RemoteSessionManager, pertanto è possibile aggiungere una proprietà statica per la classe App nel file App.xaml.cs:

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

Prima che l'app possa accedere a qualsiasi delle API sistemi remoti, innanzitutto necessario ottenere l'autorizzazione da parte dell'utente. Questa autorizzazione viene ottenuta chiamando il metodo statico RemoteSystem.RequestAccessAsync:

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

Il metodo restituirà un valore enum RemoteSystemAccessStatus che può essere usato per determinare se è stato concesso l'accesso. Questo metodo deve essere chiamato dal thread dell'interfaccia utente in modo che è stato possibile richiedere all'utente. Dopo che l'utente è state concesse o negate le autorizzazioni per l'app, tutte le chiamate successive tornerà automaticamente preferenze dell'utente. Per questa app, questa autorizzazione verrà aggiunto all'individuazione di sessione perché viene chiamato prima del flusso di lavoro.

Si noti che tutte le API di sistema remoto possano essere trovate nello spazio dei nomi Windows.System.RemoteSystem.

Il primo metodo per aggiungere alla classe RemoteSessionManager è il metodo CreateSession. Poiché sono presenti più risultati che possono essere restituiti da questo metodo, sarà riepilogo quelli in una nuova enumerazione: SessionCreationResult. SessionCreationResult dispone di quattro valori: esito positivo e tre errori diversi. Una sessione può creazione non riuscita perché l'utente non concedere l'accesso all'app; l'app ha troppe sessioni in esecuzione. o un errore di sistema non è stato possibile creare la sessione:

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

Le sessioni remote vengono gestite da un RemoteSystemSessionController. Quando si crea una nuova istanza di RemoteSystemSessionController, è necessario passare un nome che verrà visualizzato nei dispositivi tentando di partecipare alla sessione.

Dopo che il controller è richiesto, una sessione può essere avviata chiamando il metodo CreateSession. Questo metodo restituisce un RemoteSystemSessionCreationResult contenente lo stato e la nuova istanza della sessione se eseguita correttamente. Il nuovo controller e sessione del RemoteSessionManager verranno archiviati nelle variabili private.

Una nuova proprietà pubbliche, IsHost, devono essere aggiunti per la gestione, nonché per determinarne del flusso di lavoro. Durante il metodo CreateSession, questo valore è impostato su true, che identifica questa app come host. Un'altra proprietà pubblica, CurrentUser, fornisce un'istanza dell'utente nel computer e verrà utilizzata per la messaggistica. Il gestore di sessione viene inoltre gestito un oggetto ObservableCollection di utenti nella sessione corrente. Questa raccolta viene inizializzata con l'utente appena creato. Per l'host della sessione, questa istanza viene creata nel metodo CreateSession. Le aggiunte risultante per il RemoteSessionManager vengono visualizzate nella figura 6.

Figura 6 CreateSession (metodo)

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;
}

Sono presenti tre più elementi che devono essere aggiunti alla RemoteSessionManager per completare il metodo CreateSession. Il primo è un gestore eventi per un utente tenta di partecipare a una sessione e viene generato l'evento JoinRequested nella sessione. Il metodo OnJoinRequested accetterà automaticamente qualsiasi utente che tenta di creare un join. Ciò può essere estesa per richiedere l'host per l'approvazione prima che l'utente viene aggiunto alla sessione. Le informazioni della richiesta viene fornite come un RemoteSystemSessionJoinRequest inclusi nel parametro RemoteSystemSessionJoinRequestedEventArgs del gestore dell'evento. Chiamata del metodo Accept aggiungerà l'utente alla sessione. Il codice seguente include il nuovo evento da aggiungere alla RemoteSessionManager, nonché il metodo OnJoinRequested completato:

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

La gestione di sessione consente di monitorare quando vengono aggiunti o rimossi dalla sessione corrente tramite il RemoteSystemSessionParticipantWatcher partecipanti. Questa classe consente di monitorare i partecipanti e genera evento aggiunto o rimosso quando necessario. Quando un utente crea un join tra una sessione già in corso, ogni partecipante già nella sessione corrente verrà visualizzato un evento aggiunto. L'app verrà richiedere questa serie di eventi e determinare quale partecipante è l'host facendo corrispondere DisplayName contro ControllerDisplayName della sessione. In questo modo i partecipanti a comunicare direttamente con l'host. Il gestore di sessione mantiene un watcher per il partecipante come una variabile privata che viene inizializzata nel InitParticipantWatcher. Questo metodo viene chiamato se la creazione di una sessione o il join di una sessione esistente. Figura 7 contiene le nuove aggiunte. Si noterà che per questo flusso di lavoro è necessario sapere quando un partecipante viene rimosso solo se si è l'host e se un partecipante viene aggiunto se si sta unendo in join una sessione. Come host, il RemoteSessionManager riguarda solo quando un partecipante lascia la sessione. L'host verrà visualizzato un messaggio direttamente dal partecipante accedono, come si vedrà avanti in questo articolo. Solo i partecipanti devono determinare l'account host corrente.

Figura 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);
  }
}

L'ultima operazione che CreateSession (metodo) deve aggiunta alla classe è un evento per informare i consumer se e quando la sessione viene disconnessa. Il nuovo evento SessionDisconnected può essere definito come segue:

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

Aggiunta a una sessione

Ora che l'app è in grado di trasmettere una nuova sessione remota, la prossima operazione da implementare è la possibilità di partecipare a tale sessione da un altro computer. Sono disponibili due passaggi per la partecipazione a una sessione remota: individuazione e quindi ci si connette alla sessione.

L'individuazione di una sessione un'app è in grado di individuare nelle vicinanze sessioni remote tramite RemoteSystemSessionWatcher, che viene creato dal metodo CreateWatcher statico. Questa classe genera eventi ogni volta che una sessione viene aggiunto o rimosso. Aggiungere un nuovo metodo, ovvero DiscoverSessions, ovvero per il RemoteSessionManager. Questo metodo crea un RemoteSystemSessionWatcher come una variabile privata alla classe e gestire gli eventi sono state aggiunte e rimosse. Questi eventi verranno integrati in due nuovi eventi aggiunti RemoteSessionManager: SessionAdded e SessionRemoved. Perché questo sarà un altro punto di ingresso per gli utenti durante l'inizializzazione delle sessioni remote, è necessario assicurarsi di aggiungere una chiamata a RemoteSystem.RequestAccessAsync. Figura 8 contiene la variabile privata, i due eventi e il metodo DiscoverSessions completo.

Figura 8 individuazione sessioni

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;
}

È ora possibile associare il MainViewModel per aggiornare la proprietà di sessioni con sessioni disponibili localmente. Poiché il metodo DiscoverSessions è asincrono, il costruttore del MainViewModel deve inizializzare un'attività per richiamarli. Il metodo di inizializzazione deve anche registrare e gestire gli eventi SessionAdded e SessionRemoved. Poiché questi eventi non verranno attivato sul thread dell'interfaccia utente quando si aggiorna la proprietà Sessions, è importante utilizzare un oggetto CoreDispatcher. Gli aggiornamenti a MainViewModel vengono figura 9.

Figura 9 aggiunta sessione individuazione

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); });
  }
}

La connessione alla sessione dopo che un utente ha identificato la sessione di creare un join e fornisce un nome, il RemoteSessionManager deve essere in grado di connettersi a essi per la sessione selezionata. Questa operazione verrà gestita da un nuovo metodo JoinSession su RemoteSessionManager, che accetta la sessione selezionata e il nome visualizzato immesso come parametri.

Il metodo JoinSession inizia chiamando il metodo JoinAsync nella sessione fornita. In cambio, verrà generato l'evento JoinRequested nella sessione host. Se l'host approva la richiesta, viene restituito uno stato di esito positivo e CurrentUser viene impostata utilizzando il nome visualizzato. Come con il CreateSession (metodo), il metodo InitParticipantWatcher viene richiamato per registrare un gestore eventi per quando vengono aggiunti dei partecipanti alla sessione. Il metodo JoinSession è racchiusa figura 10.

Figura 10 il metodo 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;
}

Il passaggio finale necessario per la partecipazione a una sessione consiste nell'utilizzare le funzionalità create nel RemoteSessionManager per creare o partecipare a una sessione. Figura 11 Mostra il metodo di avvio in MainViewModel associata al pulsante Avvia nella MainPage. Il flusso di lavoro del metodo è semplice. A seconda IsNewSession, chiama il metodo CreateSession o il metodo JoinSession. I risultati vengono restituiti per la generazione di eventi SessionConnected o ErrorConnecting. Se la sessione ha esito positivo, l'app passa a MessagePage, che verrà creato nella sezione successiva.

Figura 11 avviare una sessione

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);
      }
    }
  }
}

Mantenere la comunicazione con più App

A questo punto, l'app è possibile creare o partecipare a una sessione in completata e ha un'interfaccia utente di messaggistica che è pronta per essere utilizzato. L'unico passaggio rimanente è abilitare i dispositivi possano comunicare tra loro. Questa operazione viene eseguita tramite l'API di sistema remoto per inviare le istanze di set di valori tra i computer. Ogni set di valori è un set di coppie chiave/valore di payload serializzato.

Ricezione di messaggi i messaggi vengono trasmessi all'interno di una sessione tramite un RemoteSystemSessionMessageChannel. Una sessione può contenere più canali. Tuttavia, questa app sarà necessario solo un singolo canale. In RemoteSessionManager, verrà per aggiungere un metodo StartReceivingMessages. Questo metodo creerà un nuovo canale di messaggio che viene archiviato in una variabile privata e quindi aggiunta un gestore all'evento ValueSetReceived.

I messaggi vengono inviati come testo e perché l'app sta usando le classi come messaggi, è necessario serializzare i dati. Quando il set di valori viene ricevuto dal canale, viene utilizzato un DataContractJsonSerializer per riattivare le classi di messaggi nella classe DeserializeMessage. Perché non riesco a indicare il tipo di messaggio viene serializzato, l'app invia ogni tipo di messaggio come un valore diverso nel set di valori. La classe DeserializeMessage determinano la chiave utilizzata e restituire la classe corretta.

Una volta pronta la classe di messaggio, la classe di gestione su cui agirà il messaggio a seconda del tipo. Come si vedrà, partecipanti annuncerà stessi all'host inviando relativa istanza CurrentUser. In risposta, l'host verrà trasmesso l'elenco di utenti aggiornati a tutti i partecipanti. Se il gestore di sessione riceve l'elenco di partecipanti, le raccolte di utenti verrà aggiornato con i dati aggiornati. L'opzione finale, un UserMessage, verrà generato un nuovo evento MessageReceived che passa il messaggio e il partecipante ha inviato il messaggio. Queste aggiunte per la RemoteSessionManager sono reperibili figura 12.

Figura 12 la ricezione di messaggi

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
    });
  }
}

Figura 12 include una nuova classe di un gestore eventi, MessageReceivedEventArgs, che deve anche essere creato. Questa classe contiene due proprietà: il mittente e il messaggio:

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

L'invio di messaggi l'API di sistemi remoti sono disponibili due metodi per il recapito dei messaggi ad altri utenti. La prima consiste nel trasmettere un messaggio a tutti gli utenti nella sessione. Questo approccio sarà usato per due dei nostri tipi di messaggio, il UserMessage e l'elenco degli utenti. Creare un nuovo metodo, BroadcastMessage, nel RemoteSystemManager. Questo metodo accetta una chiave e il messaggio come parametri. Utilizzando un DataContractJsonSerializer, è possibile serializzare i dati e utilizzare il metodo BroadcastValueSetAsync per inviare il messaggio a tutti gli utenti, come illustrato figura 13.

Figura 13 trasmette un messaggio

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;
}

Il secondo approccio consiste nell'inviare un messaggio a un unico partecipante. Questo approccio è simile a trasmettere un messaggio, ad eccezione del fatto che usa il metodo SendValueSetAsync sul messaggio, un partecipante direttamente. Questo metodo finale per RemoteSystemManager, SendMessage, sono reperibili figura 14.

Figura 14 l'invio di un messaggio diretto

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;
}

Creazione della pagina di messaggistica

Con la messaggistica ora posto, è necessario inserire in modo da usare e di fine dell'app. Aggiungere una nuova pagina vuota per l'app, MessagePage.xaml. Questa pagina sarà costituito da un elenco di utenti, una finestra di messaggio e i campi di input per l'aggiunta di un messaggio. Il codice XAML completo è reperibile nella figura 15.

Figura 15 XAML MessagePage

<Page
  x:Class="TeamMessenger.MessagePage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:TeamMessenger"
  xmlns:models="using:TeamMessenger.Models"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://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>

Ad esempio MainPage, MessagePage sarà necessario un modello di visualizzazione. Aggiungere una nuova classe, MessageViewModel, nella cartella ViewModel. Questo modello di visualizzazione deve supportare INotifyPropertyChanged per consentire l'associazione bidirezionale funzionare correttamente. Questo modello di visualizzazione conterrà tre proprietà: Gli utenti, i messaggi e NewMessage. Gli utenti semplicemente esporrà la raccolta di utenti del RemoteSessionManager alla visualizzazione. I messaggi verranno ObservableCollection di oggetti UserMessage ricevuti e NewMessage stringa contenente il testo da inviare come un nuovo messaggio. È inoltre disponibile un singolo evento, MessageAdded, che verrà usato da code-behind in MessagePage. Nel costruttore del modello di visualizzazione, è necessario eseguire il mapping alla proprietà utenti, richiamare il metodo StartReceivingMessages in RemoteSessionManager e registrarlo per l'evento MessageReceived, come illustrato figura 16. Il costruttore include anche l'implementazione di INotifiyPropertyChanged.

Figura 16 MessageViewModel costruttore

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();
}

Nel costruttore è una chiamata a RegisterUser. Questo metodo invierà CurrentUser creato durante l'aggiunta a una sessione all'host. Questa notifica per l'host che è stato aggiunto un nuovo utente e quali il nome visualizzato è. In risposta, l'host invia l'elenco corrente di utenti da visualizzare nell'app:

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

L'ultima parte del modello di visualizzazione consiste nella trasmissione di un nuovo messaggio da parte dell'utente. Il metodo SubmitMessage costruisce una nuova UserMessage e chiama il metodo BroadcastMessage sul RemoteSessionManager. Quindi Cancella il valore NewMessage e genera l'evento MessageAdded, come illustrato figura 17.

Figura 17 inviando un messaggio

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);
}

Nel codice sottostante per MessagePage, racchiusa figura 18, è necessario eseguire due operazioni: creare un'istanza di MessageViewModel per il file XAML come fare riferimento e gestire l'evento MessageAdded. Nel caso in cui gestore, è possibile indicare il controllo ListView per scorrere fino alla fine dell'elenco in cui il messaggio più recente è visibile.

Figura 18 MessagePage Codebehind

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'applicazione di messaggistica Team dovrebbe ora essere pronto per l'esecuzione. In un unico computer, eseguire l'app e creare una nuova sessione. Quindi avviare l'app in un secondo computer, che deve visualizzare il messaggio appena creato. Una volta partecipare alla sessione è verrà portato alla nuova pagina messaggio in cui è possibile iniziare chat con altri utenti nella sessione, come illustrato figura 19. Ora è stata creata un'app multiutente usando l'API di sistema remoto.

Messaggistica multiutente
Figura 19 multiutente messaggistica

Conclusioni

La creazione di esperienze utente all'interno di App spesso richiede oltre un singolo dispositivo o piattaforma o un utente anche. Microsoft ha sviluppato Roma di progetto per consentire agli sviluppatori di fornire questo livello di esperienza all'interno delle app. In questo articolo ho creato un'app UWP usando API sistemi remoti. tramite il SDK di Roma progetto disponibile per altre piattaforme, tuttavia, è possibile estendere questa app per lavorare su più piattaforme. Quando si compila il successivo ottimale dei problemi per gli utenti, è importante considerare come progetto Roma può aiutare a rendere l'app più personali. Il codice sorgente per questo articolo è reperibile in bit.ly/2FWtCc5.


Tony Championè un progettista di software con più di 20 anni di esperienza di sviluppo con le tecnologie Microsoft. Come il di vicepresidente campione di dominio Active Directory e il relativo responsabile del software architect, egli rimane attivo nei più recenti tendenze e le tecnologie, creare soluzioni personalizzate nelle piattaforme Microsoft. L'elenco dei client si estendono su più settori e include le società, ad esempio: Schlumberger, Microsoft, Boeing, MLB e sulla freccia di espansione/Philips. Champion è un partecipante attivo nella community come MVP di Microsoft sei anni, altoparlante internazionale, autore pubblicato e blogger.

Grazie al seguente esperto di documentazione tecnico di Microsoft che ha revisionato in questo articolo: Shawn Henry


Viene illustrato in questo articolo nel forum di MSDN Magazine