かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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版で試したい。