kazuakix の日記

Windows Phone とか好きです

ユニバーサル アプリで "それっぽい" タイトルバーを作る。 ビヘイビアで

昨日のブログの最後に 「そういえば XAML で操作できないんですかね?」と書いていたところ、かずきさんからアドバイスを頂きました。

そういえばビヘイビアって使ったことなかったんですよね...と「 Behavior 作り方」で検索してみると次のページがヒットしました。

「ステマかキサマッ!!」っと東に向かって叫びそうになりながら内容を確認します。
 
この記事 (WPF) では Behavior<> を継承したクラスを作るようですね。ただ、Windows Phone 8.1 (というか WinRT) には Behavior<> はありませんでした。

早速、「 WinRT Behavior 」を検索してみます。

孫悟空にでもなった気分ですが、リンク先のコードレシピを確認します。どうやら Behaviors SDK というものを使えばいいようですね。
 
後はリンク先に全て書いてありますが、せっかくなので自分でもやってみます。

まずは 参照設定から Behaviors SDK を追加しましょう。

f:id:kazuakix:20140822222036j:plain,w541
 
次に新しいクラス (今回は AppBarBehavior とします) を追加して、DependencyObject と IBehavior を継承します。
IBehavior については必要なプロパティ、メソッドを実装しておきます。

f:id:kazuakix:20140822223229j:plain

f:id:kazuakix:20140822223234j:plain

AssociatedObject にビヘイビアをセットする要素(今回だと Page) をセットし、 Attach() で前処理、Detach() で後処理をすればいいみたいです。

上記ページを参考にしながら、こんな感じにしてみました。

public class AppBarBehavior : DependencyObject, IBehavior
{
    public DependencyObject AssociatedObject { get; private set;}

    public void Attach(DependencyObject associatedObject)
    {
        this.AssociatedObject = associatedObject;
        ((FrameworkElement)this.AssociatedObject).Loaded += this.AssociatedObjectLoaded; 
    }

    private void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
    {
        throw new System.NotImplementedException();
    }

    public void Detach()
    {
        ((FrameworkElement)this.AssociatedObject).Loaded -= this.AssociatedObjectLoaded;
        this.AssociatedObject = null;
    }
}


これでアタッチした要素がロードされたときに AssociatedObjectLoaded() が呼び出されます。とりあえず昨日のコードを書いておきましょう。

    private async void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
    {
        var statusBar = Windows.UI.ViewManagement.StatusBar.GetForCurrentView();

        statusBar.BackgroundColor = Windows.UI.Color.FromArgb(255, 168, 17, 31);
        statusBar.BackgroundOpacity = 1;

        statusBar.ProgressIndicator.Text = "App7";
        statusBar.ProgressIndicator.ProgressValue = 0;
        await statusBar.ProgressIndicator.ShowAsync();
    }

 
このコードをビルドして Blend を開いてみると ビヘイビアの中に AppBarBehavior ができているので、Page にドラッグ & ドロップしてやります。

f:id:kazuakix:20140822225623j:plain

デザイナ上では何も変化がありませんが、実行してみるとこの通り。
これでページごとに同じコード書かなくて済みますね。

f:id:kazuakix:20140822230109j:plain,w500
 

最後にやっつけでタイトルと背景色をプロパティにしてみました。

using Microsoft.Xaml.Interactivity;
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;

namespace App7
{
    public class AppBarBehavior : DependencyObject, IBehavior
    {
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(AppBarBehavior), new PropertyMetadata("", new PropertyChangedCallback(OnTitleChanged)));

        private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var thisinstance = d as AppBarBehavior;
            thisinstance.ShowStatusBarTitle((string)e.NewValue);
        }

        public SolidColorBrush Background
        {
            get { return (SolidColorBrush)GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }

        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.Register("Background", typeof(SolidColorBrush), typeof(AppBarBehavior), new PropertyMetadata(null, new PropertyChangedCallback(OnBackgroundChanged)));

        private static void OnBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var thisinstance = d as AppBarBehavior;
            thisinstance.ShowStatusBar(e.NewValue as SolidColorBrush);
        }

        public DependencyObject AssociatedObject { get; private set;}

        public AppBarBehavior()
        {
            this.Title      = string.Empty;
            this.Background = (App.Current.Resources["PhoneAccentBrush"]) as SolidColorBrush;
        }

        public void Attach(DependencyObject associatedObject)
        {
            this.AssociatedObject = associatedObject;
            ((FrameworkElement)this.AssociatedObject).Loaded += this.AssociatedObjectLoaded; 
        }

        private void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
        {
            ShowStatusBar(this.Background);
            ShowStatusBarTitle(this.Title);
        }


        private void ShowStatusBar(SolidColorBrush background)
        {
            var statusBar = Windows.UI.ViewManagement.StatusBar.GetForCurrentView();

            statusBar.BackgroundColor = background.Color;
            statusBar.BackgroundOpacity = 1;
        }

        private async void ShowStatusBarTitle(string title)
        {
            var statusBar = Windows.UI.ViewManagement.StatusBar.GetForCurrentView();

            statusBar.ProgressIndicator.Text = title;
            statusBar.ProgressIndicator.ProgressValue = 0;
            await statusBar.ProgressIndicator.ShowAsync();
        }

        public void Detach()
        {
            ((FrameworkElement)this.AssociatedObject).Loaded -= this.AssociatedObjectLoaded;
            this.AssociatedObject = null;
        }
    }
}

 
一応、こんな感じで指定できるようになります。

<Interactivity:Interaction.Behaviors>
    <local:AppBarBehavior Background="#FF006699" Title="MyApp"/>
</Interactivity:Interaction.Behaviors>