Microsoft 系のあれこれ

港区の SIer で よくわからんことをしている人です。Xamarin 中心でした。(過去形)

Tech Summit の資料・動画が公開されました

shunsukekawai.hatenablog.com
↑こちらで登壇した旨を記載しましたが、資料、動画が公開されました。

資料

docs.com

なにかご質問があれば Twitter かコメントで遠慮なくどうぞー!

【Xamarin 開発の真実】 Microsoft Tech Summit で登壇してきました!

11/1,2 で開催された Microsoft Tech Summit でテクニカルスポンサー枠として登壇してきました。

microsoft-events.jp

弊社は Mobile 系の開発や Windows 系の開発を推進してきた経緯もあり、今回 日本マイクロソフト株式会社 様より Xamarin でイベントアプリ開発のご依頼を受け、その流れで登壇をさせていただきました。

講演内容

【Xamarin 開発の真実】イベントアプリの中身、すべてお見せします

と名前負けしそうなタイトルとなりましたが、主な内容としては以下の通りです。

  • Xamarin とは
  • イベントアプリの構成
  • Xamarin と UX
  • Xamarin と認証
  • Xamarin と Push 通知 + α

f:id:ShunsukeKawai:20161102191047p:plain

それぞれどんなことを話したか簡単にまとめます。

Xamarin とは

このサイト見てくださいw
www.jmas.co.jp

イベントアプリの構成

イベントアプリの構成はこんな感じですー。ってそのままの話です。
f:id:ShunsukeKawai:20161102161853p:plain
サーバーは別の会社さんが開発をしていて、そこにアプリから接続してデータのやり取りをしています。

Xamarin と UX

標準だけでいけると思ってましたがやはりカスタマイズが必要になったので、Custom Renderer や Effects を利用しなきゃダメだよってお話し。
f:id:ShunsukeKawai:20161102172853p:plain
f:id:ShunsukeKawai:20161102172939p:plain
f:id:ShunsukeKawai:20161102173047p:plain

Xamarin と 認証

イベントアプリでは Microsoft アカウントでのサインインをする必要があります。
認証機能をどうやって作っているのか、デモを交えて説明しました。
f:id:ShunsukeKawai:20161102183406p:plain
f:id:ShunsukeKawai:20161102183414p:plain

詳細はここを参照ください。
blog.xamarin.com

Xamarin と Push 通知 + α

Azure Mobile Engagement を利用して Push 通知やユーザー状況の確認、クラッシュログなどが簡単に利用できるってお話し。
f:id:ShunsukeKawai:20161102183944p:plain
f:id:ShunsukeKawai:20161102184031p:plain
f:id:ShunsukeKawai:20161102184041p:plain
こんな感じでアクティブなユーザー数がリアルタイムで表示されます。
f:id:ShunsukeKawai:20161102190000p:plain
どの画面がよく使われているかをグラフィカルに表示してくれます。
f:id:ShunsukeKawai:20161102190017p:plain

詳細はここを参照ください。
azure.microsoft.com
※上記リンクは Xamarin.Android 用 & ちょっと古いので、変更が必要な箇所があります。

  • using は Microsoft.Azure.Engagement と Microsoft.Azure.Engagement.Activity じゃなくて Microsoft.Azure.Engagement.Xamarin だけでいい
  • 標準だと res フォルダーの中に Mobile Engagement 用の画像等が入るので、Resources の drawable フォルダ、layout フォルダに移動してあげないといけない

Azure Mobile Engagement を使ってみて

セッション中も参加者の方々にご協力いただき、アプリをアクティブにしていただいてリアルタイムに数がぐーんと上がるのをお見せしましたが、あんな機能を数行のコードで実現できてしまうのはホントにスゴイと思います。
Push 通知も簡単に管理できます!(デモでは失敗しましたが…)
マーケティングの方にすごい喜ばれる機能が盛りだくさんな感じです。
是非お試しください!!
azure.microsoft.com

Visual Studio から 作成した iOS の ipa をアプリケーションローダーに喰わせると ITMS-90023 が発生する

Xamarin で開発して iOS の AppStore にアップロードするための ipa を作成する際にアプリケーションローダーを使用してます。

Visual Studio の Xamarin.Forms のテンプレートから作ったプロジェクトをそのままアップロードすると以下のエラーが発生します。

ERROR ITMS-90023: "Missing required icon file. The bundle does not contain an app icon for iPad of exactly '167x167' pixels, in .png format for iOS versions supporting iPad Pro."

f:id:ShunsukeKawai:20161013161447p:plain

対応方法

Info.plist を XML エディター等で開きます。
f:id:ShunsukeKawai:20161013161924p:plain

その中のアイコンの指定が並んでいる箇所を選択して

<array>
	<string>Icon-72@2x.png</string>
	<string>Icon-72.png</string>
	<string>Icon@2x.png</string>
	<string>Icon.png</string>
	<string>Icon-60@2x.png</string>
	<string>Icon-76.png</string>
	<string>Icon-76@2x.png</string>
	<string>Default.png</string>
	<string>Default@2x.png</string>
	<string>Default-568h@2x.png</string>
	<string>Default-Portrait.png</string>
	<string>Default-Portrait@2x.png</string>
	<string>Icon-Small-50@2x.png</string>
	<string>Icon-Small-50.png</string>
	<string>Icon-Small-40.png</string>
	<string>Icon-Small-40@2x.png</string>
	<string>Icon-Small.png</string>
	<string>Icon-Small@2x.png</string>
</array>

の中に

	<string>Icon-83.5@2x.png</string>

を追加します。

あとは対応するサイズ(167x167)のアイコンを追加すればOKです。

注意点

Visual Studio 上で iOS のプロジェクトのプロパティを編集(プロビジョニングファイル等の設定を編集)して保存すると上記のplistの設定が勝手に消えますw
なので、申請用 ipa を作成する際にはちゃんと確認した方がいいかもですね。。

追記

田淵さん(@ytabuchi)に教えていただいた方法の方が楽ちんですね。
ytabuchi.hatenablog.com

(おそらく)世界一簡単にXamarin.Formsのアプリに特定の値でQRコードを生成して表示する方法

public MainPage()
{
    var QrValue = "QRコードにしたい文字列";

    var imgQr = new Image { Aspect = Aspect.AspectFit };
    imgQr.Source = ImageSource.FromUri(new Uri($"http://chart.apis.google.com/chart?cht=qr&chs=200x200&chld=H|0&chl={QrValue}"));

    Content = imgQr;
}

以上!


というのも寂しいので、ちょっと解説です。
みなさんご存知の通り、XamarinにはURLを画像ソースに指定する機能があります。
developer.xamarin.com

それを利用してGoogleQRコード生成のAPIを呼び出し、その結果を設定しているだけです。
QR Codes  |  Infographics  |  Google Developers
※Warning: This API is deprecated. Please use the actively maintained Google Charts API instead. See our deprecation policy for details.
 ということで現在、このAPIは非推奨のようなのでいつ使えなくなるかわかりません。
 また、非推奨になったことでライセンスもよくわかりませんでした。
 なので自己責任でお願いします

画像として返却してくれるAPIであればこれでなくてもなんでも大丈夫だと思います。

さらにおまけで…

こんな感じでイメージコントロールとインジケーターを重ねておいてイメージの読み込みが完了するまでぐるぐるが表示されるようにするといい感じですね。
(このAPIからのレスポンスが一瞬なのでぐるぐるが見えることはないかもしれません。。。)

public MainPage()
{
    var QrValue = "QRコードにしたい文字列";

    var imgQr = new Image { Aspect = Aspect.AspectFit };
    imgQr.Source = ImageSource.FromUri(new Uri($"http://chart.apis.google.com/chart?cht=qr&chs=200x200&chl={QrValue}"));

    var prgIndicator = new ActivityIndicator{
                                HorizontalOptions = LayoutOptions.CenterAndExpand,
                                VerticalOptions = LayoutOptions.CenterAndExpand
                            };

    imgQr.PropertyChanged += (sender, e) =>
    {
        if (e.PropertyName != nameof(imgQr.IsLoading))
            return;
        prgIndicator.IsRunning = imgQr.IsLoading;
        prgIndicator.IsVisible = imgQr.IsLoading;
    };

    Content = new Grid { Children = { prgIndicator, imgQr } };
}

Xamarin.FormsでUWPをReleaseビルドすると埋め込みリソースの画像が表示されない

以下の環境でUWPをReleaseビルドするとPCL側に配置した埋め込みリソースの画像が表示されませんでした。
・Xamarin.Forms 2.3.1.114
・VisualStudio 2015 Enterprise Update 3
・UWP対象ターゲット 10586


Xamarin.Formsで画像を表示する際に全プラットフォームで共通の画像を使いまわしたい場合、埋め込みリソースを使用する場合があると思います。
画像の設定方法については公式を参照ください。
developer.xamarin.com

んで、埋め込みリソースを使っていて、かつUWPのReleaseビルド(ビルドプロパティの「.Net ネイティブ ツール チェーンでコンパイルする」にチェックついている状態)で作成したアプリを実行するとデバッグコンソールに以下のエラーが出力されて画像が表示されませんでした。

例外がスローされました: 'System.IO.FileNotFoundException' (System.Private.Reflection.Core.dll の中)

んで、対症療法です。(解決方法と書いていいか自信ない…)
というか、こうやったらできた。

画像のパスを設定する際にAssemblyを引数に加えてやると表示されるようになりました。

var assembly = typeof(App).GetTypeInfo().Assembly;
img.Source = ImageSource.FromResource("XamarinApp1.Images.xamarin.png", assembly);

.Net Nativeさん怖い…

Androidでアプリアンインストール時の確認メッセージに「~は次のアプリの一部です」と出てくる

Android開発に慣れている人は当たり前のことなのかもしれませんが)
Xamarin.Formsでデフォルトの設定で作成したアプリをアンインストールをしようとすると、以下のように謎の確認メッセージがでてきます。
Android 5以降で確認)
f:id:ShunsukeKawai:20160808115816p:plain

変ですよね。
普通のアプリはこんな感じになります。
f:id:ShunsukeKawai:20160808115850p:plain


それで直し方。

アプリの名前なので、AndroidManifestの記載とMainActivity.csの記載を一致させると期待する結果になりました。
f:id:ShunsukeKawai:20160808120150p:plain
f:id:ShunsukeKawai:20160808120159p:plain

結果
f:id:ShunsukeKawai:20160808120354p:plain

上の例はテスト用の命名でしたが、通常の開発だとプロジェクトの命名にスペースは普通入れないけど、アプリの命名にはスペース入れるってケースは多いと思います。
その場合、MainActivity側を修正しないといけないって点がちょっとハマりました…
(MainActivityの記載はプロジェクト作成時に勝手に作成されて、かつ違いがスペースの有無だけなので…)

"ConvertResourcesCases" タスクが予期せずに失敗しました。

備忘

Xamarinでソリューションのファイルに日本語のパスが含まれているとAndroidのビルド時にタイトルのエラーが出る。
ユーザー名が日本語だと意図せず日本語パスが含まれる可能性もあるから要注意。

エラー "ConvertResourcesCases" タスクが予期せずに失敗しました。
System.IO.DirectoryNotFoundException: パス 'C:\Users\kawai\Documents\Visual Studio 2015\Projects\XamarinTestApp縺・XamarinApp\XamarinApp.Droid\obj\Debug\__library_projects__\Xamarin.Forms.Platform.Android\library_project_imports\res' の一部が見つかりませんでした。
場所 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
場所 System.IO.FileSystemEnumerableIterator`1.CommonInit()
場所 System.IO.FileSystemEnumerableIterator`1..ctor(String path, String originalUserPath, String searchPattern, SearchOption searchOption, SearchResultHandler`1 resultHandler, Boolean checkHost)
場所 System.IO.Directory.EnumerateDirectories(String path, String searchPattern, SearchOption searchOption)
場所 Xamarin.Android.Tasks.ConvertResourcesCases.FixupResources(ITaskItem item, Dictionary`2 acwMap)
場所 Xamarin.Android.Tasks.ConvertResourcesCases.FixupResources(Dictionary`2 acwMap)
場所 Xamarin.Android.Tasks.ConvertResourcesCases.Execute()
場所 Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
場所 Microsoft.Build.BackEnd.TaskBuilder.d__26.MoveNext() XamarinApp.Droid

Android only allows one navigation page on screen at a time

備忘

Xamarin.FormsのAndroid実行でタイトルのようなエラーが出たら、

だめ

public App()
{
    MainPage = new NavigationPage(new MediaPage());
}

だいじょうぶ

public App()
{
    MainPage = new MainPage();
}

UWPでカメラを使う際の注意点

UWPでカメラを使ったアプリを作る場合、一番簡単なのは CameraCaptureUI を使用した方法です。

細かい使い方はMSDN参照↓

CameraCaptureUI を使った写真とビデオのキャプチャ - Windows app development

この方法であればマニフェストファイルを「Webカメラ」の宣言も不要なままカメラを使えます。

ただ、この方法だと「アプリケーションのテンポラリーフォルダ(ms-appdata:///temp/)」に「CCapture.png」というファイル名でファイルがドンドン溜まっていきます。

なので、気付いたら保存した覚えのない写真がいつまでも残っちゃうなんてことがあるので要注意です。


ファイルにしたくない、細かい設定がしたい、とかの場合は MediaCapture を使用する方がよいみたいです。

MediaCapture を使った写真とビデオのキャプチャ - Windows app development

MSDN サブスクリプション ユーザーのXamarin登録方法

XamarinがVisualStudioユーザーに無料でついてくるようになりTLがその話題で持ち切りですが、実際にアクティベーションする方法がわかりづらく、また、どこにも書いてなかったのでメモ。


<前提>
Visual Studio Enterprise with MSDN のライセンスの場合です。
その他のサブスクリプションに関してはわかりません。


MSDNサブスクリプションのページへ行きます。
https://msdn.microsoft.com/ja-jp/subscriptions/manage


② アカウントタブの「Xamarin Studio (for OS X)」の「Register and download」を選択
f:id:ShunsukeKawai:20160406133449p:plain


③ Xamarinの登録ページに飛ぶので必要事項を記入して登録!!!
f:id:ShunsukeKawai:20160406154257p:plain

f:id:ShunsukeKawai:20160406133552p:plain
無料だーー!

GridView(ListView)のアイテムを選択した時に凹む動作をやめたい

UWPでGridViewもしくはListViewをただ単にパネルとしてボタンとかのコントロールを置いて使いたいケースがありました。

UWPから標準のGridViewItemはPressed時に押した箇所が凹むアニメーションが勝手に入ります。
それがちょっと用途からして邪魔だったので消しました。


MSDNのページからスタイルをコピーしてきて、、
GridViewItem スタイルとテンプレート - Windows app development

悪さをしそうなStoryboardを消してきます。
根本はこいつですね↓
PointerDownThemeAnimation

ついでにPointerOver時に枠に色が付くのとかも消しました。

こんな感じ↓
f:id:ShunsukeKawai:20160328203940g:plain

XAML

一応アイテムクリックが動くのか、アイテムの中のボタンは動くのかの確認用のイベント入れてます。

        <GridView IsItemClickEnabled="True" ItemClick="GridView_ItemClick">
            <!--動くGridViewItem-->
            <GridViewItem>
                <StackPanel Width="300" Height="300">
                    <StackPanel.Background>
                        <ImageBrush Stretch="Fill" ImageSource="Assets/saru.PNG"/>
                    </StackPanel.Background>
                    <TextBlock FontSize="30" Text="動くGridViewItem" />
                    <Button Click="Button_Click" Content="テストボタン" />
                </StackPanel>
            </GridViewItem>
            <!--動かないGridViewItem-->
            <GridViewItem Style="{StaticResource DisableEffectGridViewItemStyle}">
                <StackPanel Width="300" Height="300">
                    <StackPanel.Background>
                        <ImageBrush Stretch="Fill" ImageSource="Assets/saru.PNG"/>
                    </StackPanel.Background>
                    <TextBlock FontSize="30" Text="動かないGridViewItem" />
                    <Button Click="Button_Click" Content="テストボタン" />
                </StackPanel>
            </GridViewItem>
        </GridView>
Style
        <Style x:Key="DisableEffectGridViewItemStyle" TargetType="GridViewItem">
            <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
            <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
            <Setter Property="TabNavigation" Value="Local" />
            <Setter Property="IsHoldingEnabled" Value="True" />
            <Setter Property="HorizontalContentAlignment" Value="Center" />
            <Setter Property="VerticalContentAlignment" Value="Center" />
            <Setter Property="Margin" Value="0,0,4,4" />
            <Setter Property="MinWidth" Value="{ThemeResource GridViewItemMinWidth}" />
            <Setter Property="MinHeight" Value="{ThemeResource GridViewItemMinHeight}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="GridViewItem">
                        <Grid x:Name="ContentBorder"
                              Background="{TemplateBinding Background}"
                              BorderBrush="{TemplateBinding BorderBrush}"
                              BorderThickness="{TemplateBinding BorderThickness}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal">
                                        <Storyboard>
                                            <PointerUpThemeAnimation Storyboard.TargetName="ContentPresenter" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="PointerOver" />
                                    <VisualState x:Name="Pressed" />
                                    <VisualState x:Name="Selected" />
                                    <VisualState x:Name="PointerOverSelected" />
                                    <VisualState x:Name="PressedSelected" />
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="DisabledStates">
                                    <VisualState x:Name="Enabled" />
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0"
                                                             Storyboard.TargetName="ContentBorder"
                                                             Storyboard.TargetProperty="Opacity"
                                                             To="{ThemeResource ListViewItemDisabledThemeOpacity}" />
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="FocusStates">
                                    <VisualState x:Name="Unfocused" />
                                    <VisualState x:Name="Focused">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0"
                                                             Storyboard.TargetName="FocusVisualWhite"
                                                             Storyboard.TargetProperty="Opacity"
                                                             To="1" />
                                            <DoubleAnimation Duration="0"
                                                             Storyboard.TargetName="FocusVisualBlack"
                                                             Storyboard.TargetProperty="Opacity"
                                                             To="1" />
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="MultiSelectStates">
                                    <VisualState x:Name="MultiSelectDisabled">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MultiSelectSquare" Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                                                <DiscreteObjectKeyFrame KeyTime="0:0:0.333" Value="Collapsed" />
                                            </ObjectAnimationUsingKeyFrames>
                                            <FadeOutThemeAnimation TargetName="MultiSelectSquare" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="MultiSelectEnabled">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MultiSelectSquare" Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                                            </ObjectAnimationUsingKeyFrames>
                                            <FadeInThemeAnimation TargetName="MultiSelectSquare" />
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="DataVirtualizationStates">
                                    <VisualState x:Name="DataAvailable" />
                                    <VisualState x:Name="DataPlaceholder">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextBlock" Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderRect" Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="ReorderHintStates">
                                    <VisualState x:Name="NoReorderHint" />
                                    <VisualState x:Name="BottomReorderHint">
                                        <Storyboard>
                                            <DragOverThemeAnimation Direction="Bottom"
                                                                    ToOffset="{ThemeResource GridViewItemReorderHintThemeOffset}"
                                                                    TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="TopReorderHint">
                                        <Storyboard>
                                            <DragOverThemeAnimation Direction="Top"
                                                                    ToOffset="{ThemeResource GridViewItemReorderHintThemeOffset}"
                                                                    TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="RightReorderHint">
                                        <Storyboard>
                                            <DragOverThemeAnimation Direction="Right"
                                                                    ToOffset="{ThemeResource GridViewItemReorderHintThemeOffset}"
                                                                    TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="LeftReorderHint">
                                        <Storyboard>
                                            <DragOverThemeAnimation Direction="Left"
                                                                    ToOffset="{ThemeResource GridViewItemReorderHintThemeOffset}"
                                                                    TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0:0:0.2" To="NoReorderHint" />
                                    </VisualStateGroup.Transitions>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="DragStates">
                                    <VisualState x:Name="NotDragging" />
                                    <VisualState x:Name="Dragging">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0"
                                                             Storyboard.TargetName="ContentBorder"
                                                             Storyboard.TargetProperty="Opacity"
                                                             To="{ThemeResource ListViewItemDragThemeOpacity}" />
                                            <DragItemThemeAnimation TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="DraggingTarget">
                                        <Storyboard>
                                            <DropTargetItemThemeAnimation TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="MultipleDraggingPrimary">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0"
                                                             Storyboard.TargetName="MultiArrangeOverlayBackground"
                                                             Storyboard.TargetProperty="Opacity"
                                                             To="1" />
                                            <DoubleAnimation Duration="0"
                                                             Storyboard.TargetName="MultiArrangeOverlayText"
                                                             Storyboard.TargetProperty="Opacity"
                                                             To="1" />
                                            <DoubleAnimation Duration="0"
                                                             Storyboard.TargetName="ContentBorder"
                                                             Storyboard.TargetProperty="Opacity"
                                                             To="{ThemeResource ListViewItemDragThemeOpacity}" />
                                            <FadeInThemeAnimation TargetName="MultiArrangeOverlayBackground" />
                                            <FadeInThemeAnimation TargetName="MultiArrangeOverlayText" />
                                            <DragItemThemeAnimation TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="MultipleDraggingSecondary">
                                        <Storyboard>
                                            <FadeOutThemeAnimation TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="DraggedPlaceholder">
                                        <Storyboard>
                                            <FadeOutThemeAnimation TargetName="ContentBorder" />
                                        </Storyboard>
                                    </VisualState>
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0:0:0.2" To="NotDragging" />
                                    </VisualStateGroup.Transitions>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <ContentPresenter x:Name="ContentPresenter"
                                              Margin="{TemplateBinding Padding}"
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                              ContentTransitions="{TemplateBinding ContentTransitions}" />
                            <TextBlock x:Name="PlaceholderTextBlock"
                                       Margin="{TemplateBinding Padding}"
                                       AutomationProperties.AccessibilityView="Raw"
                                       Foreground="{x:Null}"
                                       IsHitTestVisible="False"
                                       Text="Xg"
                                       Visibility="Collapsed" />
                            <Rectangle x:Name="PlaceholderRect"
                                       Fill="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
                                       Visibility="Collapsed" />
                            <Rectangle x:Name="MultiArrangeOverlayBackground"
                                       Grid.ColumnSpan="2"
                                       Fill="{ThemeResource ListViewItemDragBackgroundThemeBrush}"
                                       IsHitTestVisible="False"
                                       Opacity="0" />
                            <Rectangle x:Name="BorderRectangle"
                                       IsHitTestVisible="False"
                                       Opacity="0"
                                       Stroke="{ThemeResource SystemControlHighlightListAccentLowBrush}"
                                       StrokeThickness="2" />
                            <Border x:Name="MultiSelectSquare"
                                    Width="20"
                                    Height="20"
                                    Margin="0,2,2,0"
                                    HorizontalAlignment="Right"
                                    VerticalAlignment="Top"
                                    Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}"
                                    Visibility="Collapsed">
                                <FontIcon x:Name="MultiSelectCheck"
                                          FontFamily="{ThemeResource SymbolThemeFontFamily}"
                                          FontSize="16"
                                          Foreground="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                                          Glyph="&#xE73E;"
                                          Opacity="0" />
                            </Border>
                            <Rectangle x:Name="FocusVisualWhite"
                                       IsHitTestVisible="False"
                                       Opacity="0"
                                       Stroke="{ThemeResource SystemControlForegroundAltHighBrush}"
                                       StrokeDashArray="1.0, 1.0"
                                       StrokeDashOffset="1.5"
                                       StrokeEndLineCap="Square"
                                       StrokeThickness="2" />
                            <Rectangle x:Name="FocusVisualBlack"
                                       IsHitTestVisible="False"
                                       Opacity="0"
                                       Stroke="{ThemeResource SystemControlForegroundBaseHighBrush}"
                                       StrokeDashArray="1.0, 1.0"
                                       StrokeDashOffset="0.5"
                                       StrokeEndLineCap="Square"
                                       StrokeThickness="2" />
                            <TextBlock x:Name="MultiArrangeOverlayText"
                                       Grid.ColumnSpan="2"
                                       Margin="18,9,0,0"
                                       AutomationProperties.AccessibilityView="Raw"
                                       FontFamily="{ThemeResource ContentControlThemeFontFamily}"
                                       FontSize="26.667"
                                       Foreground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
                                       IsHitTestVisible="False"
                                       Opacity="0"
                                       Text="{Binding RelativeSource={RelativeSource TemplatedParent},
                                                      Path=TemplateSettings.DragItemsCount}"
                                       TextTrimming="WordEllipsis"
                                       TextWrapping="Wrap" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

Slackでよく使うショートカットとか

メモメモ

Alt + クリック : 既読→未読
入力領域で↑  : 直近の自分の投稿を再編集する
``` で囲む   : 囲ったところを引用
> (半角)    : その行を引用

Window.Current.Bounds.Height の罠

Mobileで実行しているUWPで画面いっぱいにポップアップを出す際の注意点です。


最初に結論から言うと、

  • ポップアップを開く前にcs上でポップアップのサイズを動的に指定して画面いっぱいにする
  • Window.Current.Bounds は危険
  • Mobileは通知領域やOSボタンを考慮しましょう(GetForCurrentView().VisibleBoundsを使えば大丈夫!)

です。

今回使用したサンプルアプリは一番下にまるまる載せているので、ご参考までに。

サンプルアプリの概要

  • 画面上にデバイスファミリー、Window.Current.Bounds(画面サイズ)、ApplicationView.GetForCurrentView().VisibleBoundsのサイズを表示
  • アプリをフルスクリーンモードへの切り替え
  • Window.Current.Boundsのサイズと同じサイズのポップアップを表示する
  • ページの一番上の階層のGridのサイズと同じサイズのポップアップを表示する

では実際に見てみましょう。

PCでサイズの確認

サンプルアプリを動かすと以下のようになります。
タイトルバーやツールバーの有無が変わるので、それぞれのモードで数字は違いますが、
Window.Current.BoundsとVisibleBoundsは両方同じ数字になってますね。

Windowモード時

f:id:ShunsukeKawai:20160217192143p:plain

フルスクリーンモード時

f:id:ShunsukeKawai:20160217192156p:plain

PCでPopup表示

Window.Current.Boundsを設定する方法

ポップアップを開く前にWindow.Current.Boundsをポップアップに設定して表示します。
ポップアップにはテキストを並べてスクロールするようになっています。
「1」を並べて、一番下だけ「2」になっています。

grdPop.Height = Window.Current.Bounds.Height;
grdPop.Width = Window.Current.Bounds.Width;

popTest.IsOpen = true;

f:id:ShunsukeKawai:20160217192232p:plainf:id:ShunsukeKawai:20160217192240p:plain
当然ながら一番下までスクロールできますね。

VisibleBoundsを設定する方法

最初に確認した通り、数字が一緒なので、同じ結果になります。
同じなので、画像は割愛します。

var view = ApplicationView.GetForCurrentView();
grdPop.Height = view.VisibleBounds.Height;
grdPop.Width = view.VisibleBounds.Width;

popTest.IsOpen = true;

それでは問題のMobileです。

Mobileで確認

同じアプリをNuAns NEOで表示してみます。
フルスクリーンモードでは同じですが、Windowモードの場合、
Window.Current.Bounds.HeightとVisibleBounds.Heightの数字が異なっています。
そうです。
こいつが諸悪の根源です。

Windowモード時

f:id:ShunsukeKawai:20160217203950p:plain 

フルスクリーンモード時

f:id:ShunsukeKawai:20160217203957p:plain

Window.Current.Boundsだと、Mobileの通知領域やOS標準のボタン領域も含めた単純な画面サイズが取得されてしまいます。
ApplicationView.GetForCurrentView().VisibleBounds だと、現在のビューに表示されている領域のサイズが取得されます。
そのため、このような差が生まれてしまうのですね。

では続いてポップアップを表示してみましょう。

MobileでPopup表示

Window.Current.Boundsを設定する方法

最終行である「2」がボタン領域の下に隠れてしまっています。
f:id:ShunsukeKawai:20160217210616g:plain

VisibleBoundsを設定する方法

正常にすべて表示されます。
f:id:ShunsukeKawai:20160217210723g:plain

まとめ

PCでは同じだけど、Mobileだと異なるっていうのがいやらしいですね。
ウチのアプリの場合、PC版を作って後からMobileに対応ってパターンがあるので、ハマりがちです。
全部見直さないと。。。。。

ソース

MainPage.xaml.cs

using Windows.System.Profile;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;

namespace WindowSizeTest
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private void grdMain_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //Page_SizeChangeはなぜかMobileのFullスクリーンモードの切り替えだと動いてくれないため、
            //GridのSizeChangeでTextの再設定を行う
            setText();
        }

        private void ToggleButton_Checked(object sender, RoutedEventArgs e)
        {
            changeViewMode(true);
        }

        private void ToggleButton_Unchecked(object sender, RoutedEventArgs e)
        {
            changeViewMode(false);
        }

        private void btnShowPopupWindowBounds_Click(object sender, RoutedEventArgs e)
        {
            grdPop.Height = Window.Current.Bounds.Height;
            grdPop.Width = Window.Current.Bounds.Width;

            popTest.IsOpen = true;
        }
        private void btnShowPopupVisibleBounds_Click(object sender, RoutedEventArgs e)
        {
            var view = ApplicationView.GetForCurrentView();
            grdPop.Height = view.VisibleBounds.Height;
            grdPop.Width = view.VisibleBounds.Width;

            popTest.IsOpen = true;
        }

        private void grdPop_Tapped(object sender, TappedRoutedEventArgs e)
        {
            popTest.IsOpen = false;
        }

        private void changeViewMode(bool isFull)
        {
            var view = ApplicationView.GetForCurrentView();

            if (isFull)
            {
                view.TryEnterFullScreenMode();
            }
            else
            {
                view.ExitFullScreenMode();
            }
        }

        private void setText()
        {
            txtDevice.Text = AnalyticsInfo.VersionInfo.DeviceFamily;
            txtWindowHeight.Text = Window.Current.Bounds.Height.ToString();
            txtWindowWidth.Text = Window.Current.Bounds.Width.ToString();

            var view = ApplicationView.GetForCurrentView();

            txtVisibleBoundsHeight.Text = view.VisibleBounds.Height.ToString();
            txtVisibleBoundsWidth.Text = view.VisibleBounds.Width.ToString();
        }
    }
}


MainPage.xaml

<Page x:Class="WindowSizeTest.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:local="using:WindowSize"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">

    <Page.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="FontSize" Value="28" />
        </Style>
        <Style x:Key="txtValueStyle" TargetType="TextBlock">
            <Setter Property="FontSize" Value="28" />
            <Setter Property="Foreground" Value="Red" />
        </Style>
        <Style TargetType="Button">
            <Setter Property="Margin" Value="10" />
            <Setter Property="FontSize" Value="28" />
        </Style>
        <Style TargetType="ToggleButton">
            <Setter Property="FontSize" Value="28" />
        </Style>
    </Page.Resources>
    <Grid x:Name="grdMain"
          Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          SizeChanged="grdMain_SizeChanged">
        <Viewbox VerticalAlignment="Top">
            <Grid x:Name="grdContent">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <StackPanel Margin="10,0">
                    <TextBlock Text="AnalyticsInfo.VersionInfo.DeviceFamily" />
                    <TextBlock Text="Window.Current.Bounds.Height" />
                    <TextBlock Text="Window.Current.Bounds.Width" />
                    <TextBlock Text="GetForCurrentView().VisibleBounds.Height" />
                    <TextBlock Text="GetForCurrentView().VisibleBounds.Width" />
                    <ToggleButton Margin="10"
                                  Checked="ToggleButton_Checked"
                                  Content="FullScreen"
                                  Unchecked="ToggleButton_Unchecked" />
                    <Button x:Name="btnShowPopupWindowBounds"
                            Click="btnShowPopupWindowBounds_Click"
                            Content="ShowPopupWindowBounds" />
                    <Button x:Name="btnShowPopupVisibleBounds"
                            Click="btnShowPopupVisibleBounds_Click"
                            Content="ShowPopupVisibleBounds" />
                </StackPanel>
                <StackPanel Grid.Column="1">
                    <TextBlock x:Name="txtDevice" Style="{StaticResource txtValueStyle}" />
                    <TextBlock x:Name="txtWindowHeight" Style="{StaticResource txtValueStyle}" />
                    <TextBlock x:Name="txtWindowWidth" Style="{StaticResource txtValueStyle}" />
                    <TextBlock x:Name="txtVisibleBoundsHeight" Style="{StaticResource txtValueStyle}" />
                    <TextBlock x:Name="txtVisibleBoundsWidth" Style="{StaticResource txtValueStyle}" />
                </StackPanel>
            </Grid>
        </Viewbox>
        <Popup x:Name="popTest">
            <Grid x:Name="grdPop"
                  Background="AliceBlue"
                  Tapped="grdPop_Tapped">
                <ScrollViewer>
                    <StackPanel>
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="1" />
                        <TextBlock Text="2" />
                    </StackPanel>
                </ScrollViewer>
            </Grid>
        </Popup>
    </Grid>
</Page>

Raspberry Pi 2 Model B に Windows 10 IoT Core for Raspberry Pi 2 をインストールする

毎年、弊社では社内ハッカソンを行っているのですが、私はWindows系が本業なのでそれを生かしつつ、ちょっと新しいことをやりたいなと。
そして昨年のde:Codeの抽選で当たったラズパイが埋もれていたのでこいつを使うことにしました。
とりあえず環境構築を行いました。
(何を作るかはまた後日。。。)

諸々を購入

本体はあるものの、周辺機器が何もないので、社内ハッカソン経費で以下を購入
無線LANアダプター(※後述しますが、これは使用しませんでした)

BUFFALO 11n対応 11g/b 無線LAN子機 親機-子機デュアルモード対応モデル WLI-UC-GNM2

BUFFALO 11n対応 11g/b 無線LAN子機 親機-子機デュアルモード対応モデル WLI-UC-GNM2

・SDカード

・ディスプレイ

・カメラ

・色々入っているキット

Raspberry Pi電子工作エントリーキット(Premium)

Raspberry Pi電子工作エントリーキット(Premium)

ヒートシンク

Raspberry Pi Model B+ / Pi2用 ヒートシンクセット

Raspberry Pi Model B+ / Pi2用 ヒートシンクセット

LEGO(本体ケースやカメラケースを自作しようかなと思い)

レゴ クラシック アイデアパーツ<エクストラセット> 10702

レゴ クラシック アイデアパーツ<エクストラセット> 10702

SDカードにWindows 10 IoT Core for Raspberry Pi 2をインストール

色々調べた結果、「raspberry pi windows10」でググると一番上にくる(2016/1/26時点)↓のサイトの様にPCでファイルをダウンロードして、イメージファイルをどうのこうの。。。とかは全く必要ありませんでした。
ascii.jp

Microsoftさんが、丁寧にまとめてくれていたので、こちらを参考にしました。
ms-iot.github.io

PCでこのサイトの真ん中へんの「Get the Windows 10 IoT Core Dashboard」をクリック
f:id:ShunsukeKawai:20160126210016p:plain

すると、IoT端末を管理するためのデスクトップアプリがPCにインストールされます。
インストール後、起動すると、、
f:id:ShunsukeKawai:20160126210812p:plain
え?勝手にやってくれる系?
すごい簡単そう。。。

んで、指示通りにSDカードをPCに接続し、インストール実行!
f:id:ShunsukeKawai:20160126211002p:plain
f:id:ShunsukeKawai:20160128155747p:plain
終わったけど、ちゃんとやってくれたのか不安を抱えながらラズパイにSDカードを接続して起動してみました。
ちなみにラズパイの起動は電源を挿すと起動します。

f:id:ShunsukeKawai:20160126212159j:plain

起動した!!
簡単過ぎ!!!

ネットワークに接続

インストールはできたけど、Windows 10 IoTはネットワークにつないでPCと通信ができないと何もできません。
※私も勘違いしていたのですが、「Windows」と銘打つからにはデスクトップがあって、エクスプローラーがあって、、とかと想像してましたが違います。
(そうなっていた方が「Windows」としての強み、他のOSとの差別化としてはいいと思うんですがね。。)

なので、ネットワークにつなぎます。
ここで無線LANアダプターの登場です。
「Raspberry Pi 無線LAN」とかでググるとたくさん出てきたので、(どうせ会社の金だし!と)あまり調べず購入したのですが、結論から言うと使えませんでした。
挿しても認識してくれません。

Raspbian OSだと大丈夫の様ですが、Windows 10 IoTでは
ms-iot.github.io
こちらに書いてある機器しかサポートしていないらしい。。。

そのため、なぜか会社にあったコイツ↓を使用してエセ無線LAN環境を構築しました。不幸中の幸いです。
これはちょっと高いですが、コンバーターとして使えるラズパイとLANケーブルでつなぐようなモノは探せばもっと安いのがあると思います。

PCと同じネットワークに接続して、こちらに書いてある通りにPCからラズパイにアクセスします。
ms-iot.github.io

・ラズパイからIPアドレスを確認
IPアドレス+「:8080」をブラウザでたたく
・ユーザーID:Administrator、パスワード:p@ssw0rdで接続

以上でラズパイの管理画面に接続できました。
めでたし、めでたし。


後日、作成したアプリとかをインストールしたいと思います。

※※※※※※※※※※※※※※※※※
2016/1/29 追記
カメラモジュールも使えませんでした...orz

Windows 10 Mobile でアプリケーションのフォルダーを参照したい

=================================================

追記:最新のバージョンでは下記手順はできなくなってます。

どうしよう。。。

=================================================

 

UWPの開発で ApplicationData.Current.LocalFolder(= ms-appdata:///local/) とかにログを出力したりするケースがあると思います。

 

ですが、Windows 10 Mobileの標準エクスプローラーでは大したところは見えません。

↓ このぐらいです。

f:id:ShunsukeKawai:20160107193942p:plain

 検索しても見つけてくれません。

f:id:ShunsukeKawai:20160107205325p:plain

なので、どうにかしてログファイルをみたいな、と。

 

調べてみたところ、以下のサイトに行きつきました。

<参考サイト>

forums.windowscentral.com

参考にして試行錯誤の末、なんとかできましたのでまとめときます。

もっとスマートな方法があったら教えてください。。。

 

1.PCでCドライブ直下のショートカットを作成する

なんでやねん。

なんでMobileってゆーのにいきなりPCが登場するんじゃい。

と突っ込みを入れたくなりますが、そこはこらえてください。

 

  • Cドライブを右クリック

f:id:ShunsukeKawai:20160107195201p:plain

  • ショートカットの作成

f:id:ShunsukeKawai:20160107200038p:plain

2.作成したショートカットをMobileに移動する

参考サイトの通り、実機でやるとUSBで簡単なのですが、今回はエミュレーターでの手順を記載しておきます。

f:id:ShunsukeKawai:20160107200831p:plain

  • SD Card タブを選択
  • ショートカットを置いた適当なフォルダーを指定
  • Insert SD Card を選択

f:id:ShunsukeKawai:20160107201213p:plain

f:id:ShunsukeKawai:20160107201441p:plain

  • SDCARD が生まれているので選択

f:id:ShunsukeKawai:20160107201557p:plain

  • 作成したショートカットがある

f:id:ShunsukeKawai:20160107201655p:plain

 

これで、Windows 10 Mobile のCドライブ直下を参照することができました。

 

3.アプリケーションのフォルダーを見つける

あとは簡単じゃーん。と思いの方もいるかもしれません。

そう思っていた時期が私にもありました。

 

答えから言うと、インストールしたパッケージたちは

C:\Data\Users\DefApps\AppData\Packages

にあります。

 

では見てみましょう。

  •  Cドライブ直下

f:id:ShunsukeKawai:20160107202422p:plain

  • Data フォルダ

f:id:ShunsukeKawai:20160107202532p:plain

  • Users フォルダ

f:id:ShunsukeKawai:20160107202611p:plain

  •  DefApps フォルダ

f:id:ShunsukeKawai:20160107202642p:plain

  •  AppData フォルダ

f:id:ShunsukeKawai:20160107204811p:plain

はい、空ですー!

隠しフォルダなのでしょうかね。

ということで・・・

  • DefApps フォルダに戻って検索ボタン

f:id:ShunsukeKawai:20160107202642p:plain

 

  • 「Package」と検索するとヒット!!

f:id:ShunsukeKawai:20160107205519p:plain

  • お目当てのアプリのパッケージ名を選択

f:id:ShunsukeKawai:20160107205743p:plain

LocalStateがありましたよー!!

 

あとはふつうに中に入っているログファイルでもなんでも見れる!!

 

こんな面倒なことしないといけないのでしょうか。。。

ひとまずよかった。