かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

UWPで電話だけにアプリバーを出したい

matatabi-ux.hateblo.jp

に乗っかりネタです。その上、VS2015RTMを入れちゃったので29日までWin10SDK入れれないので妄想で書いてます(重要)

別画面アプローチ

電話とPCは画面大きく違うから別画面で作ればいいじゃん?っていうのは個人的に賛成です。1画面は苦しい。

でも…

同じ画面に押し込めたいときもあるというか、同じ画面に押し込むときはどうしたらいいの?というのを考えてみました。頭の体操ですね。

まず、7インチより小さい端末の場合電話であるとみなします。そうするとUWPのサンプルにある

github.com

を使うと出来ます。以下にあるコードのように、デバイスが何インチかをとることが出来ます。(それC++でできるよ!事案)

github.com

#include "pch.h"
#include "DisplaySizeClass.h"
#include "sysinfoapi.h"

using namespace DisplaySizeHelper;
using namespace Platform;

DisplaySizeClass::DisplaySizeClass()
{
}

double DisplaySizeHelper::DisplaySizeClass::GetDisplaySizeInInches()
{
    double sizeInInches = 7;
    HRESULT result = GetIntegratedDisplaySize(&sizeInInches);
    if (result == S_OK)
        return sizeInInches;
    else
        return -1; //This will happen for non-integrated displays like monitors, projection surfaces etc.,
}

これをC#から呼んでやることで、何インチか取得できるようになります。C#にAPI用意してくれてもいいのにって思いますけどこれが現実。こいつを使ってごりっと出来そうではあります。

DeviceFamilyの取得

何インチかという情報に頼るほかにデバイスファミリーを取得する方法があります。こちらは丁寧にVisualStateManagerのStateTriggerのサンプルを上げてくださってる方がいます。

github.com

この中の、DeviceFamilyAdaptiveTriggerを使うとモバイルの時だけVSMを切り替えるといったことが出来ます。

// Copyright (c) Morten Nielsen. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using Windows.Foundation.Metadata;
using Windows.UI.Xaml;

namespace WindowsStateTriggers
{
    /// <summary>
    /// Trigger for switching between Windows and Windows Phone
    /// </summary>
    public class DeviceFamilyAdaptiveTrigger : StateTriggerBase, ITriggerValue
    {
        private static string deviceFamily;

        static DeviceFamilyAdaptiveTrigger()
        {
            deviceFamily = Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamily;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DeviceFamilyAdaptiveTrigger"/> class.
        /// </summary>
        public DeviceFamilyAdaptiveTrigger()
        {
        }

        /// <summary>
        /// Gets or sets the device family to trigger on.
        /// </summary>
        /// <value>The device family.</value>
        public DeviceFamily DeviceFamily
        {
            get { return (DeviceFamily)GetValue(DeviceFamilyProperty); }
            set { SetValue(DeviceFamilyProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="DeviceFamily"/> DependencyProperty
        /// </summary>
        public static readonly DependencyProperty DeviceFamilyProperty =
            DependencyProperty.Register("DeviceFamily", typeof(DeviceFamily), typeof(DeviceFamilyAdaptiveTrigger),
            new PropertyMetadata(DeviceFamily.Unknown, OnDeviceTypePropertyChanged));

        private static void OnDeviceTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = (DeviceFamilyAdaptiveTrigger)d;
            var val = (DeviceFamily)e.NewValue;
            if (deviceFamily == "Windows.Mobile")
                obj.IsActive = (val == DeviceFamily.Mobile);
            else if (deviceFamily == "Windows.Desktop")
                obj.IsActive = (val == DeviceFamily.Desktop);
            else if (deviceFamily == "Windows.Team")
                obj.IsActive = (val == DeviceFamily.Team);
            else if (deviceFamily == "Windows.Universal")
                obj.IsActive = (val == DeviceFamily.Universal);         
            else
                obj.IsActive = (val == DeviceFamily.Unknown);
        }

       #region ITriggerValue

        private bool m_IsActive;

        /// <summary>
        /// Gets a value indicating whether this trigger is active.
        /// </summary>
        /// <value><c>true</c> if this trigger is active; otherwise, <c>false</c>.</value>
        public bool IsActive
        {
            get { return m_IsActive; }
            private set
            {
                if (m_IsActive != value)
                {
                    m_IsActive = value;
                    base.SetActive(value);
                    if (IsActiveChanged != null)
                        IsActiveChanged(this, EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// Occurs when the <see cref="IsActive" /> property has changed.
        /// </summary>
        public event EventHandler IsActiveChanged;

       #endregion ITriggerValue
    }

    /// <summary>
    /// Device Families
    /// </summary>
    public enum DeviceFamily
    {
        /// <summary>
        /// Unknown
        /// </summary>
        Unknown = 0,
        /// <summary>
        /// Desktop
        /// </summary>
        Desktop = 1,
        /// <summary>
        /// Mobile
        /// </summary>
        Mobile = 2,
        /// <summary>
        /// Team
        /// </summary>
        Team = 3,
        /// <summary>
        /// Windows universal (for some reason this is returned by IoT
        /// </summary>
        Universal = 255
    }
}

こいつを使うと以下のように書けるはずです(未コンパイル)

<VisualState x:Name="MobileState">
  <VisualState.Triggers>
    <l:DeviceFamilyAdaptiveTrigger DeviceFamily="Mobile" />
  </VisualState.Triggers>
  <VisualState.Setter>
    ...ここにモバイルのときの状態...
  </VisualState.Setter>
</VisualState>
<VisualState x:Name="DesktopState">
  <VisualState.Setter>
    ...ここにモバイルじゃないときの状態...
  </VisualState.Setter>
</VisualState>

上記のSetterでAppBarの表示非表示を切り替えればOKっぽいです。

まとめ

早くRTM版で試したい。