May 2018

Volume 33 Number 5

ユニバーサル Windows プラットフォーム - UWP と Project Rome で接続アプリをビルドする

によってTony 支持

今日の成功したアプリの構築は 1 つのデバイスを越えてしまったを意味します。ユーザーは、すべてのデバイスにまたがるし、他のユーザーでも接続するアプリをします。この型のエクスペリエンスを提供するにはだめな課題できます。対応するためには、エコシステム、マイクロソフトのニーズの高まりには、プロジェクト ローマが導入されました。プロジェクトのローマでは、アプリ、デバイスとユーザーにまたがる個人 OS を作成する目的としています。プロジェクト ローマほとんどの主要プラットフォームの使用可能な Sdk には、この記事でデモ ユニバーサル Windows プラットフォーム (UWP) アプリをメッセージング チームを作成するプロジェクト ローマ使用方法について説明します。

プロジェクトのローマに挨拶します。

Project Rome は、アプリやデバイスへのユーザーの関与を促すためのイニシアティブです。これは、コレクションの Api を Microsoft Graph の一部であるし、2 つの領域に分類できます: 今すぐに続行し、後で続行します。

リモート システムの Api は、continue を有効にすると、ユーザーの現在のデバイスの境界を越えてを中断するアプリを有効にする-発生するようになりました。コンパニオンまたはリモート コントロール アプリと同様の単一のエクスペリエンスの 2 つのデバイスを使用する、または接続しは単一のエクスペリエンスを共有する複数のユーザーを許可するユーザーに許可すると、かどうか、これらの Api は、ユーザーの現在の契約の展開ビューを提供します。この記事でビルドされたアプリのメッセージング、チームでは、共有のユーザー エクスペリエンスを作成します。

プロジェクトのローマ、アクティビティの Api の残りの半分は、後で、ユーザーのエクスペリエンスの続行について説明します。これらの Api を使用すると、記録し、任意のデバイスからユーザーを続けることができます、アプリ内で特定のアクションを取得できます。この記事で説明しない、これらは確実に参考です。

作業の開始

アプリを構築する前に、まず必要自分の環境を設定します。プロジェクト ローマの最初のバージョンは、少しの間、アウトされましたが、中に、最近の秋作成者更新中にこの記事で使用される機能のいくつかリリースされただけです。そのため、コンピューター必要がありますまたは実行するビルド番号 16299 大きい。この時点では、このリリースでは、更新プログラムの低速のリングで使用できると、ほとんどのコンピューターが正しく更新する必要があります。

他のユーザーとリモート システム Api を使用したアプリを実行するには、マシンで共有のエクスペリエンスを有効にする必要があります。システム設定] の設定でこれ行う |システム |共有のエクスペリエンス。チームのメッセージング シナリオでは、ことを確認する必要がありますつまり共有エクスペリエンスが有効になっていると、共有またはで示すように、"すべてのユーザー、近くに"から受信できる、デバイスと通信するさまざまなユーザーを有効にする必要があります。図 1。.

経験を共有を有効にします。
経験を共有する図 1 を有効にします。

最終的な要件がいくつかのレベルの接続のことで、デバイスが見つからないです。リモート システム Api には、同じネットワークにも、近くの Bluetooth を使用して、上の他のマシンを検出します。システム設定で「Bluetooth やその他のデバイスの設定」ページでは、Bluetooth を有効にすることができます。

コンピューターをセットアップでは、アプリの作成、新しい Visual c# Visual Studio 2017 で空のアプリケーション (ユニバーサル Windows) テンプレートを使用して見てみましょう。アプリ"TeamMessenger"を呼び出す 前述のように、このプロジェクトの秋作成者更新プログラムが必要、そこで「ビルド 16299」にはアプリのターゲットと最低限のバージョンのセット、以上のように図 2です。こうと、アプリから以前のバージョンの Windows 10 をサポートするが、一部の機能をこの記事の内容影響を受ける必要があります。

アプリの設定の対象バージョン
図 2 の設定は、アプリのバージョンを対象

秋作成元のデバイス上の Sdk の更新を持っていない場合それらを取得する最も簡単な方法は Visual Studio 2017 を最新のリリースに更新するために注意してください。

プロジェクト ローマ Api は、このアプリを構築するためにインストールする追加の Sdk をダウンロードするがないことを意味する [Windows 10 SDK を使用すると、または NuGet パッケージの一部です。ただし、正常に動作するリモート セッションの Api の順序で、アプリに追加する必要があります、いくつかの機能があります。これにより、package.appxmanifest ファイルを開き、機能] タブを選択します。使用可能な機能の一覧で、次がチェックされますになっていることを確認します。Bluetooth、インターネット (クライアントとサーバー) およびリモート システムです。

セッションの接続の構築

このアプリは、最初のページを作成するか、リモート システム Api を使用してリモート セッションに参加するを担当すると、2 つのページで構成されます。わかりやすくするためは、ソリューションで作成された、読み込まれる最初のページを使用するアプリにワイヤード (有線) では、既に MainPage.xaml を使用してこのページをビルドします。UI が 2 つのモード: 作成するか、セッションをホストすると、および既存のセッションに参加します。セッションを作成するには、参加を検討しているユーザーに公開されるセッション名が必要です。既存のセッションに参加するは、セッションの近くにある使用可能なの一覧を表示する必要があります。両方のモードには、ユーザーに表示する名前が必要があります。図 3外観を示しています、結果として得られる UI がのこのページを構築するには、MainPage、および XAML の図 4です。

MainPage UI
図 3 に MainPage UI

図 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>

ページの XAML を作成すると、アプリで、新しい ViewModels フォルダーを作成し、MainViewModel.cs と呼ばれるフォルダーに新しいパブリック クラスを追加します。このビューのモデルは、機能を処理するビューにワイヤード (有線) されます。

ユーザーが新しいセッションを作成するかどうかを決定するオプション ボタンの状態を管理するか、既存の結合、ビュー モデルの最初の部分が処理されます。状態は、IsNewSession と呼ばれる bool に保持されます。この bool、CreateSession JoinSession の状態を切り替えるには 2 つの方法が使用されます。

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

各オプション ボタンのチェックされたイベントは、これらのメソッドのいずれかにバインドされます。

残りの UI 要素は、単純なプロパティを使って追跡されます。セッション名とユーザー名は、セッションおよび JoinName プロパティにバインドされます。SelectedSession プロパティは ListView で SelectedItem プロパティにバインドされ、その ItemsSource プロパティにバインドする、セッション。

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

ビュー モデルには、ビューのかどうか、セッションへの接続が成功したかを把握できるようにするために使用する 2 つのイベントがあります。

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

最後に、[スタート] ボタンは、Start メソッドにバインドされます。このメソッドが空にする現時点でします。

モデルの表示が完了したら、MainPage の分離コードを MainViewModel のインスタンスであるパブリック プロパティを作成する必要があります。これは、コンパイル時のバインドを作成する。 X:bind 行えるようにすることです。さらに、ビュー モデルに作成された 2 つのイベントをサブスクライブする必要があります。接続が正常に行われた場合は、宛先、新しいページに移動すればします。接続に失敗した場合、MessageDialog が表示されます、失敗した接続のユーザーに通知します。図 5 MainPage.xaml.cs のコードが含まれています。

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

データ モデルの定義

アプリの中心となるで掘り下げ、前に、アプリで使用されるデータ モデルのいくつかを定義する必要があります。アプリで、Models フォルダーを作成し、中に 2 つのクラスを作成します。ユーザーと UserMessage です。名前からわかるように、ユーザー モデルはアプリに接続しているユーザーに関する情報を追跡します。

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

UserMessage クラスが、コンテンツと、メッセージの作成時に作成するユーザーに、コンテンツ、メッセージが含まれます。

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

セッションを作成します。

メイン ページの完全なコード、リモート システム API をラップするために使用する RemoteSessionManager の構築を開始できます。アプリのルート ディレクトリに RemoteSessionManager と呼ばれる新しいパブリック クラスを追加します。App.xaml.cs で App クラスに静的なプロパティを追加できるように、アプリは、RemoteSessionManager の単一の共有インスタンスを使用します。

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

アプリは、任意のリモート システム Api にアクセスできますが、これには、最初ユーザーからアクセス許可を取得する必要があります。このアクセス許可が RemoteSystem.RequestAccessAsync の静的メソッドを呼び出すことによって得られます。

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

メソッドでは、アクセスが付与されたかどうかを判断するために使用する、RemoteSystemAccessStatus 列挙型を返します。ユーザーが正常に求めるように、UI スレッドからこのメソッドを呼び出す必要があります。ユーザーの許可や、アプリへのアクセス許可を拒否した後、後続の呼び出しは、ユーザーの基本設定を自動的に戻ります。このアプリのこのアクセス許可は、セッションを探索するため追加、ワークフロー内で最初に呼び出されます。

すべてのリモート システム Api にある Windows.System.RemoteSystem 名前空間に注意してください。

RemoteSessionManager クラスに追加する最初のメソッドは CreateSession メソッドです。このメソッドから返すことのできるいくつかの結果があるため、あるものをまとめる新しい列挙型で — SessionCreationResult です。SessionCreationResult が 4 つの値: 成功と 3 つの異なる失敗します。ユーザーがアプリへのアクセスを許可していないために、作成するセッションが失敗します。アプリにいる; を実行しているセッションが多すぎますまたは、システム エラーが、セッションの作成に失敗しました。

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

リモート セッションは、RemoteSystemSessionController によって管理されます。新しい RemoteSystemSessionController インスタンスを作成するときに、セッションに参加しようとしました。 デバイスに表示される名前で渡す必要があります。

コント ローラーが要求されると、されたら、CreateSession メソッドを呼び出すことによって、セッションを開始することができます。このメソッドは、成功した場合に表示される状態と、セッションの新しいインスタンスを含む RemoteSystemSessionCreationResult を返します。RemoteSessionManager は、新しいコント ローラーとセッションをプライベート変数に保存されます。

新しいパブリック プロパティ、IsHost は、マネージャーに、同様にワークフローを決定する追加する必要があります。CreateSession メソッド中にこの設定は true に、ホストとしてこのアプリを識別します。別のパブリック プロパティ、CurrentUser は、マシンに、ユーザーのインスタンスを提供し、メッセージングのために使用されます。セッション マネージャーは、現在のセッションでのユーザーの ObservableCollection も保持されます。このコレクションは、新しく作成したユーザーで初期化されます。セッションのホストをこのインスタンスを取得 CreateSession メソッドで作成されます。RemoteSessionManager の結果として得られる追加機能を示します図 6です。

図 6 に CreateSession メソッド

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

CreateSession メソッドを完了する RemoteSessionManager に追加する必要がある他の 3 つのアイテムがあります。2 つのイベント ハンドラーをユーザーがセッションに参加しようとして JoinRequested イベントが、セッションで発生します。OnJoinRequested メソッドは、参加を試みるすべてのユーザーを自動的に受け入れます。これは、ユーザーがセッションに参加する前に、承認のためホストを要求する拡張でした。要求情報は、イベント ハンドラーの RemoteSystemSessionJoinRequestedEventArgs パラメーターに含まれる RemoteSystemSessionJoinRequest として提供されます。Accept メソッドを呼び出すと、ユーザーがセッションに追加されます。次のコードには、RemoteSessionManager、だけでなく、完成した OnJoinRequested メソッドに追加する新しいイベントが含まれています。

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

セッション マネージャーは、参加要素が追加または、RemoteSystemSessionParticipantWatcher を通じて現在のセッションから削除されたときに監視できます。このクラスは、参加要素を監視し、必要なときに、追加または削除済みのいずれかのイベントを発生させます。ユーザーが進行中のセッションに既に参加、既に現在のセッション内の各参加者に追加されたイベントが表示されます。アプリはこの一連のイベントを受け取るし、される構成要素は、セッションの ControllerDisplayName に対して表示名を照合することによって、ホストを決定します。これにより、ホストと直接通信する参加者が許可されます。セッション マネージャーは、参加者のウォッチャーを InitParticipantWatcher で初期化されるプライベート変数として保持されます。このメソッドは、セッションを作成するかどうか、既存のセッションに参加するか。図 7新しく追加するメンバーが含まれています。このワークフローに知っておくと、ホストをしている場合にのみ、参加要素が削除されたときに、セッションに参加する場合、参加要素が追加された場合は明らかです。ホストとしては、参加者がセッションを離れたときにのみ、RemoteSessionManager が関係しています。ホストは通知、参加要素によって直接参加するときに記事の後半でわかります。参加要素は、現在のホスト アカウントを決定するだけです。

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

クラスに追加 CreateSession メソッドが必要な最後には、コンシューマーが、セッションが切断されている場合を把握できるようにするイベントです。新しい SessionDisconnected イベントは、次のように定義できます。

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

セッションに参加します。

これで、アプリは、新しいリモート セッションをブロードキャストすることを次に実装することは、別のコンピューターからそのセッションに参加する機能です。リモート セッションに参加する 2 つの手順がある: 検出と接続し、セッションに接続します。

セッションを検出するアプリは最寄りの静的 CreateWatcher メソッドから作成されると、RemoteSystemSessionWatcher 経由のリモート セッションを検出することができます。このクラスは、イベント セッションの追加または削除されるたびにイベントを発生させます。新しいメソッドを追加: DiscoverSessions —、RemoteSessionManager にします。このメソッドでは、クラスにプライベート変数として、RemoteSystemSessionWatcher が作成され、追加および削除済みのイベントを処理することができます。これらのイベントは RemoteSessionManager に追加された 2 つの新しいイベントでラップされます。SessionAdded と SessionRemoved です。ユーザーがリモート セッションの初期化中の別のエントリ ポイントである、ため RemoteSystem.RequestAccessAsync への呼び出しを追加することを確認する必要があります。図 8プライベート変数、2 つのイベント、および完全な DiscoverSessions メソッドが含まれています。

図 8 を検出するセッション

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

ローカルで利用可能なセッションでセッション プロパティを更新する MainViewModel に接続することはようになりました。DiscoverSessions メソッドは非同期なので、MainViewModel のコンス トラクターを起動するタスクを初期化する必要があります。初期化メソッドは、登録し、SessionAdded と SessionRemoved イベントを処理する必要がありますもします。これらのイベントは、セッション プロパティを更新するときに、UI スレッドで発生しません、しているので、CoreDispatcher を使用する必要があります。更新 MainViewModel はで図 9です。

図 9 を追加するセッションの検出

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

セッションに接続するRemoteSessionManager は選択されたセッションに接続できる必要があります、ユーザーが識別されると、セッションに参加し、名を指定、します。これは、パラメーターとして選択されたセッションと入力した表示名を受け取ると、RemoteSessionManager で新しい JoinSession メソッドによって処理されます。

JoinSession メソッドは、提供されているセッションで JoinAsync メソッドを呼び出して開始します。代わりに、このイベントを発生させる、JoinRequested ホスト セッションにします。ホストは、要求を承認する場合は、成功時の状態が返され、CurrentUser、表示名を使用して設定します。CreateSession メソッドを使用して、InitParticipantWatcher メソッドは、イベント ハンドラーを登録の参加者がセッションに追加されたときに呼び出されます。JoinSession メソッドが表示図 10です。

図 10 に 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;
}

最後に、セッションに参加するのに関係する、RemoteSessionManager で作成した機能を使用して作成するか、セッションに参加を開始します。図 11 MainPage で [スタート] ボタンにバインドされている MainViewModel で Start メソッドを示しています。メソッドのワークフローは簡単です。IsNewSession、に応じて CreateSession メソッドまたは JoinSession メソッドのいずれかを呼び出します。SessionConnected または ErrorConnecting のいずれかのイベントを発生させることによって、結果が返されます。セッションが成功した場合、アプリは、次のセクションを構築してみますの宛先に移動します。

図 11 セッションの開始

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

アプリと対話の保持

この時点では、アプリが正常に作成したり、セッションに参加に使用する準備が整っているメッセージング UI があるとします。残りの手順には、互いに通信するためにデバイスが有効にします。これは、コンピューター間の値セット インスタンスを送信するリモート システム API を使用して行います。各値セットは、シリアル化されたペイロードのキー/値ペアのセットです。

メッセージの受信RemoteSystemSessionMessageChannel によってセッション内でメッセージが送信されます。セッションが複数のチャネルを持つことができます。ただし、このアプリは、単一のチャネルだけを必要があります。RemoteSessionManager StartReceivingMessages メソッドを追加するつもり。このメソッドでは、プライベート変数に格納され、ValueSetReceived イベントにハンドラーを追加する新しいメッセージ チャネルを作成します。

メッセージはテキストとして送信し、アプリがメッセージとしてクラスを使用してデータをシリアル化する必要があります。値セットが、チャネルから受信されると、復元、DeserializeMessage クラス内のメッセージ クラスを DataContractJsonSerializer が使用されます。通知メッセージの種類は、シリアル化できないため、アプリは、送信メッセージの種類ごと、ValueSet で別の値として。DeserializeMessage クラスは、使用するキーを特定し、正しいクラスを返します。

メッセージ クラス準備ができたら、マネージャー クラスは、メッセージの種類に応じてで機能します。説明するように、参加者がアナウンスをホストに、CurrentUser インスタンスを送信することによってです。応答として、ホストは、すべての参加者にで更新されたユーザー リストにブロードキャストします。セッション マネージャーは、参加要素の一覧を受信する場合、ユーザー コレクション、更新されたデータ更新されます。最後のオプションで、UserMessage には、メッセージとメッセージを送信した参加要素を渡す新しい MessageReceived イベントが発生します。これらの追加、RemoteSessionManager には含まれて図 12です。

図 12 がメッセージの受信

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

図 12新しいイベント ハンドラー クラスも作成される必要があります MessageReceivedEventArgs が含まれています。このクラスには、2 つのプロパティが含まれています。 送信者とメッセージ。

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

メッセージを送信するリモート システム API は、他のユーザーにメッセージを配信するための 2 つのメソッドを提供します。最初に、セッション内のすべてのユーザーにメッセージをブロードキャストします。この方法は、メッセージの種類、UserMessage、およびユーザーの一覧の 2 つ使用されます。RemoteSystemManager BroadcastMessage、新しいメソッドを作成してみましょう。このメソッドは、パラメーターとして、キーと、メッセージを受け取ります。DataContractJsonSerializer を使用すると、すれば、データをシリアル化方法を使用して BroadcastValueSetAsync メッセージを送信するすべてのユーザーのように図 13です。

図 13、メッセージのブロードキャスト

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

2 番目の方法では、1 つの参加者にメッセージを送信します。この方法は、メッセージをブロードキャストして SendValueSetAsync メソッドを使用して、参加要素を直接メッセージ以外に似ています。この最終的な RemoteSystemManager、SendMessage、メソッドは含まれて図 14です。

図 14 直接メッセージを送信します。

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

メッセージングのページの作成

メッセージングを使用するようになりました場所では、配置を使用して、アプリを終了する時刻を勧めします。MessagePage.xaml、アプリには、新しい空白のページを追加します。このページは、ユーザー、メッセージ ウィンドウとメッセージを追加するための入力フィールドの一覧で構成されます。XAML のすべては含まれて図 15です。

図 15 に宛先 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>

MainPage などの宛先はビュー モデルを必要があります。ViewModels フォルダーに MessageViewModel、新しいクラスを追加します。このビューのモデルは、正常に機能する双方向のバインドを許可する INotifyPropertyChanged をサポートする必要があります。このビューのモデルでは、3 つのプロパティが表示されます。ユーザー、メッセージ、および新しいメッセージ。ユーザーは、RemoteSessionManager のユーザー コレクション ビューを単に公開します。メッセージは、新しいメッセージとして送信するには、受信したオブジェクトを UserMessage の ObservableCollection とテキストを含む新しいメッセージ文字列になります。イベントを 1 つの宛先に分離コードで使用される MessageAdded もあります。ビュー モデルのコンス トラクターで必要にマップする、ユーザー プロパティ、MessageReceived イベント RemoteSessionManager、および登録 StartReceivingMessages メソッドを呼び出すのように図 16です。コンス トラクターには、INotifiyPropertyChanged の実装も含まれています。

図 16 MessageViewModel コンス トラクター

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

コンス トラクターで RegisterUser への呼び出しがあります。このメソッドは、ホスト セッションに参加するときに作成された CurrentUser に送信されます。これは、新しいユーザーが参加しているホストとは、どのような表示名を発表しました。応答として、ホストが送信されます、現在のユーザーの一覧、アプリに表示します。

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

ビュー モデルの最後の部分では、ユーザーから、新しいメッセージをブロードキャストします。SubmitMessage メソッドは、新しい UserMessage を構築して、RemoteSessionManager で BroadcastMessage メソッドを呼び出します。新しいメッセージの値をクリアし、イベントを発生させる、MessageAdded のように図 17です。

メッセージを送信する図 17

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

宛先の分離コードに示す図 18、2 つの作業を行う必要があります: 参照および MessageAdded イベントを処理する XAML の MessageViewModel のインスタンスを作成します。イベントのハンドラーは指示 ListView を最新のメッセージが表示されている一覧の一番下までスクロールします。

図 18 の宛先の分離コード

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

チームのメッセージング アプリケーションが実行する準備ができます。1 台のコンピューターには、アプリを実行して、新しいセッションを作成します。新しく作成されたメッセージを表示する必要がある、2 つ目のコンピューターでアプリを起動し、します。移動することに、新しいメッセージ] ページのセッションで他のユーザーとチャットを開始することができますに示すようにセッションに参加すると図 19です。リモート システム API を使用して、マルチ ユーザーのアプリが作成されました。

マルチ ユーザー メッセージング
図 19 マルチ ユーザー メッセージング

まとめ

多くの場合、アプリ内で成功したユーザー エクスペリエンスを作成するには、1 つのデバイス プラットフォーム、またはもユーザーにとどまらずが必要です。Microsoft では、開発者がこのレベルのそれぞれのアプリ内のエクスペリエンスを提供するためのプロジェクト ローマ開発しました。この記事ではリモート システム API を使用して、UWP アプリを構築しましたただし、その他のプラットフォームを使用可能なプロジェクト ローマ Sdk を使用して複数のプラットフォームで動作するには、このアプリを拡張できます。ユーザーの [次へ] の優れたエクスペリエンスを構築する場合は、個人向けアプリを作成するプロジェクト ローマがどのように役立つかを検討してください。この記事のソース コードはありますbit.ly/2FWtCc5です。


Tony 支持20 年以上の Microsoft テクノロジの使用による開発経験ソフトウェア アーキテクトです。、支持者の DS とその潜在顧客ソフトウェア設計者の社長として彼がアクティブで、最新の傾向とテクノロジ、Microsoft プラットフォームでは、カスタム ソリューションを作成します。クライアントの一覧は、さまざまな業界にまたがるし、企業にはなどが含まれます。できるように、Microsoft、ボーイング、MLB およびシェブロン/Philips です。Champion コミュニティ内のアクティブな参加者は、Microsoft MVP の 6 つの年、国際スピーカー、パブリッシュされた作成者およびブロガーとして。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Shawn Henry


この記事について MSDN マガジン フォーラムで議論する