[WinRT] 自動的に閉じるダイアログを作る

モーダルダイアログ風なものができたので、これを応用して自動的に閉じるダイアログを作ります。

のようにメッセージを表示して、しばらくしたら閉じるっていうメッセージダイアログです。よくゲームであるパターンだし、他のアプリでも応用が利きます。
フォームアプリの場合にはタイマーを使ってイベント待ちをするのが定番ですが、WinRT の場合(というか XAMLの場合)には、storyboard を使って、3秒後の閉じる、ってのがコード的に楽です。

自動的に閉じるダイアログは、Popup でも Canvas でもいいのですが、ここでは、popup を使って実現してみます。

■popup でダイアログを作る

popup コントロールを使って、ダイアログを作成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Popup x:Name="popYaku"
        Margin="320,160,-320,328" Grid.Row="1">
    <Grid Background="Red" Height="213" Width="399">
        <TextBlock
            x:Name="popYakuText"
            FontSize="40"
            HorizontalAlignment="Left" Margin="34,32,0,0" TextWrapping="Wrap" Text="役ができました" VerticalAlignment="Top"/>
        <Image x:Name="pictYaku1" HorizontalAlignment="Left" Height="100" Margin="34,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
        <Image x:Name="pictYaku2" HorizontalAlignment="Left" Height="100" Margin="103,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
        <Image x:Name="pictYaku3" HorizontalAlignment="Left" Height="100" Margin="172,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
        <Image x:Name="pictYaku4" HorizontalAlignment="Left" Height="100" Margin="241,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
        <Image x:Name="pictYaku5" HorizontalAlignment="Left" Height="100" Margin="310,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
    </Grid>
</Popup>

この場合には、花札の役を表示するので、あらかじめ image コントロールを貼り付けておきます。

■blend で storyboard を追加する

3秒経ったらダイアログを閉じるタイムラインを、blend を使って作ります。

1
2
3
4
5
6
7
8
9
10
<Page.Resources>
    <!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
    <x:String x:Key="AppName">花札 こいこい</x:String>
    <Storyboard x:Name="sbPopYaku">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="popYaku">
            <EasingDoubleKeyFrame KeyTime="0:0:2.5" Value="1"/>
            <EasingDoubleKeyFrame KeyTime="0:0:3" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Page.Resources>

プログラムから storyboard を起動するので名前をつけておくのと、単に消えると WinRT らしくないので、最後の 0.5 秒だけは透明度を変更するという方式にします。この手のアニメーションは blend を使うとひじょうに楽ですね。

■アニメーション終了時の処理を追加

ポップアップを開くときは、IsOpen プロパティを true にすれば ok。ストリーボードの開始は sbPopYaku.Begin() な感じで実行ができます。
で、忘れちゃいけないのが、アニメーションが終了した時に、ストリーボードを Stop で終了させておくことと、ポップアップを IsOpen = false で閉じておくこと。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/// <summary>
/// 役を表示
/// </summary>
/// <param name="y"></param>
void dispPopYaku( Yaku y )
{
    this.pictYaku1.Visibility = Windows.UI.Xaml.Visibility.Visible;
    this.pictYaku2.Visibility = Windows.UI.Xaml.Visibility.Visible;
    this.pictYaku3.Visibility = Windows.UI.Xaml.Visibility.Visible;
    this.pictYaku4.Visibility = Windows.UI.Xaml.Visibility.Visible;
    this.pictYaku5.Visibility = Windows.UI.Xaml.Visibility.Visible;
    if (y.GoKou != null)
    {
        this.popYakuText.Text = "五光";
        this.pictYaku1.Source = CardUI.GetResName(y.GoKou[0].ID);
        this.pictYaku2.Source = CardUI.GetResName(y.GoKou[1].ID);
        this.pictYaku3.Source = CardUI.GetResName(y.GoKou[2].ID);
        this.pictYaku4.Source = CardUI.GetResName(y.GoKou[3].ID);
        this.pictYaku5.Source = CardUI.GetResName(y.GoKou[4].ID);
    }
// 略
 
    this.sbPopYaku.Completed += sbPopYaku_Completed;
    this.popYaku.IsOpen = true;
    this.sbPopYaku.Begin();
}
 
/// <summary>
/// タイマー完了時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void sbPopYaku_Completed(object sender, object e)
{
    this.sbPopYaku.Stop();
    this.popYaku.IsOpen = false;
}

これで、適当なところで dispPopYaku メソッドを呼び出せばポップアップが表示されます。

アニメーションは非同期で行われるので、下記のように、dispPopYaku でポップアップを表示させた後は、_curPlayer.GetCard などのメソッドは即時実行されます。さきゆきは、場からマッチしたときのアニメーションも追加するので、このあたりの整合性(画面の動き的な整合性)をあわせる必要がありますね。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// 場に出した札をクリックしてマッチさせる
/// </summary>
/// <param name="obj"></param>
void baControl1_ClickPutCard(Card obj)
{
    var lst = _board.Game.MatchBa(_board.Ba, _board.Ba.PlayerCard, _curPlayer);
    if (lst != null)
    {
        // カードが決定した場合、画面を更新
        _board.Ba.GetCard(lst);
        // 役の計算
        Yaku y = _board.Game.CalcYaku(lst, _curPlayer.TakenCards);
        if (y != null)
        {
            // 一定時間、役を表示
            dispPopYaku(y);
        }
        // 手持ちに加える
        _curPlayer.GetCard(lst);
        // 画面の更新
        ScrUpdate();
    }
}

■ダイアログをタップした瞬時の閉じる処理を追加

自動的にポップアップは閉じるのですが、タップしたときに即時終了させる処理も追加しておきます。

ポップアップの grid 部分に Tapeed イベントを追加しておきます。ためしに Popup 自身に Tapped を追加してみたのですが呼び出されませんでした。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<Popup x:Name="popYaku"
        Margin="320,160,-320,328" Grid.Row="1">
    <Grid Background="Red" Height="213" Width="399"
            Tapped="popClick"
            >
        <TextBlock
            x:Name="popYakuText"
            FontSize="40"
            HorizontalAlignment="Left" Margin="34,32,0,0" TextWrapping="Wrap" Text="役ができました" VerticalAlignment="Top"/>
        <Image x:Name="pictYaku1" HorizontalAlignment="Left" Height="100" Margin="34,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
        <Image x:Name="pictYaku2" HorizontalAlignment="Left" Height="100" Margin="103,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
        <Image x:Name="pictYaku3" HorizontalAlignment="Left" Height="100" Margin="172,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
        <Image x:Name="pictYaku4" HorizontalAlignment="Left" Height="100" Margin="241,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
        <Image x:Name="pictYaku5" HorizontalAlignment="Left" Height="100" Margin="310,96,0,0" VerticalAlignment="Top" Width="64" Source="/Images/FC097-2.png"/>
 
    </Grid>
</Popup>

アニメーションの完了イベントを発生させるために、SkipToFill メソッドを使って最終点まで移動させます。これでタップしたときには即時終了します。

1
2
3
4
5
6
7
8
9
/// <summary>
/// ポップアップをタップした時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void popClick(object sender, TappedRoutedEventArgs e)
{
    this.sbPopYaku.SkipToFill();
}

他のアニメーションとの整合性もあるのですが(札を手持ちに加えるアニメーションとか)、ひとまず役をできたときのポップアップはこれで ok ということで。

カテゴリー: C#, WinRT パーマリンク