Windows Phone 8 用アプリ「背景自動切替」公開

 自作の Windows Phone 8 用アプリ「背景自動切替」を申請していましたが、無事審査を通って公開されました。

 http://www.windowsphone.com/en-us/store/app/%E8%83%8C%E6%99%AF%E8%87%AA%E5%8B%95%E5%88%87%E6%9B%BF/1566eb62-89ea-4308-a7c3-b88970372c03
 ※現時点では、まだ日本語のサイトでは表示されないようです。

 端末内の画像をランダムに選び、ロック画面の背景を定期的に切り替える単純なアプリです。
 ロック画面の背景を切り替える機能は Windows Phone 8 で追加された機能なので、アプリも Windows Phone 8 専用となっています。

 背景を変更して気分転換したい時にでも使って見てください。
 Windows Phone 8 端末は日本で発売されていませんので、現状使える方はほとんどいないと思いますが、、、

複数画面でのバックグラウンド音楽再生

 以前、バックグラウンドで音楽再生をする方法について紹介しました。
 バックグラウンドでの音楽再生 - rebuild

 この方法でバックグラウンド再生はできるのですが、画面が複数ある場合は問題があります。

 前回作成したアプリにサブ画面を追加して、ナビゲートできるようにしてみるとわかるのですが、MediaElement を定義した画面から別の画面に移動すると音楽再生が止まってしまいます。
 Page の NavigationCacheMode を Required にして MediaElement を定義した画面がキャッシュされるようにしても状況は変りません。

 MediaElement を定義した画面がアクティブになっていないと再生が止まってしまうようですので、常にアクティブになる方法を考えます。
 テンプレートで生成された App.xaml.cs を見ると、最初に Frame のインスタンスを生成し、その Frame のメソッドを使って Page のナビゲートを行っています。
 この事から最初に生成した Frame は常に存在している物と推測されますので、ここに MediaElement を定義します。
 具体的には App.xaml で MediaElement を含んだ Frame のスタイルを定義し、App.xaml.cs でインスタンスを生成した時に定義したスタイルを適用します。

<Style x:Key="CustomFrameStyle" TargetType="Frame">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Frame">
                <Grid>
                    <MediaElement x:Name="bkMediaElem" AudioCategory="BackgroundCapableMedia" AutoPlay="True" />
                    <ContentPresenter />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
rootFrame = new Frame();
rootFrame.Style = Resources["CustomFrameStyle"] as Style;

 これで、常に MediaElement がアクティブになるようになりました。
 MediaElement を操作する場合は、MainPage.xaml.cs で画面レイアウトが終了した後(Loadedイベント発生後)に、VisualTreeHelper を使って取得します。

DependencyObject rootGrid = VisualTreeHelper.GetChild(Window.Current.Content, 0);
MediaElement mediaElem = (MediaElement)VisualTreeHelper.GetChild(rootGrid, 0);

 MediaElement が取得できてしまえば、後は画面が一つしかない場合と同じように操作するだけです。
 これで、画面が複数あっても途切れずに再生ができるようになります。もちろんどの画面でバックグラウンドにまわっても再生は継続されます。

Windows 8 UI/UXデザイン入門

 「Windows 8 UI/UXデザイン入門」を読了しました。

 Windows ストア アプリ や Windows Phone アプリで取り入れられている UI/UX のデザインについて解説した本です。
 一般的な開発本と違い、プログラムの作り方ではなくデザインにスポットを当てています。

 Windows 8 で新たに追加された Windows ストア アプリ は、UI 等について詳細なガイドラインが定められており、既存のデスクトップアプリとは違った考え方が必要になります。
 本書では、マイクロソフトが定めたガイドラインについて、その意図を説明した上で具体例を紹介しています。

 これから Windows ストア アプリ の開発を始める開発者(特に私のようにデザインを苦手としているプログラマ)にとって、非常に参考になる一冊だと思います。

Windows8UI/UXデザイン入門code name Metro

Windows8UI/UXデザイン入門code name Metro

GridView の問題点

 Windows 8 および Visual Studio 2012 の RTM が公開されましたので、以前から何度か記事にしていた GridView のマージンに関する問題点を確認しました。

 結論から言うと、Windows 8 Release PreviewVisual Studio 2012 RC のテンプレートで生成した物から変化はなく、RTM のテンプレートで生成した場合でも同様の問題があります(GridView の右側、ListView の下側のマージンが適切に反映されない)。

 問題が残ったまま RTM として公開されでしまったので、当面直らない可能性が高いと思います。
 テンプレートで生成される物がこのような動作になっているので、そのまま提出してもこの問題が原因でリジェクトされる事はないと思いますが、気になる場合はスタイル等を変更して対策をする必要があります。

 対策方法として以前書いた記事がありますので、一例として参考にして下さい。
 Release Preview における GridView の問題点 (2) - rebuild

Release Preview における GridView の問題点 (2)

 Release Preview 上で Visual Studio 2012 RC のテンプレートを使って生成したプロジェクトに、GridView や ListView のマージンに関する問題がある事がわかりましたので、解決方法について検討してみたいと思います。
 問題点は GridView の右側、ListView の下側のマージンが正しく設定されないという物です。

・GridView の場合

・ListView の場合

現象の確認

 まず、問題の詳細を確認するために Blend で Grid App を作成し、GridView のスタイルをコピーしてみます(GridView を選択して右クリックのメニューから [テンプレートの編集] - [コピーして編集] を実行)。
 中身を見てみると GridView の Padding に指定した値を ItemsPresenter の Padding に設定するようになっています。

<Style x:Key="CustomGridViewStyle" TargetType="GridView">
    <Setter Property="Padding" Value="0,0,0,10"/>
            :
            :

        <ItemsPresenter HeaderTemplate="{TemplateBinding HeaderTemplate}" Header="{TemplateBinding Header}" HeaderTransitions="{TemplateBinding HeaderTransitions}" Padding="{TemplateBinding Padding}"/>

            :
            :
</Style>

 内容だけを見ると正しく動きそうですが、Padding のスクロール方向の値(Grid App の GridView の右、ListView の下)だけ正しく反映されていないようです。
 ItemsPresenter の Padding に直接数値を設定しても、スクロール方向の値だけ反映されません。

 Margin の値は正しく反映されるようなので、Padding ではなく Margin に設定するように変更してみました。

<ItemsPresenter HeaderTemplate="{TemplateBinding HeaderTemplate}" Header="{TemplateBinding Header}" HeaderTransitions="{TemplateBinding HeaderTransitions}" Margin="{TemplateBinding Padding}"/>

 これで一見正しく動いているように見えたのですが、グループを使っていない GridView(Split App で作成した物が該当)をこのスタイルで動かしてみると、右側のマージンはきちんと取られますが、スクロールした時に左側が画面いっぱいに表示されず常にマージンがあるような形で表示されてしまいます。

対応方法

 確認した内容を検討すると、ItemsPresenter の Padding に設定されている値の内、GridView の場合は Right、ListView の場合は Bottom のみを Margin の方に設定すれば想定通りに動作しそうです。

 以前も試した通り Margin に直接値を記述したスタイルを複数用意して、必要に応じて切り替える事で対応できると思いますが、この方法は設定値が違う場合は別のスタイルとして定義しなければならないため面倒ですし、ポートレートビューになった時はスタイルを切り替える必要があります。

 せっかくテンプレートで作成した xamlポートレートビュー切替時の動作(Padding の値を変更する)が入っていますので、できればこれを生かした形でという事で Padding に設定されている値を Margin にバインドする方法で対応してみたいと思います。

 単純に Padding の値を Margin にバインドするだけであれば、以下のような xaml で対応できます。

<ItemsPresenter HeaderTemplate="{TemplateBinding HeaderTemplate}" Header="{TemplateBinding Header}" HeaderTransitions="{TemplateBinding HeaderTransitions}" Padding="{TemplateBinding Padding}" Margin="{Binding Padding, RelativeSource={RelativeSource Mode=Self}}"/>

 しかし、この対応では Padding の全ての値が Margin にそのまま設定されてしまいますので、左端等は二重に余白がとられてしまいます。

 今回やりたい事は、Padding の Right または Bottom のみを Margin に設定する事ですが、xaml だけで行う方法が見つけられませんでしたので、コンバーターを作成して対応しました。
 コンバーターのソースは以下のような物で、渡された Thickness の Right または Bottom だけを使い、その他は 0 に変換します。

public class RightMarginConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var margin = (Thickness)value;
        return new Thickness(0, 0, margin.Right, 0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

public class BottomMarginConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var margin = (Thickness)value;
        return new Thickness(0, 0, 0, margin.Bottom);
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

 作成したコンバーター などに定義した上で、以下のようにバインドする時に使用するように設定します。

<common:RightMarginConverter x:Key="RightConverter"/>

    :

<ItemsPresenter HeaderTemplate="{TemplateBinding HeaderTemplate}" Header="{TemplateBinding Header}" HeaderTransitions="{TemplateBinding HeaderTransitions}" Padding="{TemplateBinding Padding}" Margin="{Binding Padding, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource RightConverter}}"/>

 この対応で、グループを使用している物も使用していない物も同じ方法で適切なマージンが設定されるようになります。

・グループを使っている GridView

・グループを使っていない GridView

 ListView に関しても対応方法は同じです(BottomMarginConverter を使います)。

・グループを使っている ListView

・グループを使っていない ListView

注意事項

 今回の対応は Blend でプロジェクトを作成してスタイルコピーを行い、その後 Visual Studio で開きなおしてコンバーターの追加等を行いました。
 この手順で作業を行い Visual Studio 側でビルドする時に、証明書ファイルが読み込めないというエラーが発生しました(確実に発生するのかまでは確認していません)。

 このようなエラーが発生した場合、Package.appxmanifest をダブルクリックして開き、[パッケージ化]タブの[証明書の選択]ボタンを押して、[テスト証明書の作成]を行うと正常にビルドできるようになります。

まとめ

 今回行ったような対応で適切なマージンを持つようにできますが、次のリリース時にはまた何らかの対策が行われると思います。

 現時点でこのような対応を行っても対策が行われて無駄になる可能性が高いですし、Consumer Preview の時のようにキーボードやマウスでの操作に支障があるといった問題ではありませんので、どうしても必要な場合を除いては対応は行わずに対策を待った方が良いでしょう。

Release Preview における GridView の問題点 (1)

 Consumer Preview 上で Visual Studio 11 Beta のテンプレートで生成したプロジェクトを動作させた場合 GridView や ListView の動作に問題がありました。
 前回、Release Preview 上で Visual Studio 2012 RC のテンプレートで生成したプロジェクトを使って問題が解決しているか確認し問題が解決していると判断しましたが、詳細な動きを調べてみたところ、まだ問題点がある事がわかりました。

 以降、Consumer Preview での問題点が Release Preview でどのように変わったかをまとめ、その中で Release Preview での問題点についても説明したいと思います。

マウスやキーボードでのスクロール

 Consumer Preview の GridView や ListView には、マウスのホイールでスクロールできない、キーボードでカレントアイテムを移動していっても追随してスクロールせずカレントアイテムが画面外に消えてしまうという問題点がありました。
 この件は、Release Preview で解決していて、正常にスクロールするようになっています。

アイテムの仮想化

 Consumer Preview では GridView や ListView の各アイテムが仮想化されておらず、常に全てのアイテムが生成されていました。
 この件については、詳細を確認してみると解決している部分と問題?が残っている部分があります。

 まず、グループを使っていない GridView や ListView(Split App テンプレートで生成した物が該当)については、問題なく仮想化されるようになっています。

 グループを使っている場合(Grid App テンプレートで生成した物が該当)は、仮想化が行われません。
 ただし、Grid App テンプレートで生成した xaml を見ると、コメントとしてグループ内のアイテムは仮想化されないといった事が書かれており、問題点というよりも、仕様上仮想化されないという扱いなのかもしれません。

マージンの設定

 Consumer Preview でスクロールの問題点等を解決するために、GridView や ListView を ScrollViewer の外に出してしまうと、マージンが適切に設定されなくなってしまうという問題がありました。
 Release Preview で ScrollViewer の外に出してもマージンが適切に設定されるようになったと思ったのですが、細かく見てみると問題がありました。

 Release Preview で GridView や ListView の Padding に指定した値が内部のアイテム全体のマージンとして使用されるようになったようですが、GridView の右側の値と ListView の下の値が有効になっていないようです。
 右や下のマージンは Padding に指定はされているのですが、動作を見る限り無視されています。

 Grid App の場合、右側にマージンが設定されているように見えますが、これはグループのマージンが有効になっているだけです。本来は左側と同じ間隔になるはずですが、左側と比べると狭くなってしまっています。

 Split App の場合、右側のマージンは完全になくなってしまっています。

 また、スナップ時の ListView の下側のマージンもありません。

 この問題については、スタイル定義等で対処できると思われますので、次回確認してみたいと思います。

GridView の問題点について

 ※この記事では問題が解決しているように書かれていますが、まだ問題点がある事がわかりました。以下の記事を参照してください。
 Release Preview における GridView の問題点 (1) - rebuild

 以前から、何度か Visual Studio 11 Beta のテンプレートで生成したプロジェクトでの GridView や ListView の問題点について書いてきましたが、Windows 8 の Release Preview と同時に Visual Studio 2012 RC も公開されましたので、問題点が解決しているか確認してみました。

 Visual Studio 2012 RC で Grid App のテンプレートを生成して動作を見てみたところ、マウスホイールでのスクロースもキーボードによるカレントアイテム移動に合わせたスクロールも行われるようになっていました。

 生成された xaml を見てみると GridView が ScrollViewer の中ではなく単体で配置されるようになっています。単体で配置されていますので、確認はしていませんが仮想化も行われるようになっていると思われます。

<GridView
    x:Name="itemGridView"
    AutomationProperties.AutomationId="ItemGridView"
    AutomationProperties.Name="Grouped Items"
    Grid.Row="1"
    Margin="0,-3,0,0"
    Padding="116,0,40,46"
    ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
    ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
    SelectionMode="None"
    IsItemClickEnabled="True"
    ItemClick="ItemView_ItemClick">

 また、単体で配置した時のマージンについては、 Padding に設定した値が GridView 内の ScrollViewer と ItemsPresenter との間に間隔を空けるために使用されるようになったようです。

 この対応によりテンプレートからの生成後に対策を行う必要なくなり、以降はテンプレートから生成した物をそのまま使用してアプリを作成していけば問題ないと思います。