Quantcast
Channel: SourceChord
Viewing all 153 articles
Browse latest View live

UWP Community Toolkitを使ってみた

$
0
0

先日MSが、こんなライブラリを公開してました。

https://blogs.windows.com/buildingapps/2016/08/17/introducing-the-uwp-community-toolkit/#ezOozvSa1jfh3Er2.97

てことで、さっそく使ってみました。

概要・UWP Community Toolkitとは

MS製のUWP向けライブラリです。
アプリ作成の上でのフレームワークなどではなく、純粋なコントロール類や便利な各種ヘルパークラスなどが主な内容となっています。 各種機能も、数行のコードを書くだけで簡単に利用できるように作られていて、お気軽に使えます。
また、このライブラリを使うことで、アプリ全体をこう書かなきゃみたいな強制が無いので、その点でも非常に取り回しのよいライブラリだと思います。

ちなみに、MSはこのライブラリへのフィードバックを今後のWin10のSDKへと反映していくつもり、とのことです。
このライブラリで特に有用で安定してきた機能は、将来SDKに標準で取り込まれるかもしれません。

公式のサンプルアプリ

このライブラリでできることを簡単に体験できるよう、サンプルアプリが公開されてます。
https://www.microsoft.com/ja-jp/store/p/uwp-community-toolkit-sample-app/9nblggh4tlcq

まずは、ストアから以下のアプリをインストールして試してみるとよいかと。
f:id:minami_SC:20160924123730p:plain

UWP Community Toolkitでできることが一通りサンプルとして提供されています。
f:id:minami_SC:20160924123739p:plain

このアプリからコードのコピペなどもできるようになっています。
ライブラリの動作を見てみるだけでなく、開発時にコードスニペットの取得元、みたいな位置づけとしても便利かもしれません。
f:id:minami_SC:20160924123755p:plain

ロードマップ

今後のアップデートについては、以下のように書かれています。
https://github.com/Microsoft/UWPCommunityToolkit/issues?q=is%3Aopen+is%3Aissue+milestone%3Av1.1
https://github.com/Microsoft/UWPCommunityToolkit/milestones
月に一回くらいのアップデートを計画しているようですね。

準備

UWP Community Toolkitを使うには、以下の環境が必要です。

VS2015をUpdate3にしてない場合は、まずアップデートしておきましょう。

ライブラリのインストール

インストールはNugetから行えるようになっています。

UWP Community Toolkitでは、機能ごとに複数のアセンブリに分かれています。
アセンブリ一覧は↓のページにまとまっています。
https://developer.microsoft.com/en-us/windows/uwp-community-toolkit/nugetpackages

自分が必要な機能のアセンブリを適宜取得していく、という感じで使います。

これらのアセンブリは、NugetのGUIからMicrosoft.Toolkit.UWPで検索すると出てきます。
f:id:minami_SC:20160924123807p:plain

ここから探せば、目的のものをすぐ見つけられるかと。

使ってみる

ということで、さっそく使ってみます。

細かいことは、公式リポジトリのリンクからリファレンスを読むとよいですが、 ここでは特に便利そうと感じた、主な機能/コントロールの使い方をまとめてみます。

Control類

まずは、各種コントロール類から。
ハンバーガーメニューを作るコントロールをはじめ、sdkに標準で入れておいてほしいような便利なコントロールが多数用意されています。

この辺のコントロール類を使うときは、Microsoft.Toolkit.Uwp.UI.Controlsというパッケージをインストールします。
Nugetパッケージマネージャ コンソールから、以下のコマンドでインストールできます。

Install-Package Microsoft.Toolkit.Uwp.UI.Controls
HeaderedTextBlock

項目のタイトルを表示することができるようになっているテキストブロックです。 これはコードだけ見ればなんとなく使えるかと。

<StackPanel><controls:HeaderedTextBlock Margin="5"Header="Name"Text="UWP Toolkit" /><controls:HeaderedTextBlock Margin="5"Header="Title"Orientation="Horizontal"Text="ここに内容を書く" /></StackPanel>

f:id:minami_SC:20160924124333p:plain

プロパティ名内容
Header項目のヘッダーを設定
Text項目の本文を設定
Orientationヘッダーと本文の並ぶ方向を設定
RangeSelector

普通のスライダーと違い、下限値/上限値の二つを設定できるようになっているものです。
こういうコントロール、確かに微妙にほしくなる時ありますよね。

<controls:RangeSelector Maximum="100"Minimum="10"RangeMax="50"RangeMin="30" />

f:id:minami_SC:20160924124341p:plain

プロパティ名内容
Maximum現在の設定値(上限側)を設定
Minimum現在の設定値(下限側)を設定
RangeMax設定可能な値の最大値を設定(スライダーの右端の値)
RangeMin設定可能な値の最小値を設定(スライダーの左端の値)
AdaptiveGrid

これは結構使いどころの多そうなコントロール
画像のサムネイル一覧とか表示するときに便利そうです。

こんな風に、各グリッドの幅がDesiredWidth以上になるように保った上で、入るだけ列を作ってAdaptiveGridの幅いっぱいに埋める、って感じの動作です。

f:id:minami_SC:20160924124404p:plain:w300
f:id:minami_SC:20160924124420p:plain:w600

MainPage.xaml

<Page.Resources><DataTemplate x:Key="PhotosTemplate"><Grid Background="White"BorderBrush="Black"BorderThickness="1"><Image HorizontalAlignment="Center"VerticalAlignment="Center"Source="{Binding}"Stretch="UniformToFill" /></Grid></DataTemplate></Page.Resources><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><controls:AdaptiveGridView Margin="5"DesiredWidth="300"ItemHeight="200"ItemTemplate="{StaticResource PhotosTemplate}"ItemsSource="{x:Bind ImageList}" /></Grid>

MainPage.xaml.cs

publicsealedpartialclass MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            this.ImageList = new List<string>()
            {
                "Images/1.JPG",
                "Images/2.JPG",
                "Images/3.JPG",
                "Images/4.JPG",
                "Images/5.JPG",
                "Images/6.JPG",
            };
        }

        public IList<string> ImageList { get; set; }
    }
プロパティ名内容
DesiredWidth各グリッドの最小幅のサイズを設定
ItemHeight各グリッドの高さを設定
OneRowModeEnabledtrueにすると、1行だけ表示するようになります。
ItemsSourceグリッドに表示する要素のリストを設定
ItemTemplateグリッドに表示する各アイテムのテンプレートを設定
ItemClickCommand各要素をクリックした際に呼び出されるコマンドを設定
イベント名内容
ItemClick各要素をクリックした際に発生するイベント
SlidableListItem

横フリックで要素の選択などができるコントロールです。
ListBoxやListViewなどのItemTemplateとして使い、リストの各要素のテンプレートとして使うことを想定されたコントロールです。
スマホ向けのUIとかで、結構便利に使えそうですね。

MainPage.xaml

<Page.Resources><DataTemplate x:Key="EmailsItemTemplate"><controls:SlidableListItem MinWidth="300"MaxWidth="800"HorizontalAlignment="Stretch"LeftIcon="Message"LeftLabel="ダイアログを表示"LeftCommandRequested="SlidableListItem_LeftCommandActivated"RightIcon="Delete"RightLabel="削除します"RightCommandRequested="SlidableListItem_RightCommandActivated"MouseSlidingEnabled="True"><Grid Height="110"Background="WhiteSmoke"><TextBlock Grid.Column="1"Margin="12"VerticalAlignment="Center"FontSize="16"FontWeight="Light"Text="{Binding}"TextWrapping="NoWrap" /></Grid></controls:SlidableListItem></DataTemplate></Page.Resources><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><ListView x:Name="listView"HorizontalAlignment="Center"IsItemClickEnabled="False"ItemTemplate="{StaticResource EmailsItemTemplate}"ItemsSource="{x:Bind ImageList, Mode=OneWay}"SelectionMode="None"><ListView.ItemContainerStyle><Style TargetType="ListViewItem"><Setter Property="HorizontalContentAlignment"Value="Stretch" /><Setter Property="Margin"Value="0,1" /></Style></ListView.ItemContainerStyle></ListView></Grid>

MainPage.xaml.cs

publicsealedpartialclass MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            this.ImageList = new ObservableCollection<string>()
            {
                "Item1",
                "Item2",
                "Item3",
                "Item4",
                "Item5",
                "Item6",
            };
        }

        public ObservableCollection<string> ImageList { get; set; }

        private async void SlidableListItem_LeftCommandActivated(object sender, EventArgs e)
        {
            var item = (sender as SlidableListItem).DataContext asstring;

            var dlg = new Windows.UI.Popups.MessageDialog(item);
            await dlg.ShowAsync();
        }

        privatevoid SlidableListItem_RightCommandActivated(object sender, EventArgs e)
        {
            ImageList.Remove((sender as SlidableListItem).DataContext asstring);
        }
    }

f:id:minami_SC:20160924124459p:plain:w400

HamburgerMenu

名前の通り、ハンバーガーメニューを作るためのコントロール。 このコントロールを使うと、簡単にハンバーガーメニューを伴ったUIを実装できます。

ここでのサンプルでは、選択した画像を表示しているだけですが、HamburgerMenu内部にFrameコントロールを配置してページ切替できるようにすると、よく見かける感じのUIになるかと思います。

MainPage.xaml

<Page.Resources><DataTemplate x:Key="DefaultTemplate"x:DataType="local:MenuItem"><Grid Width="240"Height="48"><Grid.ColumnDefinitions><ColumnDefinition Width="48" /><ColumnDefinition /></Grid.ColumnDefinitions><Image Margin="3"Source="{x:Bind ImagePath}"Stretch="UniformToFill" /><TextBlock Grid.Column="1"VerticalAlignment="Center"FontSize="16"Foreground="White"Text="{x:Bind Title, Mode=OneWay}" /></Grid></DataTemplate></Page.Resources><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><controls:HamburgerMenu x:Name="hamburgerMenuControl"Foreground="White"ItemClick="OnMenuItemClick"ItemTemplate="{StaticResource DefaultTemplate}"ItemsSource="{x:Bind List}"OptionsItemTemplate="{StaticResource DefaultTemplate}"PaneBackground="Black"><Image x:Name="imgSelected"Margin="10" /></controls:HamburgerMenu></Grid>

MainPage.xaml.cs

f:id:minami_SC:20160924124511p:plain

RadialGauge

これは、使いどころを選びそうなコントロールですね。
車の速度計みたいな表示のコントロールです。

<StackPanel><controls:RadialGaugeGrid.Column="1"Value="{Binding ElementName=slider, Path=Value}"Minimum="0"Maximum="180"TickSpacing="20"ScaleWidth="26"Unit="Units"TickBrush="Gainsboro"ScaleTickBrush="{ThemeResource ApplicationPageBackgroundThemeBrush}"UnitBrush="Black"ValueBrush="Black"NeedleWidth="5"TickLength="18" /><Slider x:Name="slider"Margin="10"Minimum="0"Maximum="180"/></StackPanel>

f:id:minami_SC:20160924124529p:plain:w250

Animations

アニメーション効果を簡単に加えるためのクラス類です。
コードビハインドから呼ぶことも、xaml上からビヘイビアとして設定することもできます。

それぞれの使い方を順番に見ていきます。

コードビハインドからのアニメーション

BlurOffsetFadeRotateScaleといったアニメーション用の拡張メソッドが用意されています。

using Microsoft.Toolkit.Uwp.UI.Animations;という名前空間の設定を加えると、 以下のように、FrameworkElementやUIElementのようなUI要素に対して、これらのアニメーション用の拡張メソッドが呼び出せるようになります。

MainPage.xaml

<Border x:Name="border"Margin="10"HorizontalAlignment="Left"VerticalAlignment="Top"Background="LightGray"BorderBrush="Black"BorderThickness="1"Padding="10"><FontIcon FontFamily="Segoe MDL2 Assets"FontSize="128"Glyph="&#xE170;" /></Border><Button Margin="10,165,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Content="Button"Click="Button_Click" />

MainPage.xaml.cs

privatevoid Button_Click(object sender, RoutedEventArgs e)
        {
            this.border.Blur(value: 10, duration: 100, delay: 100)
                       .StartAsync();
        }
メソッドチェーンでつなぐ

以下のようにメソッドチェーンで複数のアニメーションをつなぎ、順次実行することもできます。

privatevoid Button_Click(object sender, RoutedEventArgs e)
        {
            this.border.Blur(value: 10, duration: 500)
                       .Offset(offsetX: 50, offsetY: 100, delay: 500)
                       .Rotate(value: 60, duration: 500, delay: 1000)
                       .StartAsync();
        }
ビヘイビアを用いたアニメーション

以下のように、ビヘイビアとしてこれらのアニメーションを定義することもできます。

<Border Margin="10"HorizontalAlignment="Left"VerticalAlignment="Top"Background="LightGray"BorderBrush="Black"BorderThickness="1"Padding="10"><interactivity:Interaction.Behaviors><behaviors:Blur x:Name="BlurBehavior"AutomaticallyStart="False"Delay="100"Duration="1000"Value="10" /></interactivity:Interaction.Behaviors><FontIcon FontFamily="Segoe MDL2 Assets"FontSize="128"Glyph="&#xE170;" /></Border><Button Margin="10,165,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Content="Button"><interactivity:Interaction.Behaviors><core:EventTriggerBehavior EventName="Click"><core:CallMethodAction MethodName="StartAnimation"TargetObject="{Binding ElementName=BlurBehavior}" /></core:EventTriggerBehavior></interactivity:Interaction.Behaviors></Button>

各種Helperなど

コンバーターやちょっとしたユーティリティクラスなども用意されています。

color

RGBではなく、HSV表色系で色を表す、HsvColor構造体などが用意されています。

また、HSV値や16進数文字列のRGB値などからColor構造体を作るためのヘルパークラスなどなど、色を扱う上で便利な機能が用意されてます。

            var hsv = new HsvColor();
            hsv.H = 180;

            var col1 = ColorHelper.FromHsv(180, 0.5, 0.5);
            var col2 = ColorHelper.ToColor("Red");
            var col3 = ColorHelper.ToColor("#FF00FF");
コンバーター

以下のようなコンバーターが用意されています。

使用する場合は、xamlで以下のような名前空間の設定をしておきます。

xmlns:Converters="using:Microsoft.Toolkit.Uwp.UI.Converters"
名前内容
BoolToVisibilityConverterbool値をVisibilityに変換
CollectionVisibilityConverterコレクションの要素が0個の場合、Visibility.Collapsedに変換するコンバーター
StringFormatConverterConverterParameterで指定した書式で文字列をフォーマットして返すコンバーター
StringVisibilityConverter文字列がnullまたは空文字の場合に、Visibility.Collapsedに変換するコンバーター

UWPではBooleanToVisibilityConverterが標準ではなくて何かと不便ですが、そんな時にはここで用意されているBoolToVisibilityConverterが使えます。

外部サービスとの連携

BingやTwitter, Facebookといった、定番どころのWebServiceとの連携を簡単に行うための各種ヘルパークラスも用意されています。

こんな風に簡単にBing検索結果を利用したりすることができます。 Microsoft.Toolkit.Uwp.ServicesというパッケージをNugetでインストールして、以下のように使います。

<Page x:Class="UwpToolkitTest.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:bing="using:Microsoft.Toolkit.Uwp.Services.Bing"xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"xmlns:core="using:Microsoft.Xaml.Interactions.Core"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="using:UwpToolkitTest"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><Page.Resources><DataTemplate x:Key="SearchResultTemplate"x:DataType="bing:BingResult"><Grid Margin="0,5,10,5"><Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition /></Grid.RowDefinitions><TextBlock Grid.Row="0"Grid.Column="0"FontWeight="Bold"Text="{x:Bind Title}"TextTrimming="CharacterEllipsis" /><TextBlock Grid.Row="0"Grid.Column="1"HorizontalAlignment="Right"Text="{x:Bind Published}" /><TextBlock Grid.Row="1"Grid.Column="0"Grid.ColumnSpan="2"Text="{x:Bind Summary}"TextWrapping="Wrap" /><HyperlinkButton Grid.Row="2"Grid.Column="0"Grid.ColumnSpan="2"Content="{x:Bind Link}"NavigateUri="{x:Bind Link}" /></Grid></DataTemplate></Page.Resources><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition /></Grid.RowDefinitions><TextBox x:Name="txtKerword"Width="250"Margin="10"HorizontalAlignment="Left"VerticalAlignment="Top"Text="Windows 10"TextWrapping="Wrap" /><Button Margin="265,10,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Click="Button_Click"Content="検索" /><ListView x:Name="lstResult"Grid.Row="1"Margin="10"ItemTemplate="{StaticResource SearchResultTemplate}" /></Grid></Page>
private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var searchConfig = new BingSearchConfig
            {
                Country = BingCountry.Japan,
                Query = this.txtKerword.Text
            };

            this.lstResult.ItemsSource = await BingService.Instance.RequestAsync(searchConfig, 50);
        }

f:id:minami_SC:20160924124543p:plain


TypeScript2.0での型定義ファイルの管理

$
0
0

とうとう出ました、TypeScript2.0!!

TypeScript 2.0 is now available! | TypeScript

メジャーバージョンアップなので変更点はたくさんありますが、個人的に一番気になっていた、npmからの型定義の取得を試してみます。

参考リンク

TypeScript2.0での変更点とかは、↓の記事がとても詳しいです。
http://qiita.com/vvakame/items/ae239f3d6f6f08f7c719
http://qiita.com/vvakame/items/826bf193dd301862014f

あと、TypeScript2.0での@typesからの型定義ファイルの取得方法は、↓を参考にやってみました。
http://qiita.com/laco0416/items/ed1aadf335f12cd3618d
http://qiita.com/tonkotsuboy_com/items/634b0921b6170cf56813

使ってみる

ここでは、サンプルとしてlodash を読み込んでNode.js環境で使うまでの手順をやってみます。

パッケージ類のインストール

  1. package.jsonの生成
    • フォルダを作り、npm initコマンドで、package.jsonを生成します。
  2. TypeScriptのインストール
    • npm install typescript --save-devとコマンドを実行し、TypeScriptコンパイラをインストールします。
  3. lodashのインストール
    • 続いて、npm install --save lodashのコマンドを実行し、lodashをインストールします。
  4. 型定義ファイルのインストール
    • npm install --save-dev @types/lodashを実行

型定義ファイルを取得するために、別のツールを使わずnpmでそのまま取得できて、とても便利ですね!!

tsconfig.jsonの生成

以下のコマンドを実行し、npmからローカルにインストールしたTypeScriptコンパイラを使って、tsconfig.jsonを生成します。

node_modules\.bin\tsc --init

tsconfig.json

{"compilerOptions": {"module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": true}}

細かい設定などは、お好みで色々変えていけばよいかと。

lodashを使ったサンプルコード

パッケージ類の取得ができたので、これを使ってコードを書いてみます。

index.tsというファイルを作成し、以下のようなコードを書きます。

import * as _ from "lodash";

var src = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var result = _.remove(src, (item) => item % 2 == 0);

console.dir(src);
console.dir(result);

package.jsonの編集

package.jsonscriptsに、TypeScriptのコンパイルなどをするためのタスク定義をしておきます。
こうしておくと、コンソールからnpm startと実行するだけで、TypeScriptのコンパイル⇒実行まで一発でできるようになります。

プロジェクトの構成と、package.jsonは以下のような感じです。
f:id:minami_SC:20160928005357p:plain

package.json

{"name": "typescript2-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {"test": "echo \"Error: no test specified\"&& exit 1",
    "build": "tsc",
    "prestart": "npm run build",
    "start": "node index.js"},
  "author": "",
  "license": "ISC",
  "dependencies": {"@types/lodash": "^4.14.36",
    "lodash": "^4.16.1"},
  "devDependencies": {"typescript": "^2.0.3"}}

VSCode向けの設定

ついでに、VSCode用に色々と設定をしておきます。

ビルド関係のタスク定義

コマンドパレットから、Configure Task Runnerコマンドを実行し、npm向けのタスク定義ファイルを生成します。 f:id:minami_SC:20160928005408p:plain

以下のようにtasks.jsonを書きます。
ビルド用のタスクを定義しているだけです。

tasks.json

{// See https://go.microsoft.com/fwlink/?LinkId=733558// for the documentation about the tasks.json format"version": "0.1.0",
    "command": "npm",
    "isShellCommand": true,
    "showOutput": "always",
    "suppressTaskName": true,
    "tasks": [{"taskName": "build",
            "args": ["run", "build"]}]}

launch.jsonの作成

F5キーを押し、デバッガでの実行をしようとすると、launch.jsonが作られます。

以下のように少しカスタマイズし、sourcemapを使うようにしたり、デバッグ開始前にbuildタスクを走らせたりするようにしています。

{"version": "0.2.0",
    "configurations": [{"name": "起動",
            "type": "node",
            "request": "launch",
            "program": "${workspaceRoot}/index.js",
            "stopOnEntry": false,
            "args": [],
            "cwd": "${workspaceRoot}",
            "preLaunchTask": "build",
            "runtimeExecutable": null,
            "runtimeArgs": ["--nolazy"],
            "env": {"NODE_ENV": "development"},
            "console": "internalConsole",
            "sourceMaps": true,
            "outDir": null},
        {"name": "アタッチ",
            "type": "node",
            "request": "attach",
            "port": 5858,
            "address": "localhost",
            "restart": false,
            "sourceMaps": false,
            "outDir": null,
            "localRoot": "${workspaceRoot}",
            "remoteRoot": null},
        {"name": "プロセスにアタッチ",
            "type": "node",
            "request": "attach",
            "processId": "${command.PickProcess}",
            "port": 5858,
            "sourceMaps": false,
            "outDir": null}]}

settings.jsonの設定

ファイルメニューから「ワークスペース設定」をクリックし、settings.jsonを作成します。

VSCodeの言語サービスの設定

VSCodeのエディタなどでは、VSCodeの言語サービスがTypeScriptのコードを解析し、エラー箇所の表示やコード補間などを行ってくれています。

この言語サービスで使用されるTypeScriptは、VSCodeが内部で持っているTypeScriptコンパイラが利用されています。
このプロジェクトでは、先ほどインストールしたTypeScript2.0を使用してほしいので、settings.jsonに以下のような設定を加え、npmからインストールしたTypeScriptパッケージを利用するように設定します。

"typescript.tsdk": "node_modules/typescript/lib"
その他の設定

以下の設定を加えておくと、tsファイルと同階層にある.js/.js.mapファイルなどが、VSCodeのエクスプローラの表示対象から除外されてスッキリします。

"files.exclude": {
        "**/*.js": {"when": "$(basename).ts"},
        "**/*.map": {"when": "$(basename)"}
    }

settings.json

// 既定の設定とユーザー設定を上書きするには、このファイル内に設定を挿入します{"typescript.tsdk": "node_modules/typescript/lib",
    // ファイルとフォルダーを除外するための glob パターンを構成します。"files.exclude": {"**/*.js": {"when": "$(basename).ts"},
        "**/*.map": {"when": "$(basename)"}}}

NeDBを使ってNode.js環境でお手軽にNoSQLのDBを使ってみる

$
0
0

Node.jsでNoSQLなDBを使うときは、MongoDBなどと組み合わせるのがよくあるパターンかと思います。

ただ、MongoDBは別途インストールが必要なので、ちょっとNoSQLなDBのお勉強という程度で使うには少し面倒です。
また、OS環境へのインストールが必要ということで、Electronで作ったアプリのように、実行するファイル類一式を配布するようなケースにはMongoDBはマッチしません。

そんな時に使えそうなNeDBという組み込み型DBのライブラリがあります。
https://github.com/louischatriot/nedb

NeDBは、完全にJavaScriptだけで書かれているDBなので、npmから取得してきたスクリプト類だけでNoSQLなDBを利用することができます。
アプリのコード一式と一緒に配布するような用途にバッチリなライブラリです。
イメージ的にはNoSQL界のSQLite的な立ち位置ですかね。

ここでは、TypeScriptからNeDBを使ってみます。
ちょうど先日、TypeScript2.0がリリースされたので、TypeScritp2.0を使い、型定義ファイルもnpmから取得する方法で扱ってみたいと思います。

参考リンク

NeDBの各種APIは、MongoDBのAPIのサブセットとなっているため、MongoDBの入門資料などの知識をそのまま流用できます。

以下あたりのリンクが参考になります。
http://dotinstall.com/lessons/basic_mongodb_v3

また、NeDBの使い方の説明は↓の記事がわかりやすいです。
http://qiita.com/tinymouse/items/0731eef4aebf2779bd0b

準備

インストール

  1. package.jsonの生成
    • フォルダを作り、npm initコマンドで、package.jsonを生成します。
  2. TypeScriptのインストール
    • npm install typescript --save-devとコマンドを実行し、TypeScriptコンパイラをインストールします。
  3. NeDBのインストール
    • 続いて、npm install --save nedbのコマンドを実行し、nedbをインストールします。
  4. 型定義ファイルのインストール
    • npm install --save-dev @types/nedbを実行

各種プロジェクトの準備

続いて、色々と設定ファイル類の準備をしていきます。
tsconfig.json/package.jsonをそれぞれ以下のように編集します。

tsconfig.json
{"compilerOptions": {"module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": true}}
package.json
{"name": "nedb-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {"build": "tsc",
    "prestart": "npm run build",
    "start": "node index.js"},
  "author": "",
  "license": "ISC",
  "devDependencies": {"@types/nedb": "0.0.31",
    "typescript": "^2.0.3"},
  "dependencies": {"nedb": "^1.8.0"}}

使ってみる

準備が整ったので、NeDBを使ってサンプルコードを書いてみます。

初めの一歩

とりあえず、以下のようなコードで、オンメモリなDBを作り、そこにドキュメントを作成&読み出しをしてみます。

import Datastore = require('nedb');
var db = new Datastore();

var doc = {
    name: "hoge",
    age: 20
};

db.insert(doc, function(err) {var result = db.find({}, (err, docs) => {
        console.dir(docs);
    });
});

ファイルにデータを保存

import Datastore = require('nedb');
var db = new Datastore({
    filename: 'data/database.db',
    autoload: true});

var doc = {
    name: "hoge",
    age: 20
};

db.insert(doc, function(err) {var result = db.find({}, (err, docs) => {
        console.dir(docs);
    });
});

検索

import Datastore = require('nedb');
var db = new Datastore({
    filename: 'data/database.db',
    autoload: true});

var doc = [{ name: "hoge",  age: 20 },
    { name: "hoge1", age: 25 },
    { name: "hoge2", age: 30 },
];

db.insert(doc, function(err) {var result = db.find({age: 25}, (err, docs) => {
        console.dir(docs);
    });
});

DBファイルの中身

ファイルに保存したDBファイルの中身を見てみると、こんな風にプレーンテキストなJSONデータが格納されてました。

f:id:minami_SC:20161004011728p:plain

所感

MongoDBに慣れた方々だったら、いきなりJavaScriptでも何の問題もなく書けるかもしれませんが、 この手のNoSQL系DBの経験がない自分にとっては、TypeScriptで各種関数の説明や引数の説明が出るのはとても便利でした。
f:id:minami_SC:20161004005611p:plain

APIのドキュメント以上に、インテリセンスがライブラリの使い方を教えてくれる、という感じがします。

SSDに換装してみた

$
0
0

メインで使用しているデスクトップPCのHDDが、SMARTの警告を吐くようになり、そろそろヤバそうなので重い腰を上げてSSDに換装しました。

OSもWin8⇒8.1⇒10とアップデートを繰り返していたので、クリーンインストールするいいタイミングな気もしたので。。。

ちなみに、↓の480GB買いました。

https://www.amazon.co.jp/SanDisk-Extreme-240GB-%E3%83%A1%E3%83%BC%E3%82%AB%E3%83%BC10%E5%B9%B4%E4%BF%9D%E8%A8%BC%E4%BB%98-SDSSDXPS-240G-J25/dp/B00MF27IYM/ref=sr_1_1?ie=UTF8&qid=1475940376&sr=8-1&keywords=sandisk%2Bextreme%2Bpro&th=1

速度や安定性を求めてコレにしてみました。

OSのインストールとかも以上に速くてビックリ。
インストール始めて、10分も経たないうちにデスクトップ画面が表示されるとこまで行きました。

VS2015も、ほぼすべての項目にチェック入れてインストールしましたが、1時間弱でインストール完了。。。
SSDマジすげぇ!!
以前インストールしたときは、一晩かかってたのに・・・・

ここまで劇的にパフォーマンス変わるとは思ってませんでした。
アプリの起動や、日常使いの上でのスピードもかなり向上してます。

うちのメインPC、i7 2600Kの結構古いCPUですが、これでまだしばらくやってけそうな気がしてきました。

Visual Studio Code 1.6の新機能・変更点

$
0
0

VSCode 1.6がリリースされました。
https://code.visualstudio.com/updates/v1_6

今回は割と変更点は少な目ですが、個人的に気になった新機能や変更点などをメモしておきます。

エディタ関連

リリースノートの表示

リリースノートが、Webサイトへのリンクではなく、VSCodeのエディタ領域にタブ表示するようになりました。

f:id:minami_SC:20161012004334p:plain

アイコン表示

VSCode1.5で追加された、ファイル種別ごとのアイコン表示ですが、 VSCodeのエクスプローラ領域以外に、タブのファイル名横などにも表示されるようになりました。
f:id:minami_SC:20161012004412p:plain

VSCodeのウィンドウ切替

現在起動中の、別インスタンスのVSCodeのウィンドウへとスイッチするためのコマンドが追加されました。
コマンドパレットから、Switch Windowというコマンドで別ウィンドウのVSCodeへとフォーカスを移すことができます。

画像サイズと、ファイルサイズ表示

画像を開いたときに、ステータスバーに画像のピクセル数とファイルサイズを表示するようになりました。

f:id:minami_SC:20161012004734p:plain

地味に便利♪

ファイル保存時のオートフォーマット

settings.jsonに以下のような設定をすると、ファイル保存時に自動でインデントなどのフォーマットをしてくれるようになります。

"editor.formatOnSave": true

検索履歴

alt+↑alt+↓で、検索時のキーワードを入力履歴から選ぶことができるようになりました。
f:id:minami_SC:20161012004436p:plain

その他

extensions.json

.vscodeフォルダ内にワークスペースでの使用が推奨される拡張機能一覧を定義するファイルを作れるようになりました。
f:id:minami_SC:20161012004510p:plain

extensions.json

{// See http://go.microsoft.com/fwlink/?LinkId=827846// for the documentation about the extensions.json format"recommendations": [// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp"msjsdiag.debugger-for-chrome",
        "msjsdiag.debugger-for-edge",
        "eg2.tslint"]}

このファイルを含んだワークスペースを開いているときには、このextensions.jsonに書かれた拡張機能を、以下のように一覧表示したり、ここからインストールできるようになっています。
f:id:minami_SC:20161012004520p:plain

NeDB + TypeScriptで、asyncな非同期呼び出しをしてみる

$
0
0

この間使ってみたNeDB、 とても便利なんですが、各種APIがcallback形式のものとなっていて、、安易にコールバック地獄に突入してしまいそうな雰囲気を感じます。
NeDBを使ってNode.js環境でお手軽にNoSQLのDBを使ってみる - SourceChord

せっかくTypeScriptで書くなら、やっぱasync/awaitと一緒に書きたい!!
ということで、asyncな呼び出しができるようなラッパークラスを書いてみました。

コード一式は、以下の場所に上げておきました。 github.com

こんな風に、awaitしながら呼び出すことができます。

async function main() {
    await db.loadDatabaseAsync();

    // 初期データの書き込み
    await db.insertAsync(doc);

    // 全件表示var allItems = await db.findAsync({});
    console.log("■全件表示");
    console.dir(allItems);

    // findでの検索var searchResult1 = await db.findAsync({ name: "hoge2"});
    console.log("■findでの検索");
    console.dir(searchResult1);
}

やっぱこうやってawaitで待機できると、ちょっと入り組んだ処理になっても、とても理解しやすいですね。

こんな風にPromiseでラップした関数呼び出しを追加したクラスを作り、このクラスを通してasyncな関数を呼ぶようにしています。

import * as NeDBDataStore from "NeDB";

exportclass AsyncNeDBDataStore extends NeDBDataStore {/**     * コンストラクタ1     */
    constructor();
    constructor(path: string);
    constructor(options: any);
    constructor(args?: any) {super(args);
    }// 後で、戻り値を考え直すpublic loadDatabaseAsync(): Promise<void> {returnnew Promise((resolve, reject) => super.loadDatabase((err) => {if (err) {
                reject(err);
            }else{
                resolve();
            }}));
    }public ensureIndexAsync(options: Object): Promise<void> {returnnew Promise((resolve, reject) => super.ensureIndex(<any>options, (err) => {if (err) {
                reject(err);
            }else{
                resolve();
            }}));
    }public removeIndexAsync(fieldName: string): Promise<void> {returnnew Promise((resolve, reject) => super.removeIndex(fieldName, (err) => {if (err) {
                reject(err);
            }else{
                resolve();
            }}));
    }public insertAsync<T>(newDoc: T): Promise<T> {returnnew Promise((resolve, reject) => super.insert(newDoc, (err, document) => {if (err) {
                reject(err);
            }else{
                resolve(document);
            }}));
    }public countAsync(query: any): Promise<number> {returnnew Promise((resolve, reject) => super.count(query, (err, n) => {if (err) {
                reject(err);
            }else{
                resolve(n);
            }}));
    }public findAsync<T>(query: any): Promise<Array<T>>;
    public findAsync<T>(query: any, projection: T): Promise<Array<T>>;
    public findAsync<T>(query: any, projection?: T): Promise<Array<T>> {if (projection) {returnnew Promise((resolve, reject) => super.find(query, projection, (err, documents) => {if (err) {
                    reject(err);
                }else{
                    resolve(documents);
                }}));
        }else{returnnew Promise((resolve, reject) => super.find(query, (err, documents) => {if (err) {
                    reject(err);
                }else{
                    resolve(documents);
                }}));
        }}public findOneAsync<T>(query: any): Promise<T>;
    public findOneAsync<T>(query: any, projection: T): Promise<T>;
    public findOneAsync<T>(query: any, projection?: T): Promise<T> {if (projection) {returnnew Promise((resolve, reject) => super.findOne(query, projection, (err, document) => {if (err) {
                    reject(err);
                }else{
                    resolve(document);
                }}));
        }else{returnnew Promise((resolve, reject) => super.findOne(query, (err, document) => {if (err) {
                    reject(err);
                }else{
                    resolve(document);
                }}));
        }}public updateAsync<T>(query: any, updateQuery: any, options: any): Promise<{ numberOfUpdated: number, affectedDocuments: any, upsert: boolean}> {returnnew Promise((resolve, reject) => super.update(query, updateQuery, options, (err, numberOfUpdated, affectedDocuments, upsert) => {if (err) {
                reject(err);
            }else{
                resolve({ numberOfUpdated, affectedDocuments, upsert });
            }}));
    }public removeAsync<T>(query: any): Promise<number>;
    public removeAsync<T>(query: any, options: any): Promise<number>;
    public removeAsync<T>(query: any, options?: any): Promise<number> {if (options) {returnnew Promise((resolve, reject) => super.remove(query, options, (err, n) => {if (err) {
                    reject(err);
                }else{
                    resolve(n);
                }}));
        }else{returnnew Promise((resolve, reject) => super.remove(query, (err, n) => {if (err) {
                    reject(err);
                }else{
                    resolve(n);
                }}));
        }}}

UWP Community Toolkit 1.1がリリースされました

$
0
0

github.com

UWP Community Toolkit 1.1がリリースされました。
このライブラリの初回の月例アップデートですね。

今回追加された中で、面白そうなものをいくつか使ってみました。

BladeControl

Azureのポータルとかで使われているようなブレードコントロールです。

最近この手の横スクロールはあまり使わないので、使いどころは少ないかもしれないですが、面白いコントロールです。

f:id:minami_SC:20161016124727p:plain

<controls:BladeControl><controls:BladeControl.Blades><controls:Blade IsOpen="True"TitleBarVisibility="Collapsed"><controls:Blade.Element><StackPanel Margin="8"><Button Width="180"Height="100"Margin="0, 20, 0, 0"controls:BladeControl.ToggleBlade="Blade1"Content="ブレード1" /><Button Width="180"Height="100"Margin="0, 20, 0, 0"controls:BladeControl.ToggleBlade="Blade2"Content="ブレード2" /></StackPanel></controls:Blade.Element></controls:Blade><controls:Blade Title="ブレード1"BladeId="Blade1"IsOpen="False"><controls:Blade.Element><TextBlock HorizontalAlignment="Center"VerticalAlignment="Center"Text="色々説明とか・・・・" /></controls:Blade.Element></controls:Blade><controls:Blade Title="ブレード2"BladeId="Blade2"IsOpen="True"><controls:Blade.Element><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition /></Grid.ColumnDefinitions><TextBlock HorizontalAlignment="Center"VerticalAlignment="Center"Text="名前" /><TextBox Grid.Column="1"Margin="5"HorizontalAlignment="Left"VerticalAlignment="Top"Width="200"Text="ほげほげ"TextAlignment="Right"TextWrapping="Wrap" /><Button Grid.Row="1"Grid.Column="1"Margin="5"HorizontalAlignment="Right"VerticalAlignment="Top"Content="設定" /></Grid></controls:Blade.Element></controls:Blade></controls:BladeControl.Blades></controls:BladeControl>

GridSplitter

WPFには普通にあったのに、なぜかUWPに無かったヤツ。 レイアウトをユーザーが微調整できるようなUIを作るときに便利!!

f:id:minami_SC:20161016124742p:plain

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Column="0"
                   Stroke="LightGray"
                   StrokeThickness="1" />
        <Rectangle Grid.Column="1"
                   Stroke="LightGray"
                   StrokeThickness="1" />
        <Rectangle Grid.Column="2"
                   Stroke="LightGray"
                   StrokeThickness="1" />
        
        <TextBlock Text="123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 " TextWrapping="Wrap" />
        <TextBlock Grid.Column="1"
                   Text="123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 "
                   TextWrapping="Wrap" />
        <TextBlock Grid.Column="2"
                   Text="123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 "
                   TextWrapping="Wrap" />

        <controls:GridSplitter Width="11"
                               HorizontalAlignment="Right"
                               Background="LightGray"
                               GripperCursor="Default"
                               GripperForeground="White"
                               ResizeBehavior="BasedOnAlignment"
                               ResizeDirection="Auto" />
    </Grid>

.NET Coreことはじめ

$
0
0

RTMになってからしばらく経ちますが、ちょっとやってみたのでφ(..)メモメモ

.NET Coreというと、ASP.NET Core向けのチュートリアルが多いですが、 ASP.Net Coreを使っていきなりWebサーバーを作ると、多くのファイルが一気にでてきて理解が難しくなりそうです。
そこで、.NET Coreを使ってコンソールアプリを作るところから順に始めたいと思います。

Node.jsを学ぶ際も、最初からexpressを使ったWebサイトの作成とかはせず、普通はNode.jsのコンソールアプリから入門しますよね!!
ということで、今日は.NET CoreのインストールからコンソールでHello Worldまで。

インストール

↓を参考に色々インストールします。

VS2015を使う場合は、Update3をインストール後、.NET Core 1.0.0 VS2015 Tooling Preview2というのをインストールすればOKです。

VSの拡張機能の画面から、以下の項目を選んで更新するか、
f:id:minami_SC:20161020130111p:plain
以下のページのリンクから、「.NET Core 1.0.0 - VS 2015 Tooling Preview 2」というのをインストールしてください。
https://www.microsoft.com/net/core#windows

プロジェクトを作ってみる

以下の二通りの方法で、それぞれプロジェクトを作ってみます。

最初に書いた通り、今回は単純なHello Worldアプリを作るだけです。

Visual Studioで作成

VSから作成する場合は、新規プロジェクト作成のダイアログで、.Net Coreの項目から「Console Application(.NET Core)」という項目を選びプロジェクトを作成します。
f:id:minami_SC:20161020130140p:plain

プロジェクトの構造

出来上がったプロジェクトの中身を見てみます。
こんな構成のプロジェクトになっています。
f:id:minami_SC:20161020130214p:plain

Program.csを開き、Main関数内に以下のようにConsole.WriteLine関数を追加して、Hello World!!という文字列の表示を行います。 Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    publicclass Program
    {
        publicstaticvoid Main(string[] args)
        {
            Console.WriteLine("Hello World!!");
        }
    }
}

Ctrl+F5で起動すると、「Hello World!!」が表示されます。
f:id:minami_SC:20161020130241p:plain

コマンドラインで作成

今度はVisual Studioを使わずに、コマンドラインからプロジェクトの作成&実行をしてみます。

dotnetコマンド

先ほどの「.NET Core 1.0.0 VS2015 Tooling Preview2」をインストールすると、dotnetコマンドが使えるようになっています。
Visual Studioは使わない、という場合には、別途「.NET Core SDK for Windows」をインストールしておけば大丈夫かと思います。

まずは、コマンドプロンプトdotnetコマンドを打ってみて、dotnetコマンドが使えるようになっているか確認しておきましょう。
f:id:minami_SC:20161020130306p:plain

VSCodeの拡張機能

VSCodeで編集する場合は、以下の拡張機能をインストールしておきます。
f:id:minami_SC:20161020130319p:plain

プロジェクト作成

プロジェクト用に適当な名前でディレクトリを作成しておきます。
この作成したディレクトリをカレントディレクトリにしてコマンドラインを開き、以下のコマンドを実行します。

dotnet new

すると、以下のようなプロジェクトとの雛形が作成されます。
f:id:minami_SC:20161020130332p:plain

Program.csは、以下のように単純なHelloWorldなコードとなっています。 Program.cs

using System;

namespace ConsoleApplication
{
    publicclass Program
    {
        publicstaticvoid Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

続いて、依存するアセンブリ類を取得します。
以下のコマンドを実行すればOK!!

dotnet restore

すると、project.lock.jsonというファイルが作成され、依存関係などの情報がすべて書き込まれたファイルが出来上がります。
依存ファイルの依存ファイルの・・・、と再帰的にたどって記録するようなので、結構でっかいファイルが出来上がってます。

Node.js環境でnpmを使うときのshrinkwrap.jsonのような立ち位置のファイルなのかな。

実行方法

以下のコマンドで、コンパイル&実行できます。

dotnet run

f:id:minami_SC:20161020130355p:plain

このとき、bin/Debugフォルダに以下のようなファイル類が出力されます。
デフォルトの設定では、exeファイルではなくdllファイルとして出力されるようです。
f:id:minami_SC:20161020130459p:plain

このdllファイルは、以下のようにdotnetコマンドに引数として渡すことで実行できるようになっています。
f:id:minami_SC:20161020130415p:plain

VSCode用の設定ファイル

先ほど作った雛形プロジェクトをVSCodeで編集していると、以下のようなメッセージが画面上部に表示されています。
f:id:minami_SC:20161020130726p:plain

ここで「Yes」というのを押すと、VSCodeでビルドやデバッグをするためのファイル類が生成されます。
f:id:minami_SC:20161020130508p:plain

launch.json

{"version": "0.2.0",
    "configurations": [{"name": ".NET Core Launch (console)",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            "program": "${workspaceRoot}\\bin\\Debug\\netcoreapp1.0\\dotnetcore-test.dll",
            "args": [],
            "cwd": "${workspaceRoot}",
            "externalConsole": false,
            "stopAtEntry": false,
            "internalConsoleOptions": "openOnSessionStart"},
        {"name": ".NET Core Attach",
            "type": "coreclr",
            "request": "attach",
            "processId": "${command.pickProcess}"}]}

tasks.json

{"version": "0.1.0",
    "command": "dotnet",
    "isShellCommand": true,
    "args": [],
    "tasks": [{"taskName": "build",
            "args": ["${workspaceRoot}\\project.json"],
            "isBuildCommand": true,
            "problemMatcher": "$msCompile"}]}

これで、Ctrl+F5でビルド&実行したり、F5実行でVSCodeのデバッガでデバッグできるようになります。
f:id:minami_SC:20161020130540p:plain

.NET Coreで使用するproject.jsonについて

以前から少し話題には出てますが、project.jsonやxprojというファイルは今後使わなくなり、csprojに統一されるそうですです。
なので、ここで扱ったファイルのいくつかは、今後のバージョンでは使わなくなっていきます。

しかし、dotnet resotredotnet buildなどなど、基本的なdotnetコマンドの使い方は変わらないようなので、この辺のコマンドは今覚えた知識はそのまま使えるので一安心かと。

この辺の話は、以下のブログ記事に今後の情報が載っています。
https://blogs.msdn.microsoft.com/dotnet/2016/10/19/net-core-tooling-in-visual-studio-15/

今回はここまで。


.NET Coreことはじめ~その2~

$
0
0

前回に引き続き、.NET Coreを使ったプロジェクトの基本を見ていきます。

今回はファイルや依存ライブラリの追加、ビルド時の各種設定などを見ていきます。

ファイルの追加など

.Net Coreのプロジェクトでは、今までのcsprojでのファイル管理とは異なり、project.jsonのあるフォルダ以下のファイルはすべてプロジェクトに含まれた状態となるようです。

そのため、プロジェクトにファイルを追加したい場合は、こんな風に単純にファイルを作成してその中にクラスを書くだけでOKです。
f:id:minami_SC:20161023120947p:plain

こんなファイルを用意しました。

Person.cs

namespace Sample
{
    publicclass Person
    {
        publicstring Name { get; set; }

        publicint Age { get; set; }

        public Person (string name, int age)
        {
            this.Name = name;
            this.Age = age;
        }
    }
}

Program.cs

using System;
using Sample;

namespace ConsoleApplication
{
    publicclass Program
    {
        publicstaticvoid Main(string[] args)
        {
            var person = new Person("hoge", 20);
            Console.WriteLine($"{person.Name}さん, {person.Age}才");
        }
    }
}

実行結果は以下の通り。
f:id:minami_SC:20161023121224p:plain

ただ、Win10のコマンドプロンプトで実行したら、こんな風に文字化けしてしまいました・・・
f:id:minami_SC:20161023121230p:plain

文字コードの問題とかですかね。。。

依存ライブラリの追加

Json.NETを使い、JSON形式のデータを扱ってみます。

依存するライブラリを追加するには、以下のようにproject.jsonのdependenciesの項目にライブラリとそのバージョンの情報を追記します。

追記したら、dotnet restoreを実行すればOK!!

project.json

{"version": "1.0.0-*",
  "buildOptions": {"debugType": "portable",
    "emitEntryPoint": true},
  "dependencies": {"Newtonsoft.Json": "9.0.1"},
  "frameworks": {"netcoreapp1.0": {"dependencies": {"Microsoft.NETCore.App": {"type": "platform",
          "version": "1.0.1"}},
      "imports": "dnxcore50"}}}

微妙にメンドイですね。。。
npmでのパッケージインストールみたいにできないのかなぁ、と思いGitHubのissuesを眺めていたら、以下のようなページを見つけました。
https://github.com/dotnet/cli/issues/1578

今後のアップデートで、ライブラリをインストールするためのコマンドなども追加されるのかな??

準備ができたら、Program.csを以下のように書き換えて実行してみます。

Program.cs

using System;
using Sample;
using Newtonsoft.Json;

namespace ConsoleApplication
{
    publicclass Program
    {
        publicstaticvoid Main(string[] args)
        {
            var person = new Person("hoge", 20);
            var json = JsonConvert.SerializeObject(person, Formatting.Indented);
            Console.WriteLine(json);
        }
    }
}

こんな風に、フォーマットされたJSON文字列が出力されます。
f:id:minami_SC:20161023121245p:plain

exeファイルとしてビルドする

最後に、実行可能なexeファイル形式としてビルドをしてみます。

project.jsonのファイルを、以下の手順で編集します。

  1. "type": "platform"と書かれている部分を削除します。
  2. runtimesという項目を追加し、以下のように対象のプラットフォームを書きます。
"runtimes": {"win10-x64": {},
    "osx.10.11-x64": {}}

project.json

{"version": "1.0.0-*",
  "buildOptions": {"debugType": "portable",
    "emitEntryPoint": true},
  "dependencies": {"Newtonsoft.Json": "9.0.1"},
  "frameworks": {"netcoreapp1.0": {"dependencies": {"Microsoft.NETCore.App": {"version": "1.0.1"}},
      "imports": "dnxcore50"}},
  "runtimes": {"win10-x64": {},
    "osx.10.11-x64": {}}}

project.jsonを編集したら、dotnet restoredotnet buildとコマンドを実行します。

すると、こんな風にexeファイルとして出力されます。
f:id:minami_SC:20161023121253p:plain

別のプラットフォーム用にビルド

先ほどの手順では、「win10-x64」環境向けのファイルだけが作られていました。

runtimesの項目に書いた、別のプラットフォーム向けにビルドするには、 以下のように、-rオプションを付けてビルドします。

dotnet build -r osx.10.11-x64

dotnet publish

先ほどの手順で、dllではなくexeとして実行可能な形式でビルドすることができました。
しかし、この実行ファイルは.NET Coreの各種パッケージに依存した実行ファイルなので、.NET Coreのランタイムがない環境では実行できません。

.NET CoreのランタイムがないPCで実行できるような形式でビルドするには、以下のコマンドを実行します。

dotnet publish

すると、ビルド結果が保存されるディレクトリにpublishというフォルダができます。
この中には実行に必要なライブラリ類とexeファイルがすべて格納された状態となっています。
このフォルダ一式をまとめてコピーすれば、ビルドした環境以外でもexeファイルを実行できる状態となっています。

MacLinuxなどの環境での動作はまだ試せていませんが、 この方法でビルドしたexeファイルを、.NET CoreをインストールしてないWin環境に持っていっても実行することができました。

リリースビルド

リリースビルドする場合は、以下のように-cオプションを加えてビルドします。

dotnet build -c Release

今回はここまで。

VSCode向けのTypeScript 2.0 + Electronサンプル

$
0
0

以前作ってみたTypeScript + Electronのサンプルプロジェクトですが、TypeScript2.0を使うように色々更新しました。

  • TypeScript 2.0.3に更新
  • Electron 1.4.4に更新
  • 型定義ファイルの取得を、typing⇒@typesでの取得に変更
  • VSCode用の各種設定ファイル類を更新

使い方

  • GitHubからクローンしてきたら、npm installnpm startで実行できます。
  • VSCodeで開いてF5キーを押すと、TypeScriptのコンパイル⇒デバッガでの起動ができます。

TypeScript2.0からは、npm経由で型定義ファイルの取得ができるようになりました。
なので、tsdやtypingsなどの外部ツールを使う必要がなくなり、若干シンプルにプロジェクトを構成できるようになってます。

npmだけで型定義ファイルを取得できると、やっぱ楽でイイですね!!

.NET Coreことはじめ~その3・最小構成のWebサーバー~

$
0
0

今回は、.NET Coreで最小構成のWebサーバーを作ってみます。
ただし、まだASP.NET Coreのプロジェクトの雛形などは使わず、一から書き始めてみます。

参考サイト

今回は、公式ページのチュートリアルを参考に、最小構成のサーバーを実装するところまで。
https://docs.asp.net/en/latest/getting-started.html

次回以降は、↓のサンプルコードなどを参考にもう少し詳しく見ていこうと思います。
https://github.com/dodyg/practical-aspnetcore

プロジェクトの準備

プロジェクトの作成

まずは適当なフォルダを作り、そこをカレントディレクトリとして、コンソールから以下のコマンドを実行します。

dotnet new

依存パッケージの設定(project.jsonの編集)

以下のように、"Microsoft.AspNetCore.Server.Kestrel": "1.0.0"という項目を追加します。

project.json

{"version": "1.0.0-*",
  "buildOptions": {"debugType": "portable",
    "emitEntryPoint": true},
  "dependencies": {},
  "frameworks": {"netcoreapp1.0": {"dependencies": {"Microsoft.NETCore.App": {"type": "platform",
          "version": "1.0.1"},
        "Microsoft.AspNetCore.Server.Kestrel": "1.0.0"},
      "imports": "dnxcore50"}}}

ファイルを保存したら、dotnet restoreコマンドを実行します。

コードの編集

Startupクラスの作成

Startup.csというファイルを追加し、以下のようなコードを書きます。
f:id:minami_SC:20161027234020p:plain

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

namespace aspnetcoreapp
{
    publicclass Startup
    {
        publicvoid Configure(IApplicationBuilder app)
        {
            app.Run(context =>
            {
                return context.Response.WriteAsync("Hello World!!");
            });
        }
    }
}

エントリポイントの作成(Main関数)

Program.csを開き、Main関数に以下のようなコードを書きます。

Program.cs

using System;
using Microsoft.AspNetCore.Hosting;

namespace aspnetcoreapp
{
    publicclass Program
    {
        publicstaticvoid Main(string[] args)
        {
            var host = new WebHostBuilder().UseKestrel()
                                           .UseStartup<Startup>()
                                           .Build();

            host.Run();
        }
    }
}

WebHostBuilderのインスタンスを作り、各種設定を行うためのメソッドをメソッドチェーンで繋いで実行していきます。

UseStartupメソッドでは、先ほど作ったStartupクラスをジェネリック型引数に指定して実行します。
このStartupメソッド、特に何かのクラスを継承しているわけでも、インターフェースの実装をしているわけでもないですが、このStartupクラスのConfigureメソッドが呼び出されます。

内部でリフレクションでもやってるのかな、、とか思い調べてみると、この辺の動作を調べてまとめてくれている記事を見つけました。 http://ryuichi111std.hatenablog.com/entry/2016/05/29/151111

この記事を参考にして、ASP.NET CoreのHostingにかかわる部分のコードを追いかけてみました。

以下の箇所でConfigure**というような名前の関数をリフレクションで探しているようです。 https://github.com/aspnet/Hosting/blob/b6da89f54cff11474f17486cdc55c2f21f2bbd6b/src/Microsoft.AspNetCore.Hosting/Internal/StartupLoader.cs#L118-L135

これ、なんでこんな設計にしたんだろう・・・??? 普通にやるなら、何らかのインターフェースor基底クラスを定義して、その型を実装した派生型だけ受け付ける、、、とかにしそうなもんだけど・・・

実行

dotnet runコマンドを実行すると、Webサーバーが起動します。
f:id:minami_SC:20161027234118p:plain

5000番ポートでリッスンしているので、ブラウザで以下のアドレスにアクセスすると、Startupクラスで書いた内容(ここでは「Hello World!!」)が表示されます。
f:id:minami_SC:20161027234128p:plain

今日はここまで。

.NET Coreことはじめ~その4・サーバーの各種設定~

$
0
0

今回は、Startupクラスの各種関数内で、色々なサーバーの設定をしてみたいと思います。

Startupクラスの各種メソッド

Startupクラスでは、コンストラクタとConfigureServecies, Configureメソッドなどで、サーバーの各種設定を行うことができます。

それぞれどんなことができるかは、追々見ていくこととして、まずはこれらのメソッドだけ作ってConsole.WriteLineを書き、実行時に呼び出されていることの確認だけをしておきます。

Startup.cs

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace aspnetcoreapp
{
    publicclass Startup
    {
        public Startup (IHostingEnvironment env)
        {
            Console.WriteLine("--Startup Constructor--");
        }

        publicvoid ConfigureServices(IServiceCollection services)
        {
            Console.WriteLine("--ConfigureServices--");
        }
        
        publicvoid Configure(IApplicationBuilder app)
        {
            Console.WriteLine("--Configure--");

            app.Run(context =>
            {
                return context.Response.WriteAsync("Hello World!!");
            });
        }
    }

}

f:id:minami_SC:20161030135024p:plain

Startupクラスの各種メソッドの引数

コンストラクタ、ConfigureServecies、Configureの各メソッドには、それぞれ以下のような引数を設定できるようになっています。

https://docs.asp.net/en/latest/fundamentals/startup.html#services-available-in-startup

こんな風に、いろんな引数を持ったメソッドとして定義できます。
その時々に応じて、必要な引数を追加して使うイメージなのかもしれません。

publicvoid Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory logger)
        {
            app.Run(context =>
            {
                return context.Response.WriteAsync("Hello World!!");
            });
        }

Development/Productionモード

WebサーバーをDevelopmentモード、Productionモードのどちらで動いているかに応じて、処理を切り替えることができます。

以下のように、IsDevelopment、IsProduction関数の結果を見ることで、それぞれのモードに応じて処理を切り替えることができます。

publicclass Startup
    {
        publicvoid Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            var message = "Hello World!!\n";
            if (env.IsDevelopment()){
                message += "(Development)";
            }

            if (env.IsProduction()){
                message += "(Production)";
            }

            app.Run(context =>
            {
                return context.Response.WriteAsync(message);
            });
        }
    }

どんなモードで動作するかは、Main関数でWebHostBuilderでUseEnvironment関数を呼び出すことで変更できます。
下のように、UseEnvironment("Production")と呼び出すと、Productionモードでの実行となります。 Program.cs

publicstaticvoid Main(string[] args)
        {
            var host = new WebHostBuilder().UseKestrel()
                                           .UseStartup<Startup>()
                                           .UseEnvironment("Production")
                                           .Build();

            host.Run();
        }

f:id:minami_SC:20161030135917p:plain

ログ出力

依存パッケージの追加

これらのログ出力機能を使うためには、"Microsoft.Extensions.Logging": "1.0.0""Microsoft.Extensions.Logging.Console": "1.0.0"というパッケージを追加します。

project.jsonのdependenciesを以下のように修正し、dotnet restoreを実行します。

{"version": "1.0.0-*",
  "buildOptions": {"debugType": "portable",
    "emitEntryPoint": true},
  "dependencies": {"Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0"},

そして、ログを出力したい箇所で、以下のようにLogTraceなどのログ出力用メソッドを実行します。 Startup.cs

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace aspnetcoreapp
{
    publicclass Startup
    {
        publicvoid Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole((str, level) => {
                return level >= LogLevel.Trace;
            });

            var log = loggerFactory.CreateLogger("Startup");
            app.Run(context =>
            {
                log.("log trace test");
                log.LogWarning("log warning test");
                return context.Response.WriteAsync("Hello World!!");
            });
        }
    }
}

フレームワーク側のログも出力されますが、自分で追加したログも以下のようにコンソールに出力されるようになりました。
f:id:minami_SC:20161030135319p:plain

アプリのライフサイクルに伴うイベント

以下のようにConfigureメソッドに、IApplicationLifetime型の引数を追加すると、アプリ起動時や終了前などのタイミングで任意の処理を実行できるようになります。

↓のサンプルでは、アプリ起動時と終了前にログ出力をするようにしてみました。

publicvoid Configure(IApplicationBuilder app, IApplicationLifetime lifetime, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole((str, level) => {
                return level >= LogLevel.Trace;
            });
            var log = loggerFactory.CreateLogger("Startup");

            lifetime.ApplicationStarted.Register(() => log.LogTrace("--ApplicationStarted--"));
            lifetime.ApplicationStopping.Register(() => log.LogTrace("--ApplicationStopping--"));
            lifetime.ApplicationStopped.Register(() => log.LogTrace("--ApplicationStopped--"));

            app.Run(context =>
            {
                return context.Response.WriteAsync("Hello World!!");
            });
        }
    }

f:id:minami_SC:20161030135333p:plain

.NET Coreことはじめ~その5・ミドルウェア~

$
0
0

今回はミドルウェアASP.NET Coreのミドルウェアの概念について学んでいきます。

ミドルウェア

ASP.NET Coreでは、Webサーバーがクライアントからリクエストは、ミドルウェアというものを通して処理されます。

ミドルウェアは複数定義され、それらがパイプラインとなって順に実行されていきます。

↓のリンク先の図のようなイメージ。
https://docs.asp.net/en/latest/fundamentals/middleware.html#creating-a-middleware-pipeline-with-iapplicationbuilder

たぶんこの辺の概念は、Node.jsでExpressとかを使ってた人は直感的に理解できるのでは、、と思います。

これらのミドルウェアは、StartupクラスのConfigureメソッドの引数として渡される、IApplicationBuilder型の引数を通して設定をすることができます。
このIApplicationBuilderの引数に対して、Run/Use/Mapなどのメソッドを呼び出して設定を行います。

Runメソッド

今までのサンプルでも何度か使用してきましたが、このメソッドの引数に書いたデリゲートでリクエストに対するレスポンスを定義できます。

以下の例では、レスポンスに「Hello, World!」という文字列を書いて応答します。

publicvoid Configure(IApplicationBuilder app)
        {
            app.Run(async context =>
            {
                await context.Response.WriteAsync("Hello, World!");
            });
        }

dotnet runで実行し、ブラウザからlocalhostにアクセスすると、Runメソッドで定義したレスポンスが返ってきていることが確認できます。
f:id:minami_SC:20161102122553p:plain

ミドルウェアの終端

Runメソッドは、ミドルウェアのチェーンを終端させます。

そのため、以下のように複数Runメソッドを書いても、最初のRunメソッドで書いた部分しか実行されません。

publicvoid Configure(IApplicationBuilder app)
        {
            app.Run(async context =>
            {
                await context.Response.WriteAsync("Hello, World!");
            });

            app.Run(async context =>
            {
                // 以下の部分は実行されない。
                await context.Response.WriteAsync("Hello, Again");
            });
        }

Useメソッド

Useメソッドは、ミドルウェアのチェーンに処理を追加します。
Runメソッドとは異なり、処理を終端させないので、Useメソッド呼出し後に別のミドルウェアを繋げることができます。

以下のように、Use/Runメソッドを順に呼び出しておくと、メソッドの呼び出し順に実行されていることが確認できます。

            app.Use(async (context, next) =>
            {
                await context.Response.WriteAsync("Hello, World!");
                // 後続のミドルウェアを呼び出す
                await next.Invoke();
            });

            app.Run(async context =>
            {
                await context.Response.WriteAsync("Hello, Again");
            });

f:id:minami_SC:20161102122855p:plain

next.Invokeメソッド

この関数を呼び出すと、後続のミドルウェアが実行されます。
先ほどの例では、UseでWriteAsyncした後、Runで定義した内容が続けて実行されます。

後続のミドルウェアの処理が終わると、awaitの後の部分が実行されます。 以下のサンプルのように、コンソール出力をしてみると、どのような順で実行されているか確認できます。

publicvoid Configure(IApplicationBuilder app)
        {
            app.Use(async (context, next) =>
            {
                Console.WriteLine("--[1]--");
                await context.Response.WriteAsync("Hello, World!");
                // 後続のミドルウェアを呼び出す
                await next.Invoke();
                Console.WriteLine("--[3]--");
            });

            app.Run(async context =>
            {
                Console.WriteLine("--[2]--");
                await context.Response.WriteAsync("Hello, Again");
            });
        }

f:id:minami_SC:20161102122603p:plain

ちなみに、このnext.Invoke()というのを呼び出さないと、後続のミドルウェア実行されず、Runメソッドでミドルウェアのチェーンを終端させたような動作になるので注意が必要です。

Mapメソッド

Mapメソッドを使うと、リクエストで受けたURLのパスに応じて、ミドルウェアのチェーンを分岐できます。

以下の例では、/hello/worldでそれぞれ別の処理を実行します。

publicvoid Configure(IApplicationBuilder app)
        {
            app.Map("/hello", hello => {
                hello.Run(async context => {
                    await context.Response.WriteAsync("This is hello page.");
                });
            });

            app.Map("/world", world => {
                world.Run(async context => {
                    await context.Response.WriteAsync("This is world page.");
                });
            });
        }

それぞれのURLにアクセスすると、以下のように異なったレスポンスが返ります。
f:id:minami_SC:20161102122644p:plain
f:id:minami_SC:20161102122652p:plain

標準で用意されているミドルウェア

ASP.NET Coreでは以下のようなミドルウェアが標準で用意されています。

  • Authentication
  • CORS
  • Routing
  • Session
  • Static Files

https://docs.asp.net/en/latest/fundamentals/middleware.html#built-in-middleware

ここでは、簡単に使える例として、StaticFilesミドルウェアを使い、指定フォルダ内の内容を静的に配信するサーバーを作ってみます。

Static Filesミドルウェアの使用

まず、このミドルウェアを使用するには、project.jsonのdependenciesの項目に"Microsoft.AspNetCore.StaticFiles": "1.0.0"というのを追記し、dotnet restoreコマンドを実行します。

{"version": "1.0.0-*",
  "buildOptions": {"debugType": "portable",
    "emitEntryPoint": true},
  "dependencies": {"Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0"},

Startup.cs

publicclass Startup
    {
        publicvoid Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseStaticFiles();
        }
    }

Program.cs
Main関数では、UseContentRoot()という関数を呼び出しておきます。
このメソッドで、Webアプリのルートとなるディレクトリを設定します。

publicstaticvoid Main(string[] args)
        {
            var host = new WebHostBuilder().UseKestrel()
                                           .UseContentRoot(Directory.GetCurrentDirectory())
                                           .UseStartup<Startup>()
                                           .UseEnvironment("Production")
                                           .Build();

            host.Run();
        }

また、wwwrootフォルダを作り、その中にWebサーバーで配信したいファイル/フォルダなどを配置します。

ここでは以下のような二つのファイルを作成しました。
f:id:minami_SC:20161102122714p:plain

index.html

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><title>Document</title></head><body><h1>This is index.html page</h1></body></html>

hello/world.html

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><title>Document</title></head><body><h1>This is hello/world.html page</h1></body></html>

dotnet runコマンドで実行すると、それぞれwwwrootに配置した通りのファイルが静的に配信できていることが確認できます。

f:id:minami_SC:20161102122723p:plain
f:id:minami_SC:20161102122730p:plain

このような静的なファイルサーバーの実装方法については、以下のドキュメントに詳細情報がまとまっています。
https://docs.asp.net/en/latest/fundamentals/static-files.html

ミドルウェアの作成

ミドルウェアは、独立したクラスとして作成することもできます。
独立したクラスとして作成しておくと、複雑な処理にも対応しやすく、再利用しやすいコードになります。

公式ドキュメントのサンプルと同じように、ここでは受け付けたリクエストのログ出力を行うミドルウェアを作ってみます。

ミドルウェアの定義

まずは、ミドルウェア定義用のクラスを作ります。

publicclass RequestLoggerMiddleware
    {
        privatereadonly RequestDelegate _next;
        privatereadonly ILogger _logger;

        public RequestLoggerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
        {
            this._next = next;
            this._logger = loggerFactory.CreateLogger<RequestLoggerMiddleware>();
        }

        public async Task Invoke(HttpContext context)
        {
            this._logger.LogInformation($"Handling request: {context.Request.Path}");
            await this._next.Invoke(context);
            this._logger.LogInformation("Finished handling request.");
        }
    }

使い方

このミドルウェアを使うには、型引数として先ほど作ったクラスを指定してUseMiddlewareメソッドを呼び出します。

また、ログ出力を伴うミドルウェアなので、Configureメソッドの最初でログ出力を行うためloggerfactory.AddConsole();を実行しています。

publicvoid Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
        {
            loggerfactory.AddConsole();
            app.UseMiddleware<RequestLoggerMiddleware>();
動作確認

次のサンプルのように、Mapメソッドでのミドルウェア定義と合わせて書いておくと、それぞれのURLにアクセスした際に、コンソールにリクエストのパス情報が出力されるようになります。

publicvoid Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
        {
            loggerfactory.AddConsole();
            app.UseMiddleware<RequestLoggerMiddleware>();

            app.Map("/hello", hello => {
                hello.Run(async context => {
                    await context.Response.WriteAsync("This is hello page.");
                });
            });

            app.Map("/world", world => {
                world.Run(async context => {
                    await context.Response.WriteAsync("This is world page.");
                });
            });
        }

f:id:minami_SC:20161102122744p:plain

拡張メソッドを定義してから使う

以下のような拡張メソッドを定義しておくと、

publicstaticclass RequestLoggerExtensions
    {
        publicstatic IApplicationBuilder UseRequestLogger(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestLoggerMiddleware>();
        }
    }

このように、拡張メソッドの呼び出し一発で使えるようになります。
また、app.と打った時点でインテリセンスの候補にも出るの便利です。

publicvoid Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
        {
            loggerfactory.AddConsole();
            app.UseRequestLogger();
ミドルウェア作成のまとめ

ミドルウェアを作る場合は、以下のようなステップで作成します。

  • ミドルウェア定義用のクラスを作成
  • 上記クラスで、コンストラクタやInvokeメソッドなどを実装し、ミドルウェアの処理を実装
  • IApplicationBuilderに対する拡張メソッドを作って、利用しやすくする

自分でミドルウェアを作る機会がどの程度あるかは、まだよくわかりません。
ですが、ミドルウェアがどのように作られているかを知っておくと、フレームワーク標準のミドルウェアを使うときなどに、内部でどのような処理が行われているか、具体的なイメージを持って実装ができるのでは、と思います。

今回はここまで。

Visual Studio Code 1.7の新機能・変更点

$
0
0

VSCode 1.7.1がリリースされました。
http://code.visualstudio.com/updates/v1_7

1.7ではpackage.jsonを見て、依存するライブラリの型定義ファイルを、npmの@typesスコープから自動でダウンロードする機能を導入していたそうです。
しかし、この機能がnpmに大量のトラフィックを送って大きな負荷をかけてしまうという問題があったようで、この機能を無効にして速攻で1.7.1としてリリースし直したようです。
この辺の詳細は、以下のブログで事細かな経緯が書かれていました。
http://code.visualstudio.com/blogs/2016/11/3/rollback

エディタ

縦方向に並べた表示

今まで、エディタは垂直にしか分割できませんでした。
今回のバージョンからは、水平方向に分割し、縦にエディタを並べられるようになりました。
f:id:minami_SC:20161106000311p:plain

Shift+Alt+1というショートカットか、Toggle Editor Group Vertical/Horizontal Layoutというコマンド、またはGUI上の以下のボタンから、表示の切り替えができます。
f:id:minami_SC:20161106000330p:plain

settings.jsonへ移動した設定項目

以下のような項目が、setting.jsonで設定できるようになりました。

  • workbench.sideBar.location
    • サイドバーをウィンドウの左右どちらに配置するか。
  • workbench.statusBar.visible
    • ステータスバーを表示するか否か

Quick Openから、連続して複数ファイルを開く

Ctrl+Pのショートカットで開かれる、QuickOpenの以下のUIですが、
f:id:minami_SC:20161106000341p:plain
ここで、カーソルの「→」キーを押すと、QuickOpenのUIを閉じずに、複数のファイルを連続して開くことができるようになりました。

ドキュメントのフォーマットについて

選択範囲のテキストだけをフォーマットできるようになりました。
そういえば、今まではファイル全体のフォーマットしかできませんでしたね。

f:id:minami_SC:20161106000351p:plain

それぞれ以下のショートカットで実行できます。

  • Shift+Alt+F
    • ファイル全体のフォーマット
  • Ctrl+K Ctrl+F
    • 選択範囲のフォーマット

拡張機能

拡張機能の無効化

こんな風に、インストール済みの拡張機能を無効にできるようになりました。
f:id:minami_SC:20161106000402p:plain

ワークスペース単位、アプリ全体のどちらかのレベルで有効/無効の切り替えができます。

Extension Packs

複数の拡張機能がセットになったExtension Packsというものができました。

f:id:minami_SC:20161106000411p:plain

カテゴリの追加

拡張機能のカテゴリに、Keymasp、Formattersというカテゴリが追加されました。
Keymaps Extensions - Visual Studio Marketplace
Formatters Extensions - Visual Studio Marketplace

デバッガ

launch.jsonの簡略化

F5キーを押したときなどに、自動で生成されるlaunch.jsonの内容がシンプルになりました。

デフォルトでは、以下のような内容のファイルが作られます。

{// Use IntelliSense to learn about possible Node.js debug attributes.// Hover to view descriptions of existing attributes.// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387"version": "0.2.0",
    "configurations": [{"type": "node",
            "request": "launch",
            "name": "プログラムの起動",
            "program": "${workspaceRoot}\\index.js",
            "cwd": "${workspaceRoot}"},
        {"type": "node",
            "request": "attach",
            "name": "プロセスに添付",
            "port": 5858
        }]}

また、頻繁に書き換えたりしないような項目は、以下のようにグレー表示されるようになりました。
f:id:minami_SC:20161106000456p:plain
重要な箇所に目が行きやすくなってイイですね!!

ブレークポイントのヒットカウント対応

条件付きブレークポイントで、ヒットカウントの条件指定ができるようになったとのこと。

以下のような、ヒットカウント設定用のUIが追加されました。
f:id:minami_SC:20161106000504p:plain

しかし、この機能、自分の環境ではうまく動きませんでした。。。
ヒットカウントの条件で指定した回数だけ実行しても、ブレークしません。

コンボボックスをヒットカウントにしたときに、条件入力エリアの説明文などが切り替わっていないので、おそらく「式」と「ヒットカウント」の条件の切り替えが正しくできていないっぽい・・・

マルチターゲット デバッグ

これはメッチャ助かる!!

launch.jsonで定義しているデバッグ設定を、複数同時にデバッガで開始できるようになりました。

Node.jsで作ったサーバー側のコードと、ブラウザ側のスクリプトを同時にデバッグするなどの用途で活用できそうです。

こんな風に、"type": "composite"というタイプのデバッガの設定を追加し、configurationNamesに同時にデバッグしたい項目のnameの値を列挙します。

launch.json

{// Use IntelliSense to learn about possible Node.js debug attributes.// Hover to view descriptions of existing attributes.// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387"version": "0.2.0",
    "configurations": [{"type": "node",
            "request": "launch",
            "name": "プログラムの起動",
            "program": "${workspaceRoot}/bin/www",
            "cwd": "${workspaceRoot}",
            "sourceMaps": true},
        {"name": "Launch localhost with sourcemaps",
            "type": "chrome",
            "request": "launch",
            "url": "http://localhost:3000/",
            "webRoot": "${workspaceRoot}/public",
            "sourceMaps": true},
        {"type": "composite",
            "request": "launch",
            "name": "Launch Both",
            "cwd": "${workspaceRoot}",
            "configurationNames": ["プログラムの起動",
                "Launch localhost with sourcemaps"]}]}

この例では、Node.jsで作ったサーバー側と、Debugger for Chrome拡張機能を用いてChromeで開いたブラウザ側スクリプトを、VSCodeから同時にデバッグできるようにしています。

その他

XAMLアドベントカレンダー 作りました

$
0
0

この記事はXAMLアドベントカレンダーの1日目の記事です。

XAMLに関するアドベントカレンダーが無さそうなので作りました。

数日前にチェックしたときにはUWPカレンダーがあった気がするのですが、書こうと思いチェックしてみたら無くなってしまってました・・・orz

そこで改めてXAML系の話題を扱うためのカレンダー作りました。
WPFやUWP、はたまたWFなどなど、XAML系全般の話題ウェルカムなカレンダーです。

例年、WPFXAMLなどのカレンダーあったのに、今年はXAML系の話題がさみしい気がします。
C#や.NET Core、ASP.Netと言ったテーマはありますが、XAMLフレームワークに特化したものはありませんよね。
今年は、Xamarinとかの方面が人気なんですかね。

ということで、誰か参加してくれる人いないでしょうか?
興味がある方はお気軽にご参加ください!!

まずは明日は、あまり知られてなさそうなVSのXAMLエディタ便利機能について書きたいと思います。


Debug時とRelease時のXAML表示内容を切り替える方法

$
0
0

この記事はXAMLアドベントカレンダー 2016 2日目の記事です。

ちょっと予定を変更して、本日はXAMLの小ネタ。
XAML上で、以下のような表示切替をしてみます。

  • Debug/Releaseモードに応じて表示内容を切り替える
  • VSのデザイナ上での実行orアプリとしての実行に応じて、表示内容を切り替える

Debug時とRelease時のXAML表示内容を切り替える

やっていることは非常にシンプルです。
以下のようなstaticなプロパティを作り、Debug/Releaseビルドに応じて返す値を変えるだけです。

Misc.cs

namespace WpfApplication1
{
    publicclass Misc
    {
        /// <summary>/// DebugモードのみVisibleとなるプロパティ/// </summary>/// <remarks>/// コントロールのVisibilityプロパティに設定すると、/// Debugビルド時のみ表示、という表示切替ができます。/// </remarks>publicstatic Visibility IsDebugVisible
        {
#if DEBUGget { return Visibility.Visible; }
#elseget { return Visibility.Collapsed; }
#endif
        }
    }
}

そしてこのプロパティを、以下のようにVisibilityプロパティに設定します。

<Button Width="75"Margin="5"Content="Button2"Visibility="{x:Static local:Misc.IsDebugVisible}"/>

こうすると、簡単にDebugビルド時だけ表示するコントロールを作ることができます。

使用例

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"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="clr-namespace:WpfApplication1"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="MainWindow"Width="300"Height="200"mc:Ignorable="d"><Grid><StackPanel HorizontalAlignment="Left"><Button Width="75"Margin="5"Content="Button1" /><Button Width="75"Margin="5"Content="Button2"Visibility="{x:Static local:Misc.IsDebugVisible}"/><Button Width="75"Margin="5"Content="Button3" /></StackPanel></Grid></Window>
DebugビルドReleaseビルド
f:id:minami_SC:20161202193608p:plainf:id:minami_SC:20161202193707p:plain
注意点

この方法は、単純にVisbilityを切り替えてるだけです。
Snoopなどのツールを使うとコントロールの存在は見えますし、Visibilityも簡単に切り替えられます。

Releaseビルド時に本当に見えちゃまずいものは、ちゃんとXAML上から消しておいた方がよいでしょう。

デザインモード時だけの表示

応用で、こんな風にデザイン時だけ表示されるようにすることもできます。

こんなプロパティを作ります。

/// <summary>/// デザインモード時のみVisibleとなるプロパティ/// </summary>publicstatic Visibility IsDesignModeVisible
        {
            get
            {
                var isDesignMode = DesignerProperties.GetIsInDesignMode(new DependencyObject());
                return isDesignMode ? Visibility.Visible : Visibility.Collapsed;
            }
        }

こうすると、VisualStudioなどのXAMLデザイナ上でだけ表示され、アプリの実行時には表示されないようにすることができます。

MainWindow.xaml

<StackPanel HorizontalAlignment="Left"><Button Width="75"Margin="5"Content="Button1" /><Button Width="75"Margin="5"Content="Button2"Visibility="{x:Static local:Misc.IsDebugVisible}"/><Button Width="75"Margin="5"Content="Button3"Visibility="{x:Static local:Misc.IsDesignModeVisible}"/></StackPanel>

Misc.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApplication1
{
    publicclass Misc
    {
        /// <summary>/// DebugモードのみVisibleとなるプロパティ/// </summary>/// <remarks>/// コントロールのVisibilityプロパティに設定すると、/// Debugビルド時のみ表示、という表示切替ができます。/// </remarks>publicstatic Visibility IsDebugVisible
        {
#if DEBUGget { return Visibility.Visible; }
#elseget { return Visibility.Collapsed; }
#endif
        }

        /// <summary>/// デザインモード時のみVisibleとなるプロパティ/// </summary>publicstatic Visibility IsDesignModeVisible
        {
            get
            {
                var isDesignMode = DesignerProperties.GetIsInDesignMode(new DependencyObject());
                return isDesignMode ? Visibility.Visible : Visibility.Collapsed;
            }
        }
    }
}

以上、XAML表示切替のちょっとした小ネタでした。

Visual StudioのXAMLエディタの地味に便利な機能

$
0
0

この記事はXAMLアドベントカレンダー 2016 3日目の記事です。

この記事では、Visual StudioXAMLエディタの機能で、そこそこ便利なんだけど、あまり知られて無さそうなものをいくつか紹介したいと思います。

VS2015に搭載されている機能を紹介しているので、以前のバージョンのVSでは使用できないものもちらほらありますのでご注意を。

GUI部品のレイアウトなど

まずは、XAMLデザイナ上でのレイアウト機能について。

VSのXAMLデザイナでは、パワーポイントなどでの図形編集のように、要素を水平/垂直方向に整列させたり、レイアウトを調整するようなことができます。

整列/レイアウト

以下のように適当にコントロールを配置し、それらを複数選択した状態から、要素の中央ぞろえなどをすることができます。
f:id:minami_SC:20161203235708p:plain

ほかにも、右クリックメニューレイアウトの項目から、余白やサイズの調整などもできます。
f:id:minami_SC:20161203235841p:plain

パネルに含める

f:id:minami_SC:20161203235847p:plain
上記メニュー項目を実行すると、選択していた項目を指定したパネルに内包した構造になるように変更できます。

グリッド表示とスナップ

f:id:minami_SC:20161203235928p:plain
サンプルコード用などで、ちゃっちゃとコントロール類を配置したい場合には、
グリッドへのスナップをONにして、適当にコントロールを並べるのも便利かもしれません。

ドキュメントアウトライン

XAMLのツリー構造を、このようにアウトライン表示できます。
f:id:minami_SC:20161203235857p:plain

ドキュメントアウトラインで要素を選択すると、その要素をXAMLデザイナで選択した状態になります。

コントロールの非表示/ロック機能

f:id:minami_SC:20161203235946p:plain
このドキュメントアウトラインで、要素名右側のアイコンで以下のようなことができます。

  • デザイナ上での表示/非表示を切り替え
  • デザイナ上で編集できないようにロックする
    • ⇒ロックしておくと、誤ったマウス操作により、レイアウトを崩してしまうのを防げます。

非表示/ロックの設定をすると、↓のようにd:IsHiddenなどのプロパティが設定されます。

<Button Width="75"Margin="5"Content="Button1"d:IsHidden="True" /><Button Width="75"Margin="5"Content="Button2"d:IsLocked="True"/>

XAMLエディタの関係

この辺は、主にVS2015で追加された機能です。

XAMLコードの折り畳み機能

<!--#region 領域名--><!--#endregion -->というコメントで括った領域を、XAMLエディタ上で折りたたむことができます。

通常状態折り畳み状態
f:id:minami_SC:20161204000000p:plainf:id:minami_SC:20161204000006p:plain

詳細は以前書いた↓をご参照ください。
http://sourcechord.hatenablog.com/entry/2015/08/08/005048

Peek in XAML

XAMLでも、「定義をここに表示」できるようになりました。
f:id:minami_SC:20161204000029p:plain

StaticResouceなどを指定している部分で、右クリックメニューから「定義をここに表示」を選ぶと、以下のように定義内容をポップアップで表示してくれます。
f:id:minami_SC:20161204000046p:plain

また、この状態でコードを編集することもできます。

今日はこの辺まで。

.Net 4.6.2でのWPFのソフトキーボード対応の改善

$
0
0

この記事はXAMLアドベントカレンダー 2016 4日目の記事です。

今まで、WPFのTextBoxはタップしても、ソフトキーボードが表示されませんでした。

ですが、.Net4.6.2でソフトキーボードのサポートが強化され、テキストボックスタップ時に自動でソフトキーボードが出現したり、フォーカスが外れたらソフトキーボードが非表示になったりするようになりました。

あまり、詳細な説明は書かれてませんが、以下に少し説明が書かれています。 https://blogs.msdn.microsoft.com/visualstudio_jpn/2016/08/02/net-framework-4-6-2-%E3%82%92%E7%99%BA%E8%A1%A8/

しかし、どのように動作するのか詳細なことが書かれていないので、実際に試して確認してみました。

Win10 Aniverssary Update 環境で試してみたところ以下のように動作しました。 * 通常のデスクトップモードでは、ソフトキーボードは自動で出現しない * タブレットモード動作時にTextBoxをタップすると、自動でソフトキーボードが出る

タブレットモードで、TextBoxをタップした時
f:id:minami_SC:20161205000807p:plain:w400

タブレットモードOffの時はソフトキーボードが出ないのは若干気になります。
ですが、ソフトキーボードが使いたくなるシチュエーションはタブレットモードの時が多いと思うので、これでも一応大まかな役目は果たしてくれるのかな、と思います。

.Net 4.6.2以降でのWPFのPer-Monitor DPI対応

$
0
0

この記事はXAMLアドベントカレンダー 2016 14日目の記事です。

WPFでは、ボタンをはじめとする各種UI要素はベクターベースでの描画を行っています。
そのため、WinFormsやMFCなどのGDI系のUIフレームワークと違い、High-DPIな環境でもボケずにキレイな描画ができる、、、と言われていました。

Per-Monitor DPI

Win8.1からはPer-Monitor DPIという仕組みが導入され、ディスプレイごとに異なったDPI設定をすることができるようになっています。

Win8.1が出てきたころには、10inchほどのサイズでFullHD解像度のタブレットやノートPCなどが多数登場してきました。
それらの環境はピクセル密度が非常に高いので、標準で125%や150%のdpi設定になっているものが多数ありました。

このようなHigh-DPI環境のPCを外部ディスプレイに繋ぐ場合、外部ディスプレイ側はピクセル密度が高いものではないので普通の解像度にしたい、ということが多々あります。
そこで、ディスプレイごとに別々のdpiを設定できるようにPer-Monitor DPIという仕組みが導入されました。

Per-Monitor DPIの悪夢

Win8.1で導入されたPer-Monitor DPIは、正直なところ微妙な出来だったと思います。

WPFベクター形式でのUI描画なので、このようなディスプレイごとにDPIが異なる場合も、ボケずにちゃんと描画できると思っていました。。。。
しかし、現実はそうではありませんでした。

何も考えずに作ったWPFアプリは、Per-Monitor DPI環境ではボケボケ表示となってしまいます。
(厳密に言うと、Per-Monitor DPI有効にして、プライマリ以外のディスプレイで表示した場合)

また、WPFだけでなくエクスプローラなどのOS標準のUIなどもボケた表示となっていました。

WPFアプリでも、GetDpiForMonitor関数などを使いP/Invokeを多用するコードをかけば、このPer-Monitor DPIに対応することもできます。   しかし、OS標準のUIなどもPer-Monitor DPI対応は微妙だったので、個人的にはWin8.1のPer-Monitor DPIは使えない技術として目を背けていました。

.Net 4.6.2でのWPFのPer-Monitor DPI対応の改善

https://blogs.msdn.microsoft.com/dotnet/2016/08/02/announcing-net-framework-4-6-2/#wpf

こんな微妙な立ち位置だったWPFとPer-Monitor DPIですが、とうとう.Net4.6.2でフレームワーク側の対応が行われました。

Anniversary Update以降の環境向けの場合、WPFアプリは簡単にPer-Monitor DPI対応することができます。

ターゲットフレームワークを.Net 4.6.2としたWPFアプリ

.Net4.6.2をターゲットとしたWPFアプリは、マニフェストファイルに以下の記述を追加しておくと、Windows10 Anniversary Update以降の環境では、Per-Monitor DPIに対応した状態で動作します。

app.manifest
マニフェストファイルのapplication/windowsSetting以下に、dpiAwarenessという要素を追加し以下のように記述します。

<application xmlns="urn:schemas-microsoft-com:asm.v3"><windowsSettings><dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness></windowsSettings></application>
ターゲットフレームワークの設定

プロジェクトのプロパティで、ターゲットフレームワークを以下のようにビルドしておけばOK。
f:id:minami_SC:20161214080742p:plain

こうすると、ディスプレイをまたいでもボケたりせず、適切なDPI値でのUI表示が行われます。

.Net Framework 4.6.2 Developer Pack

VS2015の標準の状態では、ターゲットフレームワークには4.6.2の選択肢が出てきません。

そんな時は、↓の.Net Framework 4.6.2 Developer Packをインストールすると、ターゲットとして4.6.2を選べるようになります。 https://www.microsoft.com/ja-jp/download/details.aspx?id=53321

ターゲットフレームワークが、.Net 4.6.2より前のWPFアプリ

続いて、ターゲットフレームワークを4.6.2に上げたくない場合の対応方法です。 (アプリの実行環境に極力制限をかけたくない場合などでしょうか。。。)

ターゲットフレームワークを.Net4.6.2にしなくても、以下の設定をすることでPer-Monitor DPI対応ができます。 * 前述のapp.manifestファイルの設定 * App.configに↓のようにAppContextSwitchOverridesという項目を追加

App.config

<?xml version="1.0" encoding="utf-8"?><configuration><startup><supportedRuntime version="v4.0"sku=".NETFramework,Version=v4.5.2" /></startup><runtime><AppContextSwitchOverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false"/></runtime></configuration>

※補足
ただし、Per-Monitor DPI対応が有効になるのは、Win10 Anniversary Update以降の環境で実行した場合のみです。

Win10 Anniversary Updateより前のOSの場合

Anniversary Update以前のWin10(1507、1511など)や、Win8/8.1、Win7と言ったOSでは、.Net 4.6.2で追加されたdpiAwarenessの設定は機能しません。
これらのOSでPer-Monitor DPIに対応したい場合は、今までと同じようにP/Invokeを使った対処をする必要があります。

NCA(Non Client Area)のスケーリングが行われない問題

dpiAwarenessの設定でPer-Monitor DPI対応を行っても、ウィンドウのタイトルバーなどのNCA領域は正しくスケーリングされませんでした。

f:id:minami_SC:20161214080912p:plain
左側がWPFアプリ/右側がエクスプローラ表示です。
並べてみると、WPFアプリのタイトルバーはdpiに合わせてスケーリングされず、元のdpiでのピクセル数のままとなっていることがわかります。

この辺は、また将来のアップデートで改善されるといいのですが・・・

まとめ

.Net4.6.2で追加されたこの方法は、コードを書かずにマニフェストなどの設定ファイルをいじるだけで対応できるお手軽なものです。

Win10だったら基本的に最新のバージョンに更新されますし、今となってはWin8/8.1はかなりの少数派でしょう。
また、Win7世代のタブレット/ノートPCはHigh-DPI設定にしたくなるような、高解像度な端末は少なかったと思います。

いっそのことWin10 Anniversarry Update以降のみ、Per-Monitor DPIまでの対応をする、 という割り切った判断もありなのかな、と感じました。

WPF/UWP向けに、グリッドレイアウト補助ライブラリを作ってみました~GridExtra~

$
0
0

この記事はXAMLアドベントカレンダー 2016 18日目の記事です。

WPF/UWP向けに、グリッドレイアウトに役立つクラス類を提供するライブラリを作りました。

今のところ、この二つのクラスだけですが、今後少しずつ色々なパネルを追加していきたいと思っています。

  • ResponsiveGrid
  • GridEx
    • Gridの行/列定義を簡単にするための各種添付プロパティ類

目次

準備

インストール方法

Nugetから以下のパッケージをインストールすればOK!!
https://www.nuget.org/packages/GridExtra/

Nugetパッケージ管理のGUIで、GridExtraで検索するか、
f:id:minami_SC:20161218155955p:plain

Nugetのパッケージマネージャー コンソールから、↓のコマンドでインストールできます。

Install-Package GridExtra
名前空間の定義

XAMLに以下のような名前空間の定義を追加します。
WPFとUWPで若干指定方法が違うので注意。

WPFの場合

xmlns:ge="clr-namespace:SourceChord.GridExtra;assembly=GridExtra.Wpf"

UWPの場合

xmlns:ge="using:SourceChord.GridExtra"

使い方

ResponsiveGrid

f:id:minami_SC:20160702010414g:plain

こんな風にResponsiveGrid.XS, ResponsiveGrid.MDなどのプロパティを指定すると、ウィンドウ幅の変更に応じて自動でレイアウトが切り替わります。
CSSの定番フレームワークbootstrapのレイアウトシステムを模倣したものです。

<Grid><Grid.Resources><Style TargetType="{x:Type Border}"><Setter Property="BorderBrush"Value="Black" /><Setter Property="BorderThickness"Value="1" /><Setter Property="Background"Value="LightGray" /><Setter Property="Height"Value="60" /><Setter Property="TextBlock.FontSize"Value="10" /></Style></Grid.Resources><ge:ResponsiveGrid Margin="10"BreakPoints="345, 567, 789"><Border ge:ResponsiveGrid.XS="12"><TextBlock Text="[Header]&#xa;XS=12"/></Border><Border ge:ResponsiveGrid.XS="6"ge:ResponsiveGrid.SM="3"ge:ResponsiveGrid.MD="2"><TextBlock Text="[A]&#xa;XS=6&#xa;SM=3&#xa;MD=2"/></Border><Border ge:ResponsiveGrid.XS="6"ge:ResponsiveGrid.SM="3"ge:ResponsiveGrid.MD="2"><TextBlock Text="[B]&#xa;XS=6&#xa;SM=3&#xa;MD=2"/></Border><Border ge:ResponsiveGrid.XS="6"ge:ResponsiveGrid.SM="3"ge:ResponsiveGrid.MD="2"ge:ResponsiveGrid.SM_Push="3"ge:ResponsiveGrid.MD_Push="2"><TextBlock Text="[C]&#xa;XS=6&#xa;SM=3&#xa;MD=2"/></Border><Border ge:ResponsiveGrid.XS="6"ge:ResponsiveGrid.SM="3"ge:ResponsiveGrid.MD="2"ge:ResponsiveGrid.SM_Pull="3"ge:ResponsiveGrid.MD_Pull="2"><TextBlock Text="[D]&#xa;XS=6&#xa;SM=3&#xa;MD=2"/></Border><Border ge:ResponsiveGrid.XS="12"ge:ResponsiveGrid.SM="6"ge:ResponsiveGrid.MD="2"><TextBlock Text="[E]&#xa;XS=12&#xa;SM=6&#xa;MD=2"/></Border><Border ge:ResponsiveGrid.XS="12"ge:ResponsiveGrid.SM="6"ge:ResponsiveGrid.MD="2"><TextBlock Text="[F]&#xa;XS=12&#xa;SM=6&#xa;MD=2"/></Border><Border ge:ResponsiveGrid.XS="12"><TextBlock Text="[Footer]&#xa;XS=12"/></Border></ge:ResponsiveGrid></Grid>

以前、ResponsiveGridというライブラリを作って個別にGitHubに上げてたものですが、今回作ったGridExtraの中に移動しました。
ResponsiveGridの詳細な使い方は、↓の記事を参照してください。

GridEx

Gridパネルでのグリッド定義を便利にする、各種添付プロパティを定義したクラスです。

行×列の定義

普通にGridの行×列を定義をする場合には、以下のようなXAMLを書きます。

<Grid ShowGridLines="True"><Grid.RowDefinitions><RowDefinition Height="50" /><RowDefinition Height="*" /><RowDefinition Height="50" /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="2*" /><ColumnDefinition Width="100" /></Grid.ColumnDefinitions><Button Grid.Row="0"Grid.Column="1"Margin="5"Content="Button" /></Grid>

f:id:minami_SC:20161218155608p:plain

これって、結構冗長ですよね。
インテリセンスでコード補間されるとはいえ、何回</>を書けばいいんだ・・・と。

GridExの添付プロパティを使うと、以下のように単純に書くことができます。

<Grid ge:GridEx.ColumnDefinition="*, 2*, 100"ge:GridEx.RowDefinition="50, *, 50"ShowGridLines="True"><Button Grid.Row="0"Grid.Column="1"Margin="5"Content="Button" /></Grid>

MaxWidth, MinWidthなどの設定には対応してませんが、多くのケースはこれだけでも便利に利用できるのでは、と思います。

MaxWidthなどのプロパティは、今後のアップデートで対応しようと思います。

グリッドの子要素の位置定義

こちらも、先ほどと同じように、まずは普通にGridクラスのプロパティを使って定義してみます。

<Grid ge:GridEx.ColumnDefinition="*, *, *, *"ge:GridEx.RowDefinition="*, *, *, *"ShowGridLines="True"><Button Grid.Row="1"Grid.Column="2"Grid.RowSpan="3"Grid.ColumnSpan="2"Margin="5"Content="Button" /></Grid>

f:id:minami_SC:20161218155617p:plain

子要素の位置定義では、Gridクラスの添付プロパティを複数書かなければならず、少々面倒です。

そこで、Row/Column/RowSpan/ColumnSpanを一度に設定する添付プロパティを作成しました。
GridExクラスのArea添付プロパティを使うと、以下のようにこれらを一度に定義できます。

<Grid ge:GridEx.ColumnDefinition="*, *, *, *"ge:GridEx.RowDefinition="*, *, *, *"ShowGridLines="True"><Button ge:GridEx.Area="1, 2, 3, 2"Margin="5"Content="Button" /></Grid>

GridEx.Areaプロパティには、「Row, Column, RowSpan, ColumnSpan」の順番で、カンマ区切りで値を指定します。

ASCIIアート風のグリッド定義

HTML&CSSでのグリッドレイアウト用の仕様として、CSS Grid Layout Module Level1という仕様の策定が進んでいます。

この中で、グリッドをアスキーアートのように文字列で定義して各領域に名前を付け、グリッド内の子要素はこの領域名を用いて配置できる機能があります。

参考リンク
CSS Grid Layout Module Level 1
ElectronでCSS Grid Layout Moduleを使ってちょっと未来のレイアウト方法を先取りしてみる - SourceChord

TemplateAreaは、グリッドの各領域に対し名前を付けながら、行×列の定義を行います。
各領域はスペース区切りの文字列で領域名を定義していきます。
また、改行コードで行の終わりを定義します。

隣接している箇所に同じ名前を付けておくと、複数の行×列にまたがる領域を定義できます。

<Grid ge:GridEx.TemplateArea="            Header Header Header &#10;            Menu Content SubMenu &#10;            Footer Footer Footer &#10;"ShowGridLines="True"><Button Margin="5"ge:GridEx.AreaName="Header"Content="Header" /><Button Margin="5"ge:GridEx.AreaName="Menu"Content="Menu" /><Button Margin="5"ge:GridEx.AreaName="Content"Content="Content" /><Button Margin="5"ge:GridEx.AreaName="SubMenu"Content="SubMenu" /><Button Margin="5"ge:GridEx.AreaName="Footer"Content="Footer" /></Grid>

f:id:minami_SC:20161218155632p:plain

行の終わりの定義
xmlでは複数行にまたがる文字列を定義しても、行末の改行は無視されてしまいます。
そこで、&#10というエスケープ文字列を使って改行コード書くと、xmlでも改行を扱うことができます。

また、毎回このエスケープ文字列を書くのは面倒なので、/という文字列でも行の終わりを定義できるようにしています。

<Grid ge:GridEx.TemplateArea="            Header Header Header/            Menu Content SubMenu/            Footer Footer Footer/">

RowDefinition/ColumnDefinitionとの併用
TemplateAreaは、GridEx.RowDefinitionなどの定義と併用できます。
GridEx.RowDefinition, GridEx.ColumnDefinitionと組み合わせることで、柔軟なグリッド定義ができます。

<Grid ge:GridEx.RowDefinition="50, *, 30"ge:GridEx.ColumnDefinition="*, 2*, 100"ge:GridEx.TemplateArea="            Header Header Header/            Menu Content SubMenu/            Footer Footer Footer/"ShowGridLines="True"><Button Margin="5"ge:GridEx.AreaName="Header"Content="Header" /><Button Margin="5"ge:GridEx.AreaName="Menu"Content="Menu" /><Button Margin="5"ge:GridEx.AreaName="Content"Content="Content" /><Button Margin="5"ge:GridEx.AreaName="SubMenu"Content="SubMenu" /><Button Margin="5"ge:GridEx.AreaName="Footer"Content="Footer" /></Grid>

f:id:minami_SC:20161218155641p:plain

このようにTemplateAreaの編集にXAMLデザイナも追従するので、直感的にグリッドを定義できるのでは、と思います。
f:id:minami_SC:20161218155440g:plain

Viewing all 153 articles
Browse latest View live