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

MVVMな設計のTips~サービスを作ってVMの依存性を排除~

$
0
0

最近、色々とMVVMな設計のサンプル類を見てると、○○Serviceみたいなクラスを作って、VMが他のモジュールへの依存を極力持たないように設計している例をよく目にするようになった気がします。

ここでは、MVVMでの定番の躓きポイント「VMからのダイアログ表示どうするの?」というネタで、実際にサービスを用いたパターンでコード書いて試してみたいと思います。

また、この手のサービスを使ったパターンでは、DIコンテナ、サービスロケータなどを提供する各種ライブラリを使ったサンプルが多いです。
ですが、DependencyInjectionなどは、MVVMな設計の本質ではないと思うので、ここではそういったライブラリ類は使わず、素のWPFでの最低限なコードでサンプルを書いてみます。

色々参考にしたリンク

http://wp.qmatteoq.com/the-mvvm-pattern-dependency-injection/
https://blog.rsuter.com/recommendations-best-practices-implementing-mvvm-xaml-net-applications/
http://stackoverflow.com/questions/25366291/how-to-handle-dependency-injection-in-a-wpf-mvvm-application

あと、UWPのテンプレートを作っているTemplate10というプロジェクトでも、似たような感じでパターンを用いた設計になってます。 (Template10のサービスでは、IoCコンテナのような仕組みは使わず、シングルトンでサービスを取得するようにしているようです。) https://github.com/Windows-XAML/Template10/tree/master/Templates%20%28Project%29/Minimal/Services/SettingsServices

作ってみる内容

とりあえず、サンプルとして以下のようなものを作ってみます。

  • ボタンを押すと、テキストボックスに入力した文字列を使ってメッセージボックスを表示 f:id:minami_SC:20160123164108p:plain

データバインディングを使わず、コードビハインドだけで書くのであれば、なんてことは無い内容です。
しかし、MVVMを意識した設計をしてると、このVMからダイアログボックスの表示だとかで躓くと思います。

  • VMから、MessageBox.Showとかやるの気持ち悪い
    • VMに思いっきりView依存なコードが出現
  • こういう処理がVMに入ると、VM単体テストなども実質不可能に
    • ⇒MSTestやらNUnitやらでテストコード走らせてると、途中でメッセージボックスが出現して、テスト中断・・・・orzとか。

最初の一歩

まずはふつうにVMにそのままメッセージボックス表示処理まで含めて書いてみます。
サンプルの中では、BindableBase/RelayCommandというクラスが出てきますが、これはそれぞれINotifyPropertyChanged/ICommandを実装した汎用的なベースクラスです。

最初のサンプルでは、MainWindowViewModel中にそのままMessageBox.Showと書いてメッセージボックスの表示を行っています。
問題なく動作はしますが、前述のとおり色々とイケてません。

ここから順を追って、VM中のViewへの依存を取り除いてきます。
MainWindow.xaml

<Window x:Class="MVVMServiceSample.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:MVVMServiceSample"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="MainWindow"Width="400"Height="300"mc:Ignorable="d"><Window.DataContext><local:MainWindowViewModel /></Window.DataContext><StackPanel><TextBox x:Name="textBox"Width="120"Height="23"Margin="10"HorizontalAlignment="Left"Text="{Binding Name}"TextWrapping="Wrap" /><Button x:Name="button"Width="75"Margin="10,5"HorizontalAlignment="Left"Content="Show"Command="{Binding ShowNameCommand}"/></StackPanel></Window>

MainWindowViewModel.cs

publicclass MainWindowViewModel : BindableBase
    {
        privatestring name;
        publicstring Name
        {
            get { return name; }
            set { this.SetProperty(refthis.name, value); }
        }

        private RelayCommand showNameCommand;
        public RelayCommand ShowNameCommand
        {
            get { return showNameCommand = showNameCommand ?? new RelayCommand(ShowName); }
        }

        privatevoid ShowName()
        {
            System.Windows.MessageBox.Show($"こんにちは、{this.Name}さん");
        }
    }

サービス作成

メッセージボックス表示用のサービスを作り、View依存なコードをサービス側に移動します。

DialogServiceというクラスを作り、メッセージボックスの表示に関する処理をサービス側に移動します。 DialogService.cs

publicclass DialogService
    {
        publicvoid ShowMessage(string message)
        {
            MessageBox.Show(message);
        }
    }

MainWindowViewModel.cs

    public class MainWindowViewModel : BindableBase
    {
        private DialogService _dialogService;

        public MainWindowViewModel()
        {
            this._dialogService = new DialogService();
        }
        // 省略
        private void ShowName()
        {
            // サービス経由で、ダイアログの表示を行う。
            this._dialogService.ShowMessage($"こんにちは、{this.Name}さん");
        }
    }

サービスからIFを切り出す

DialogServiceというクラスにMessageBox表示のためのコードを分離しました。

ですが、以下のような目的で、DialogServiceの実装を別のものに差し替えたくなることもよくあるのではないか、と思います。

  • サービス側で実装する処理を、とりあえずモックで作って動かしたい
    • 例えば、インターネット経由でWebAPIから何らかのJSONデータを取得し、それをプログラム中で利用する形式のクラスに変換して返す、みたいなサービスを作る場合には、 いきなりWebAPIを叩いて、、、ってのは手間がかかるので、まずは毎回同じダミーデータを返す、単純なダミーサービスを作っておく、などというケースが考えられます。
  • VM単体テストしやすくする
    • VMでダイアログ表示などユーザー入力を必要とする処理を行っていると、そこで処理が止まるので単体テストを一気に走らせられない
    • ファイル入出力/DBアクセスなどを、ダミーの入出力に差し替えておき、テストの度にあちこちアクセスせずに実行できるようにする

などなど。。。
こういう場面は多々あるかと思います。

そこで、IDialogServiceというIFをつくり、VMはこのIFのメンバを持つように修正してみます。
また、単体テストなどで扱いやすくするために、DummyDialogServiceというクラスも作ります。
このクラスのShowMessageメソッドでは、ダイアログ表示ではなく、コンソールに文字を出力するだけの実装としておきます。

↓こんな感じ。

// IDialogService.cspublicinterface IDialogService
    {
        void ShowMessage(string message);
    }
    
// DialogService.csclass DialogService : IDialogService
    {
        publicvoid ShowMessage(string message)
        {
            MessageBox.Show(message);
        }
    }
    
// DummyDialogService.cspublicclass DummyDialogService : IDialogService
    {
        publicvoid ShowMessage(string message)
        {
            Console.WriteLine(message);
        }
    }

しかし、MainWindowViewModelのコンストラクタ内で、DialogServiceのインスタンスを生成してるので、これではVM内に直接MessageBox表示処理を書いているのと何も変わりませんよね。

public MainWindowViewModel()
        {
            this._dialogService = new DialogService();
        }

IFを実装したクラスを、VMの外部から注入する

ということで、MainWindowViewModelは、インターフェースを通してサービスにアクセスをするだけ。サービス実体の生成は外部で行い、生成したインスタンスVMに渡してメンバに設定します。
コンストラクタに引数を追加したので、XAMLファイルのDataContextは削除します。

DependencyInjection(DI)、依存性の注入、なんて言われるパターンの、シンプルな実例ですね。 ここでは、コンストラクタ注入の方法を使ってます。

// MainWindowViewModel.cspublicclass MainWindowViewModel : BindableBase
    {
        private IDialogService _dialogService;

        public MainWindowViewModel(IDialogService dialogService)
        {
            this._dialogService = dialogService;
        }
        // 以下略// MainWindow.xaml.cspublicpartialclass MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            // MainWindowViewModelに、コンストラクタ経由でDialogServiceへの依存性を注入する。// ※コンストラクタに引数が必要なので、XAML上ではなくコードビハインドからVMを生成する。this.DataContext = new MainWindowViewModel(new DialogService());
        }
    }

こうすることで、VMの依存性をサービスを通して外部に取り除くことができました。

単体テストからの利用

外部からIDialogServiceの実態を注入できるようになったので、単体テストを行う際は、DummyDialogServiceに差し替えてテストを行うことができるようになります。

ソリューションに単体テストプロジェクトを追加し、こんなテストクラスを書いてみます。
※とりあえずサンプルなので、Assertは書いてません。

    [TestClass]
    publicclass UnitTest1
    {
        [TestMethod]
        publicvoid TestMethod1()
        {
            var vm = new MainWindowViewModel(new DummyDialogService());
            vm.Name = "hoge";
            vm.ShowNameCommand.Execute(null);
        }
    }

こんな風に、ShowNameCommandを実行しても、アプリ実行時とは異なりメッセージボックスなどは表示せずに単体テストを続けることができます。
※補足
単体テスト中にコンソールに出力した内容は、テストエクスプローラの「出力」というリンクを押すと見ることができます。 f:id:minami_SC:20160123164124p:plain

全体のクラス構成

今回使ったクラスはこんな関係です。
f:id:minami_SC:20160123164130p:plain:w350

ウィンドウを持たせる

MessageBox.Showのメソッドもそうですが、たまにWindowのインスタンスが必要になる場面などもあるかと思います。
そんな時は、これらのサービスをView側で作成するときに、サービスに対してWindowのインスタンスを設定しておけばよいかと。

例えば、新規にサブウィンドウを開く際に、ウィンドウのオーナー設定のために親ウィンドウのインスタンスを指定したい、っってときとかですかね。

↓では、owner指定でメッセージボックスの表示をできるようにしています。

// DialogService.csclass DialogService : IDialogService
    {
        private Window _owner;

        public DialogService(Window owner = null)
        {
            this._owner = owner;
        }

        publicvoid ShowMessage(string message)
        {
            if (this._owner != null) { MessageBox.Show(this._owner, message); }
            else { MessageBox.Show(message); }
        }
    }

// MainWindow.xaml.cspublic MainWindow()
    {
        InitializeComponent();
        // MainWindowViewModelに、コンストラクタ経由でDialogServiceへの依存性を注入する。this.DataContext = new MainWindowViewModel(new DialogService(this));
    }

サンプルコード

今回のサンプル一式は以下の場所に置いておきました。

まとめ

とりあえず、VMがいろんなクラスに依存し始めてヤバイ!!となったら、こんな風に依存をサービスとして外部に切り分けてみるのもよいかと思います。

また、こんな風にWindowのインスタンスを持たせたサービスを作り、それをVMに渡すことで、VMからViewへの通知など、いろんな用途に応用が利くかと思います。

まずは、こうやってVMから依存性を取り除いていって、こういったパターンに慣れたらDIコンテナやらサービスロケータを使ったパターンに移っていけばいいのかな、と思います。


WPFでシンプルな独自ナビゲーション処理のサンプルを書いてみた

$
0
0

WPFに標準で用意されてるナビゲーション系のクラス類が何かと扱いにくいので、もうちょいシンプルな独自のページ遷移を行うサンプルを書いてみました。

なぜ作った?

WPFには標準で、ナビゲーションを行うための仕組みとして、NavigationWindow、Frame、NavigationServiceなどといったコントロールやクラスが用意されてます。
しかし、まぁこの辺のコントロール類は使いにくい。。

特にイヤなのが以下のような点。

  • ナビゲーションの履歴管理が邪魔
    • Frame/NavigationWindowを使ったページ遷移では、特に何もしなくてもページ遷移の履歴などが記録されます。
    • この履歴がviewクラスの参照を掴んでいて、なかなかGCされなくなる
    • ナビゲーション関係のコマンドを受け付けると、勝手にナビゲーション動作をする。。
      • F5キー押したらページリロードする・・・
      • 一方通行なページ遷移しかさせたくないのに、マウスの戻るボタン押したら前のページに戻ってしまう・・・
      • などなど。
    • ナビゲーション時に音が出るケースがある
      • OSの設定によっては、ページ遷移するときに、エクスプローラで移動したときとかに鳴るような「カチッ」という音がします。
      • Win8.x系/Win10では標準ではオフになってるようですが、Win7では・・・
  • ナビゲーション先の指定方法が微妙
    • XAML上でページ遷移先の定義をするときは、遷移先ページのURI指定。
    • ⇒もっと厳密に型で指定したい
  • NavigationSeriviceの管理する範囲が微妙に扱いにくい

※補足
このページ遷移で音が鳴るっていう動作は、OSの以下の設定に依存します。(「ナビゲーションの開始」の項目など)
f:id:minami_SC:20160201002804p:plain:w250

で、このナビゲーション時の音は、基本的にはアプリ側から消すことができません。
細かいことですが、気にし始めると地味にイライラします・・・w
(ちょっとこだわったデザインのUI作った時などは特に・・・)

ということで、以下のような方針で必要最低限なナビゲーションを独自に行うサンプルを書いてみました。

方針
  • Frameは使わない
    • 前述の通り、なにかと問題があるので、、
    • ナビゲーション対象のページはContentControlで表示
  • ナビゲーションの履歴とかはいらない
    • 履歴も何かと問題になるので、あえて排除
    • 履歴が必要、と思ったタイミングでコード足せばいいかな。
  • MVVM関係
    • ナビゲーションはあくまでもView側のレイヤーとして作る
    • VMから直接ページ遷移の指示などは行わない。
      • VMからページ遷移を指示したければ、VMからViewになんらかのメッセージを送ってViewがナビゲーションを行えばいいかな。

使い方

概要

独自のナビゲーション処理の起点となる、NavigationServiceExというクラスを添付プロパティとして、任意のコントロールに付けられるようにしています。
使い方の概要は以下の通り。

  • NavigationServiceExの設定
    • Targetプロパティで、ページ遷移を行う領域を指定
    • Startupプロパティで、初期表示に利用するページを指定
  • ページ遷移動作の定義
    • XAML上でページ遷移の定義
      • NavigationCommands.GoToPageコマンドを送るとページ遷移する
      • 遷移先ページは、{x:Type ・・・という形で型情報で指定する
    • コードビハインドからのページ遷移

こんなイメージです。
f:id:minami_SC:20160201002951p:plain

ナビゲーション領域の定義

こんな風に、ナビゲーションを管理したいレイヤーに、NavigationServiceExクラスのTarget/Startup添付プロパティを設定します。
ここではWindowクラスにナビゲーション機能の設定を行い、ナビゲーションを行う領域として「content」という名前のContentControlを指定してます。

<Window x:Class="CustomNavigationSample.Shell"省略nav:NavigationServiceEx.Target="{Binding ElementName=content}"nav:NavigationServiceEx.Startup="{x:Type view:MainView}"><DockPanel><Grid Background="LightGray"DockPanel.Dock="Top">:
           省略
           :
        <ContentControl x:Name="content" /></DockPanel></Window>

ページ遷移させる

ナビゲーション領域の外部からでも内部からでも、同じような書き方でページ遷移できます。

XAML上でのページ遷移定義
<Hyperlink Command="NavigationCommands.GoToPage"CommandParameter="{x:Type view:MainView}">Main Page</Hyperlink>
コードビハインドからのページ遷移
privatevoid button_Click(object sender, RoutedEventArgs e)
        {
            // 遷移先ページとなるインスタンスを渡してナビゲーションthis.Navigate(new SubView());
            // ↓こんな風に、型情報を指定して遷移も可能//this.Navigate(typeof(SubView));
        }

コード

今回追加した独自クラスのコードはこれだけ。
サンプル一式は↓に置いときました。

https://github.com/sourcechord/WPFSamples/tree/master/NavigationSamples

class NavigationServiceEx : DependencyObject
    {
        /// <summary>/// ページナビゲーションを行う領域となるContentControlを保持するプロパティ/// </summary>public ContentControl Content
        {
            get { return (ContentControl)GetValue(ContentProperty); }
            set { SetValue(ContentProperty, value); }
        }
        // Using a DependencyProperty as the backing store for Content.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty ContentProperty =
            DependencyProperty.Register("Content", typeof(ContentControl), typeof(NavigationServiceEx), new PropertyMetadata(null));


        #region ナビゲーションで利用する各種メソッド/// <summary>/// view引数で指定されたインスタンスのページへとナビゲーションを行います。/// </summary>/// <paramname="view"></param>/// <returns></returns>publicbool Navigate(FrameworkElement view)
        {
            this.Content.Content = view;
            returntrue;
        }

        /// <summary>/// viewType引数で指定された型のインスタンスを生成し、そのインスタンスのページへとナビゲーションを行います。/// </summary>/// <paramname="viewType"></param>/// <returns></returns>publicbool Navigate(Type viewType)
        {
            if (viewType == null) { returnfalse; }

            var view = Activator.CreateInstance(viewType) as FrameworkElement;
            returnthis.Navigate(view);
        }
        #endregion/// <summary>/// NavigationCommands.GoToPageコマンドに対する応答処理/// </summary>/// <paramname="sender"></param>/// <paramname="e"></param>privatevoid OnGoToPage(object sender, ExecutedRoutedEventArgs e)
        {
            var nextPage = e.Parameter as Type;
            this.Navigate(nextPage);
        }


        // 以下、添付プロパティなどの定義        #region ページナビゲーションを行う領域となるContentControlを指定するための添付プロパティ// この添付プロパティで指定した値は、NavigationServiceEx.Contentプロパティとバインドして同期するようにして扱う。publicstatic ContentControl GetTarget(DependencyObject obj)
        {
            return (ContentControl)obj.GetValue(TargetProperty);
        }
        publicstaticvoid SetTarget(DependencyObject obj, ContentControl value)
        {
            obj.SetValue(TargetProperty, value);
        }
        // Using a DependencyProperty as the backing store for Target.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty TargetProperty =
            DependencyProperty.RegisterAttached("Target", typeof(ContentControl), typeof(NavigationServiceEx), new PropertyMetadata(null, OnTargetChanged));

        privatestaticvoid OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as FrameworkElement;
            var target = e.NewValue as ContentControl;

            if (element != null&& target != null)
            {
                // NavigationServiceExのインスタンスを、添付対象のコントロールに付加する。
                var nav = new NavigationServiceEx();
                NavigationServiceEx.SetNavigator(element, nav);

                // ContentプロパティとTargetをバインドしておく。
                BindingOperations.SetBinding(nav, NavigationServiceEx.ContentProperty, new Binding() { Source = target });

                // ナビゲーション用のコマンドバインディング
                element.CommandBindings.Add(new CommandBinding(NavigationCommands.GoToPage, nav.OnGoToPage));

                var startup = NavigationServiceEx.GetStartup(element);
                if (startup != null)
                {
                    nav.Navigate(startup);
                }
            }
        }
        #endregion        #region スタートアップ時に表示するページを指定するための添付プロパティpublicstatic Type GetStartup(DependencyObject obj)
        {
            return (Type)obj.GetValue(StartupProperty);
        }
        publicstaticvoid SetStartup(DependencyObject obj, Type value)
        {
            obj.SetValue(StartupProperty, value);
        }
        // Using a DependencyProperty as the backing store for Startup.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty StartupProperty =
            DependencyProperty.RegisterAttached("Startup", typeof(Type), typeof(NavigationServiceEx), new PropertyMetadata(null, OnStartupChanged));

        privatestaticvoid OnStartupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as FrameworkElement;
            var startupType = e.NewValue as Type;

            if (element != null&& startupType != null)
            {
                var nav = NavigationServiceEx.GetNavigator(element);
                nav?.Navigate(startupType);
            }
        }
        #endregion        #region 任意のコントロールに対して、NavigationServiceExをアタッチできるようにするための添付プロパティpublicstatic NavigationServiceEx GetNavigator(DependencyObject obj)
        {
            return (NavigationServiceEx)obj.GetValue(NavigatorProperty);
        }
        // ↓protectedにして外部からは利用できないように。publicstaticvoid SetNavigator(DependencyObject obj, NavigationServiceEx value)
        {
            obj.SetValue(NavigatorProperty, value);
        }
        // Using a DependencyProperty as the backing store for Navigator.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty NavigatorProperty =
            DependencyProperty.RegisterAttached("Navigator", typeof(NavigationServiceEx), typeof(NavigationServiceEx), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
        #endregion
    }



    publicstaticclass NavigationServiceExtensions
    {
        /// <summary>/// view引数で指定されたインスタンスのページへとナビゲーションを行います。/// </summary>/// <paramname="element"></param>/// <paramname="view"></param>/// <returns></returns>publicstaticbool Navigate(this FrameworkElement element, FrameworkElement view)
        {
            var navigator = NavigationServiceEx.GetNavigator(element);
            return navigator.Navigate(view);
        }

        /// <summary>/// viewType引数で指定された型のインスタンスを生成し、そのインスタンスのページへとナビゲーションを行います。/// </summary>/// <paramname="element"></param>/// <paramname="viewType"></param>/// <returns></returns>publicstaticbool Navigate(this FrameworkElement element, Type viewType)
        {
            var navigator = NavigationServiceEx.GetNavigator(element);
            return navigator.Navigate(viewType);
        }
    }

これでFrameを使わず最低限のページ遷移が行えます。
WPF標準のFrame/NavigationWindowといったコントロール類のイヤな動作は塞ぎつつ、これらを使った時に近い感覚でのページ遷移もできるかと思います。(NavigationCommands.GoToPageでの遷移など)

余力があれば、ページ遷移前後のイベント通知を追加したり、遷移時のアニメーションなどをやってみようかな。

Visual Studio Code Insiders版が出ました

$
0
0

Visual Studio Codeの新機能を一足先に試せる、Insiders版がリリースされました。
今回からはInsiders版は別のインストーラを用いて、別々のアプリとしてインストールする形になっています。

つい先月、こんな記事↓書いてinsidersチャネル設定の方法を書いたばかりでしたが・・・・

まさかの1月の月例更新がinsiderチャネルにリリースされる前に、この方法は廃止となりました。。。/(^o^)\ナンテコッタイ

Visual Studio Code Insiders版

Introducing Insiders Builds | Visual Studio Code

今までは、insider版/stable版を切り替えるには、いちいちインストーラを動かす必要があったので、stable版を残しつつinsiders版を試すことはできませんでした。
今回からの新しいInsiders版は通常のVSCodeと共存インストールできるので、両方インストールしておいて気軽にInsiders版の新機能をお試しできるようになります。

またこのInsiders版をインストールしておけば、今後は通常版のVSCodeよりも一足早く、月例の更新が配布されるようです。

ダウンロードは↓から。
https://code.visualstudio.com/insiders

こんな風にデバッグ出力などの領域が、エディタを縦方向に分割して表示してくれるようになって、デバッグ時の視認性がよくなりました!!
f:id:minami_SC:20160203003722p:plain:w450

通常版とInsiders版の共存

このInsiders版は別インストーラでの配布になっていて、通常のVSCodeとは別アプリとして扱われます。

↓こんな風にスタートメニューにも別アプリとして登録。
f:id:minami_SC:20160203003403p:plain

インストールフォルダも、各種設定フォルダなども、通常版とInsiders版それぞれで別管理になります。
ということで、Insiders版を入れたら、拡張機能やsettings.jsonなどの設定を別途する必要があります。

コンテキストメニューからの起動
Insider版インストール時に、コンテキストメニューへの追加でチェックを付けておくと、以下のように右クリックメニューからどちらのバージョンを起動するか選べます。
f:id:minami_SC:20160203003413p:plain

settings.jsonのinsidersチャネル設定について

今回からInsiders版は別々のアプリとして配布するようになったので、以前までのinsidersチャネルでの配布は行わなくなるそうです。

"update.channel": "insiders"の設定をしていても、通常設定と同じようにstableチャネルでの更新となるようです。

JSON5を使ってみる

$
0
0

JSONファイルを書いててよく困る点の一つとして、コメントが書けない、、ってのがあります。

そこで、そんなJSONの不満点を解決してくれる、alt JSONなフォーマットの一つ。JSON5というのを使ってみました。

JSON5

公式サイトや、npmのページは以下の通り。
http://json5.org/
https://www.npmjs.com/package/json5

インストール

以下のコマンドでインストールします.

npm install json5 --save

このJSON5、"dependencies": {}となっていて、他のライブラリに依存してないみたいなんで、割と手を出しやすいかな。
f:id:minami_SC:20160204013101p:plain

Node.js関係のライブラリって、なんかインストールするとdependenciesの連鎖で、大量のライブラリの依存関係を持ってしまうものが多いですが、このJSON5はその辺もスッキリしてて好印象です。

使い方

まずは、こんなjson5のファイルを用意します。
sample.json5

{// こんな風にコメントを入れられます。"name": "hogehoge",
    // プロパティ名の方は、ダブルクォーテーションで括らなくてもOK
    sample: "This is JSON5 text.",
    
    // ↓最後にカンマがついててもOK"value": 123,
}

JSON5.parse/JSON5.stringifyでの読み書き

JSON5で、parse/stringifyというメソッドが用意されてます。
これを使って、JSON.parseなどと同じような感じで使うことができます。

var JSON5 = require('json5');
var fs = require('fs');

// JSON5のファイルを読み込みvar data = fs.readFileSync('./sample.json5');

// JSON5でパースvar obj = JSON5.parse(data);
// JSON5で文字列に戻すvar strJson5 = JSON5.stringify(obj);
console.log(strJson5);

var strJson = JSON.stringify(obj);
console.log(strJson);

出力結果

{name:"hogehoge",sample:"This is JSON5 text.",value:123}
{"name":"hogehoge","sample":"This is JSON5 text.","value":123}

ちなみに、JSON/JSON5それぞれのstringifyでの出力の違いは、プロパティ名をダブルクォーテーションで括るかどうか、って点みたい。

requireして読み込む

わざわざfsモジュールを使ってファイルを読んだりしなくても、requireでjson5を直接読み込む方法も用意されてます。

require('json5/lib/require');

var sample = require('./sample.json5');
console.log(sample.sample);

こんな風に、最初にrequire('json5/lib/require');と書いておくと、そのあとは.json5のファイルをそのままrequireして読み込むことができます。

VSCodeでJSON5ファイルの編集

JSON5のファイルは、.json5という拡張子で扱います。
そのため、VSCodeで普通にjson5のファイルを開くと、ただのプレーンテキストとして扱われてしまいます。

ですが、エディタの言語モードをJSONに変えれば、そこそこ読みやすく表示できます。
f:id:minami_SC:20160204013114p:plain
(ダブルクォーテーションなしのプロパティ名などは警告出ますが。。)

VSCodeのJSON

余談ですが、そういえばVSCodeもsettings.jsonやらいろんなJSONファイルで、ファイル中にコメントが書けるようになってます。
f:id:minami_SC:20160204013129p:plain

これどうやってるのかなぁ、、、と思ってちょろっとコードを追いかけてみました。

VSCodeのsettings.jsonをパースする処理は、多分このファイルのjson.parseとやってるあたりかな。。
https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/node/userSettings.ts

で、ここで出てくるjsonモジュールは↓のもの。
https://github.com/Microsoft/vscode/blob/master/src/vs/base/common/json.ts

どうやら、独自にjsonパーサーを定義してるみたいですね。

Nugetでパッケージを作ってみる

$
0
0

Nugetパッケージを作ってみたので、作り方を少しメモしときます。
実際にやってみると、思ってたよりだいぶ簡単に作れそうです。

Nugetのパッケージを作るには、以下の二通りの方法があります。

どちらの方法からでも、.nupkgという形式のファイルが出来上がります。

こいつをNugetギャラリーにアップロードして公開することもできますが、 そこまでせず、自分のローカルPC上にファイルを置いて、そこをプライベートなリポジトリとして使うこともできます。

今回は、このローカルPC上にファイルを配置しておく方の使い方を試してみました。

参考リンク

http://www.slideshare.net/kiyokura/nuget-16200386
http://www.buildinsider.net/enterprise/nugetprivate/01

↓ちょっと前の記事ですが、この動画を見れば、gui版のだいたいの使い方がわかると思います。
http://blogs.msdn.com/b/chack/archive/2013/03/25/visual-studio-spirit-aspnet-nuget.aspx

準備

まずは準備。 https://www.nuget.org/

以下のページからNuget.exeをダウンロードしてきます。
https://dist.nuget.org/index.html

このexeはただのコマンドラインツールなので、はじめのうちは以下のNuGet Package ExplorerというGUIツールもダウンロードして使いながら慣れていけばよいのかな、と思います。 ↓のページの「click here and you're done」というとこのリンクからインストールできます。
https://docs.nuget.org/Create/Using-a-Gui-to-build-packages

ClickOnceでのインストールがイヤという場合には、↓からzip圧縮された実行ファイル類をダウンロードできます。
https://npe.codeplex.com/releases/view/68211

作ってみる

一応、公式のドキュメントはこちら。 https://docs.nuget.org/create

guiから作ってみる

手始めに、NuGet Package Explorerを使って作ってみます。

WPF用にBindableBaseやRelayCommandなど、毎度使う各種基底クラスやヘルパークラス類を、Nugetパッケージとしてまとめてみました。
↓こんな感じのパッケージ。
f:id:minami_SC:20160207144631p:plain

細かい作り方は、参考リンクに上げた動画が詳しいので、ここでは省略。
これを保存すると、.nupkg形式のファイルを作ることができます。

基底の名前空間の利用

ちなみに、ライブラリの参照を加えるのではなく、ここのサンプルのようにコードをプロジェクトに追加する場合には、 Nugetパッケージ利用側のプロジェクトの名前空間に置き換えたい、という場合もあるかもしれません。

そんな時には、ファイル名を「元のファイル名.pp」という名前にしておき、ファイル内の置換したい部分に、$rootnamespace$というキーワードを入れておくと、その部分がプロジェクトの基底の名前空間に置換されます。

ローカル環境のギャラリー

nugetは通常は、インターネット上のnugetギャラリーからパッケージの取得を行います。

VisualStudioのオプションから、以下のようにパッケージ取得元にローカルのフォルダを追加します。
f:id:minami_SC:20160207144659p:plain

こうすると、Nugetパッケージマネージャから参照できるようになります。
f:id:minami_SC:20160207144802p:plain

こんな風にローカルPCのフォルダに自分用のプライベートなNugetパッケージ作って置いておくのも便利かも。
職場とかだったら、共有フォルダに.nupkg保管場所を作って、開発チーム内のローカルなNugetギャラリーとして共通ライブラリなどの管理するのもよさそうですね。

openerを使ってnpmスクリプトからブラウザを開いてみる

$
0
0

↓を見てこれはよさそう!!と思ってやってみました。
http://qiita.com/mysticatea/items/12bb6579b9155fd74586

このパッケージを使うと、npmスクリプトから指定したページをブラウザで開くことができます。
Win/Mac/Linuxどの環境でも、同じスクリプトでブラウザを開けるのがいい感じ。
これは便利!!

ここ最近、VSCodeからブラウザ開くタスク作れないかなぁ、、なんて思って色々調べてましたが、npmのパッケージを使って簡単にクロスプラットフォームに実現する方法がありました♪

以下のコマンドでdevDependenciesに入れておいて、npmスクリプトからブラウザ開くのに使えます。

npm install opener --save-dev

で、こんなnpmスクリプトを書いておくと、ブラウザを開くことができます。

"scripts": {
        :
    "browser": "opener http://localhost:3000/"
        :
  },

サーバーの起動とブラウザ起動をセットにする

今度はnode.jsのサーバー実行とブラウザの起動を、ひとつのスクリプトで一気に実行してみます。
npmスクリプト中では、&&複数のコマンドを続けて実行することもできますが、そうするとnodeのサーバーを起動して実行している間は後続のコマンドが実行されません。

ということで、複数のnpmスクリプトを並列で実行することができるnpm-run-allというパッケージを使ってみます。

インストールは以下のコマンドで。

npm install npm-run-all --save-dev
"scripts": {
    "server": "node ./bin/www",
    "browser": "opener http://localhost:3000/",
    "start": "npm-run-all -p server browser"
  },

こうしておくと、npm startと打つだけで、サーバーの起動とブラウザの起動をまとめて行うことができます。

TypeScriptのコンパイルもしてみる

もうひとひねり加えて、startコマンドの前にTypeScriptコンパイラコンパイルを走らせてみます。

"scripts": {
    "build": "tsc",
    "prestart": "npm run build",
    "server": "node ./bin/www",
    "browser": "opener http://localhost:3000/",
    "start": "npm-run-all -p server browser"
  },

こうすることで、npm startと打つだけで、tsファイルのコンパイル⇒node.jsのサーバー起動⇒ブラウザ起動、というのをまとめて行うことができます。

prestartを使わずに、npm-run-allだけで実行する場合はこんな感じ。

"start": "npm-run-all build -p server browser"
サンプルコード

↓express&typescriptなプロジェクトで使ってみたサンプルです。
https://github.com/sourcechord/typescript-express-template

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

$
0
0

VSCode 0.10.8のstable版がリリースされました。
先日リリースされたVSCode Insiders版とだいたい同じ内容ですが、今回も更新点でめぼしい部分をざっとメモしておきます。

https://code.visualstudio.com/Updates

JavaScriptの新言語サービスSalsaのプレビュー版

VSCodeのJavaScript言語サービスとして、新しくSalsaというものを作っているそうです。
で、このSalsaを使うようになるとJSDocで書いたコメントの情報がインテリセンスで表示されるようになったりするみたい。
あと、JavaScriptの流儀で定義したクラスとかもちゃんと認識して、いい感じなインテリセンス表示してくれるっぽい。

Salsaを有効にするには

今回のSalsaの機能は、プレビュー版という位置づけでリリースされていて、デフォルトでは無効になっています。
Salsaを使うにはTypeScript1.8が必要です。で、現状1.8はβ版ですが、こいつをインストールして、VSCodeが使うTypeScriptモジュールをこの1.8のものに切り替え、VSCODE_TSJSという環境変数とともに起動する必要があります。

自分は普段、生のJavaScriptはあんま書かないので、この辺の機能はとりあえずスキップ。
SalsaとTypeScript1.8関係は、次の2月の更新が出てから試してみようかな、と思います。

エディタの新機能

テーマ

今まで、dark/lightというのが標準のテーマにありましたが、dark+/light+というテーマも加えられました。
VisualStudioと同じようなシンタックスハイライトを行うのがdark/lightで、今までのVSCodeのテーマがlight+/dark+になります。
VSCodeは、昨年11月のリリース時にTextMate形式のシンタックスハイライトを使うようになり、よりカラフルなハイライト表示を行うようになってましたが、このよりカラフルなテーマがdark+/light+となってます。

darkとdark+を比較してみました。
+のテーマでは、変数定義や関数定義の時の型もハイライト表示されるようになります。

  • dark
    • f:id:minami_SC:20160214234707p:plain
  • dark+
    • f:id:minami_SC:20160214234729p:plain

その他細かい点

以下のような細かい変更・追加があります。

  • タブキーでフォーカス移動するように
  • Experimentalな機能として、音声読み上げアプリへの対応を進めたみたい。
  • アプリのローカライズに取り掛かり始めたとのこと
    • 日本語はまだ対応してないみたいなんで、普通に英語表示のままです。
    • あと、↓のWikiとか見てみると、3月末のBuild2016のタイミングでの1.0リリースを目指してるみたいなんで、そこに向けてローカライズ作業進めてるのかな、って印象です。
    • https://github.com/Microsoft/vscode/wiki/Roadmap
  • Ligatures for VSCode
    • =>とか!=みたいな特定の文字の組み合わせを専用の形状の特殊な文字で表示するのをLigaturesというそうです。で、これに対応したとのこと。 これは、個人的にはあんま使わなそうかなぁ。。
    • 一応、自分の環境でも試してみたけど、このLigaturesの特殊な表示はされませんでした。。 というか、たぶん対応するフォントじゃないとこの機能使えない、、、のかな??
  • 検索ウィジェットの改善

    • 検索時に「○○ of XX」みたいな表示が出るようになり、検索に一致した数が何個で、現在何個目の項目を選択しているのか、が表示されるようになりました。
    • f:id:minami_SC:20160214234745p:plain
  • Input Handling

    • AutoHotKeyなどのキーボードオートメーションを行うツールからの入力を受け付けるように対応したそうです。
  • カーソルのスタイル
    • setting.json"editor.cursorStyle": "block"という設定をすると、カーソルがブロック形状で表示ができるようになりました。
  • Auto Save
    • 自動保存関係の設定を、setting.jsonから色々行えるようになった。
  • File Picker
    • filePicker.alternateFileNameMatchingの指定がデフォルトで有効になりました。Ctrl+Pでファイル検索するときに、あいまいな入力でも候補に色々表示してくれる機能です。
  • インテリセンス
    • インテリセンスの候補表示時に、より詳細な内容を表示するためのUI追加

全体的なUI系

メニューバーを非表示にできるように

メニューから、View⇒Toggle Menu Bar、という項目を選択すると、VSCodeのメニューの表示/非表示を切り替えることができます。
非表示にした場合は、Altキーを押すとメニューが一時的に出てきます。

縦方向のパネル分割

これは以前からだいぶ要望があったヤツですね。
VSCodeのOutputやDebug Consoleを、縦方向に分割したパネルで表示するようになりました。
f:id:minami_SC:20160214234802p:plain

デバッガ関係

オブジェクトの内容のツリー表示

変数にカーソルホバーした時の値表示で、ツリー表示が行われるようになりました。
f:id:minami_SC:20160214234820p:plain
これは見やすくて助かりますね。

条件付きブレークポイント

VSのように、条件付きのブレークポイントを作れるようになりました。
ブレークポイントを右クリックすると、メニューから「Edit Breakpoint」という項目が選べるようになってます。
すると、以下のようなUIが出てきて、条件文を入力できるようになります。
f:id:minami_SC:20160214234848p:plain
頻繁には使わないかもですが、Node.jsみたいなイベント駆動なコードをデバッグするときには、割と便利に使えるんではないか、と思います。express使ってる場合だったら、クエリパラメータやURIパラメータが特定の値のときだけブレークする、などの使い道もあるかと。

変更したプロパティの強調表示

デバッガのWatchやVariableの項目で表示している値が変更されると、以下のように強調表示されるようになりました。
f:id:minami_SC:20160214234901p:plain

Inlined Sourceへの対応

デバッガのソースマップとの連携で、Inlined Sourceという方式に対応したそうです。
今までのVSCodeでは、Inlined source mapsというのには対応してたけど、こちらは対応してなかったみたい。 で、これらは言葉が似てて紛らわしいので、ざっと概要が説明されてました。
この二つは、言葉は似てるけど独立した概念で、それぞれ以下のような意味らしいです。知らんかった・・・orz

  • Inlined source maps
    • ソースマップの内容は、出力されたjsファイルには含めないけど、jsファイルの最後にURLを記載している方式。
  • Inlined source
    • オリジナルのソースの情報を、ソースマップ中に埋め込む方式。

リモートデバッグ

別PC上で実行しているNodeのコードに対してリモートでアタッチできるようになったみたいです。
Dockerコンテナ上で実行してるコードのデバッグとかできるみたい。

その他細かいこと

launch.jsonのパス指定

2月の更新で、以下のような変更を予定しているそうです。

  • launch.jsonのパスの扱い 今までは、launch.jsonの中で相対パスで書いたものは、自動でプロジェクトのルートからの相対パスとしてあつかっていました。 launch.jsonを、VSCodeの他の各種セッティングファイルの動作と統一性を持たせるために、相対パスでの指定は受け付けないようにする予定だそうです。
    プロジェクトのルートからの相対パス指定をするには、${workspaceRoot}というのをパス指定の頭に付ければよいとのこと。
--nolazyオプション

今までは、F5キー押したときに、自動で作られるlaunch.jsonには、デフォルトで--nolazyオプションが付いていました。
これを、今後は付けなくなるみたいです。
デフォルトではつけなくなりますが、もし、デバッグしててブレークポイントがヒットしなくなったり、なんか動きが信頼できないなぁ、、、ってなったら、--nolazyオプションを付けてみるといいそうです。

Mono Debugging

Monoを使ってデバッグするときにもexternalConsoleオプションが使えるようになったそうです。
このオプションをtrueにすることで、デバッグ実行時のコンソール出力を外部のウィンドウではなく、VSCodeのDebug Consoleに表示できるようになります。

その他

WPFでLightBox風のポップアップ表示をするサンプル

$
0
0

WPFLightBox風のポップアップ表示を行うサンプルを作ってみました。

コードはこちら↓

以下のような点を意識して作ってみました。

  • XAML上の構造に依存しない
  • モードレスな表示のみサポート
    • Adornerを使って同一ビジュアルツリー上に表示するんで、MessageBox.ShowみたいなノリでUIスレッドを止めると、肝心のダイアログ表示が行われない。
  • 複数ダイアログの同時表示
    • モードレス表示をするので、ダイアログ表示中に別ダイアログの表示処理が重なる可能性がある。
      • ↑みたいなときに、複数同時表示できるようにしておく。
  • 表示方法のカスタマイズ
    • Template/ItemsPanel/ItemContainerStyleなどで、表示方法をカスタマイズできるようにしておく。
    • 複数表示するときに、ItemsPanelをいじって、縦方向に並べる/横方向に並べる、というのを切り替えられる。

使い方

LightBoxの表示方法は、以下のように添付プロパティを使って色々カスタマイズできるようになってます。

MainWindow.xaml

<Window x:Class="LightBoxSample.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:ctrl="clr-namespace:LightBoxSample.Controls"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:LightBoxSample"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="MainWindow"Width="525"Height="350"mc:Ignorable="d"><ctrl:LightBox.Template><ControlTemplate><Grid Background="#88000000"><ItemsPresenter /></Grid></ControlTemplate></ctrl:LightBox.Template><ctrl:LightBox.ItemsPanel><ItemsPanelTemplate><Grid/></ItemsPanelTemplate></ctrl:LightBox.ItemsPanel><ctrl:LightBox.ItemContainerStyle><Style TargetType="{x:Type ContentControl}"><Setter Property="ContentTemplate"><Setter.Value><DataTemplate><Border Width="150"Height="150"Background="Beige"BorderBrush="Black"BorderThickness="2"Margin="5"CornerRadius="3"><ContentPresenter Content="{Binding}" /></Border></DataTemplate></Setter.Value></Setter></Style></ctrl:LightBox.ItemContainerStyle><Window.Resources /><Grid><Button Margin="50"HorizontalAlignment="Left"VerticalAlignment="Top"Click="button_Click"Content="Show LightBox" /></Grid></Window>

で、こんな風に表示を行います。

LightBox.Show(this, new SimpleDialog());

SimpleDialogは、ダイアログ表示の中身となるUserControl。
f:id:minami_SC:20160216075746p:plain

こんな風に並べて表示することもできます。
f:id:minami_SC:20160216075757p:plain

もう少しいい感じにコードを整理したら、ちゃんとライブラリの形に整えてNugetにでも上げてみようかな、と。


Nugetギャラリーにライブラリを登録するまでの手順

$
0
0

WPF用のちょっとしたライブラリを作って、初めてNugetに登録してみました。
ライブラリ自体はまだまだ作りかけの状態ですが、とりあえず登録までの手順をφ(..)メモメモ

参考リンク

http://docs.nuget.org/Create/Creating-and-Publishing-a-Package
https://sakapon.wordpress.com/2013/07/16/nugetpackage/

アカウントの作成

まずは、↓のページからアカウントを作成します。
https://www.nuget.org/

Register/Sign in、という項目をクリックして、そこからアカウントを作ります。
Microsoftアカウントがあれば、そのアカウントに追加情報を登録って感じで行けます。

Nuget.exeの取得

Nugetのページから、最新版のNuget.exeをダウンロードし、適当な場所においてパスを通しておきます。

nuspecファイルの作成

nuspecを作るには、csprojのあるフォルダで、nuget specと実行すればOK。
これで、プロジェクトファイルの情報を元に、nvspecファイルが出来上がります。

こんな感じのxmlファイルが出来上がります。

<?xml version="1.0"?><package ><metadata><id>$id$</id><version>$version$</version><title>$title$</title><authors>$author$</authors><owners>$author$</owners><licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl><projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl><iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl><requireLicenseAcceptance>false</requireLicenseAcceptance><description>$description$</description><releaseNotes>Summary of changes made in this release of the package.</releaseNotes><copyright>Copyright 2016</copyright><tags>Tag1 Tag2</tags></metadata></package>

licenseUrl、projectUrlなどには、サンプル値が入力されてるので適宜書き換えます。
tagsには、nugetギャラリーに登録する際に付けておきたいタグをスペース区切りで入力します。

また、$id$などのような$で括られている部分は、パッケージ作成時にプロジェクトファイルに書かれた値で補間することができます。

プロジェクトのアセンブリ情報修正

プレースフォルダには、プロジェクトファイルの情報が書き込まれます。
プロジェクトのプロパティから、アセンブリ情報を編集しておきましょう。

以下のように、プロジェクトのアセンブリ情報が利用されるようです。

項目名プロジェクトファイルの対応項目
$id$アセンブリ
$title(たぶんアセンブリ名)
$version$アセンブリバージョン情報(AssemblyVersionAttribute)
$author$会社
$description$説明

パッケージの作成

パッケージは以下のコマンドで作ります。

nuget pack Lighty.csproj -Prop Configuration=Release

注意点は二つ。

  • Nugetコマンドの引数にはcsprojを渡す。
    • .nuspecファイルを渡すと、$でくくられたプレースフォルダが認識されず、パッケージの作成に失敗します。
    • .nuspecファイル指定で作る場合には、プレースフォルダに実際の値を手入力しておく必要があります。
  • -Prop Configuration=Releaseという項目で、パッケージ作成時に使用するソリューション構成を指定します。
    • これをつけ忘れると、デフォルトのDebugビルドになってたりするので要注意。
      • 久しぶりにやったりすると絶対忘れてる気がするので、自分はパッケージ作成用のbatファイルにしておいて、コードと一緒にgitで管理してます。

Nugetギャラリーへの登録

Nugetギャラリーに登録する際にはAPIキーの入力が必要です。
一度以下のコマンドを打っておけばAPIキーが保存されます。

Nuget setApiKey [Nugetの自身のアカウントページで表示されるキー]

続いて以下のコマンドで、作成した.nupkgファイルを指定すれば、Nugetギャラリーに登録できます。

nuget push nupkgファイル

すると、こんな風に自分のライブラリがちゃんとNugetに上がります。

インデックスされるのをしばらく待つと、Nugetのパッケージマネージャからも検索できるようになります。
こうやってNugetのパッケージマネージャから使えるようになると、なんか感慨深いもんがあります♪ f:id:minami_SC:20160219201000p:plain

実際に手を動かしてみると、結構簡単にできるもんなんだなぁ、とビックリしました。

MahApps.Metroを使ってみた

$
0
0

お手軽にWPFアプリにモダンなデザインを適用できる、MahApps.Metroというライブラリを使ってみました。
MahApps.Metro Documentation

この手のモダンなデザインを適用するライブラリでは、 Modern UI for WPF(MUI)というのもあります。
まだちょろっと使ってみただけですが、MUIと比較してみるとMahApps.Metroの方がクセが少なくてシンプルで使いやすそうな印象です。

使い方

元のウィンドウ

まずは、こんな感じのウィンドウを作ります。
TextBlockとTextBox、Buttonを配置しただけのシンプルなページです。
f:id:minami_SC:20160223002940p:plain:w300

<Window x:Class="MahAppsMetroTest.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:MahAppsMetroTest"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="MainWindow"Width="525"Height="350"mc:Ignorable="d"><Grid><TextBlock Margin="10,10,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Text="TextBlock"TextWrapping="Wrap" /><Button Width="75"Margin="10,31,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Content="Button" /></Grid></Window>

インストール

NugetからMahApps.Metroで検索してインストールします。

準備

WindowをMetroWindow派生クラスになるように修正します。
xamlとコードビハインドをそれぞれ以下のように修正します。

MainWindow.xaml

<Controls:MetroWindow x:Class="MahAppsMetroTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:MahAppsMetroTest"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="MainWindow"Width="525"Height="350"mc:Ignorable="d"><Grid><TextBlock Margin="10,10,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Text="TextBlock"TextWrapping="Wrap" /><Button Width="75"Margin="10,31,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Content="Button" /></Grid></Controls:MetroWindow>

MainWindow.xaml.cs

using MahApps.Metro.Controls;

namespace MahAppsMetroTest
{
    /// <summary>/// MainWindow.xaml の相互作用ロジック/// </summary>publicpartialclass MainWindow : MetroWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

コードビハインド側は、クラスの継承の部分を省略してもokとの事。
windowはパーシャルクラスとして定義されてるんで、xaml側でちゃんとクラスの宣言されてますもんね。

スタイルの設定

続いて、app.xamlにデザインのテーマを決めるためのリソースを追加します。

App.xaml

<Application x:Class="MahAppsMetroTest.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:MahAppsMetroTest"StartupUri="MainWindow.xaml"><Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! --><ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" /><ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" /><ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" /><!-- Accent and AppTheme setting --><ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" /><ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" /></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources></Application>

スタイルは他にもいろいろあります。スタイルについてはまた後程。
で、たったこれだけで、こんな感じにイカしたデザインのウィンドウが出来上がります。
f:id:minami_SC:20160223003015p:plain:w300

スタイルの変更

アプリ全体のスタイルは、App.xamlに設定するResourceDictionaryを切り替えることで選択できます。

Red, Green, Blue, ・・・などのアクセントカラーの選択と、BaseLight/BaseDarkのどちらかから白基調/黒基調のどちらにするかを選ぶことができます。
App.xaml

<!-- Accent and AppTheme setting --><ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" /><ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />

用意されているスタイル類は以下に書かれてます。
http://mahapps.com/guides/styles.html#app

BaseLight/BaseDarkの比較

BaseLightBaseDark
f:id:minami_SC:20160223003015p:plain:w250f:id:minami_SC:20160223003144p:plain:w250

ResourceをApp.xamlにではなく、各MetroWindowに書けば、Windowごとに別々のスタイルを設定することもできます。

コードビハインドからのスタイル切替

ThemeManagerクラスを使うと、コードから動的にアプリのデザインを変更することができます。

例えばボタンクリックのイベントハンドラに、↓みたいなコードを書けば、ボタン押下時にGreen/BaseDarkなデザインに切り替わります。

privatevoid Button_Click(object sender, RoutedEventArgs e)
        {
            var theme = ThemeManager.DetectAppStyle(Application.Current);

            ThemeManager.ChangeAppStyle(Application.Current,
                                        ThemeManager.GetAccent("Green"),
                                        ThemeManager.GetAppTheme("BaseDark"));
        }

ウィンドウのカスタマイズ

MetroWindowクラスは、以下のようなプロパティでカスタマイズできます。

各種基本のプロパティ

プロパティ名内容
ShowTitleBarタイトルバーの表示有無
ShowIconOnTitleBarタイトルバーにアイコンを表示するか否か
ShowMinButton
ShowMaxRestoreButton
ResizeModeこいつをNoResizeにしたりCanMinimizeにしたりすると、最大化/最小化ボタンの表示有無も併せて切り替わります。
SaveWindowPositionウィンドウの表示位置を保存するか否か。こいつをTrueにしておくと、次回ウィンドウ表示時には、依然閉じた際の座標で表示されるようになります。

WindowCommands

MetroWindowクラスには、LeftWindowCommands/RightWindowCommandsというプロパティがあり、タイトルバーの左右にコマンド実行のためのボタン類が作れるようになっています。

こんな感じ。

<Controls:MetroWindow x:Class="MahAppsMetroTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:MahAppsMetroTest"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="MainWindow"Width="500"Height="300"BorderThickness="0"GlowBrush="Black"mc:Ignorable="d"><Controls:MetroWindow.RightWindowCommands><Controls:WindowCommands><Button Content="settings" /><Button Content="acount" /></Controls:WindowCommands></Controls:MetroWindow.RightWindowCommands>以下略

f:id:minami_SC:20160223003354p:plain

Windowのデザイン

ボーダー付きウィンドウ

MetroWindowに、以下のプロパティをつけることでこんな枠付きのデザインになります。

BorderBrush="{DynamicResource AccentColorBrush}"
BorderThickness="1"

f:id:minami_SC:20160223003340p:plain

VisualStudio風のWindow枠

GrowBrushプロパティの指定を行うと、こんな感じのVS風の光ったウィンドウ枠になります。

GlowBrush="{DynamicResource AccentColorBrush}"
BorderThickness="1"

f:id:minami_SC:20160223003423p:plain

GlowBrushをBlackにして、枠線の太さを0にしておけば、ウィンドウにドロップシャドウを付けたようなデザインになります。

GlowBrush="Black"
BorderThickness="0"

f:id:minami_SC:20160223003441p:plain

各種コントロール

Button系

Button/ToggleButtonに使える、以下のようなスタイルが用意されてます。

<WrapPanel Orientation="Horizontal"><Button Width="75"Margin="10"Content="Button" /><Button Width="75"Margin="10"Content="Button"Style="{DynamicResource MetroCircleButtonStyle}" /><Button Width="75"Margin="10"Content="Button"Style="{DynamicResource SquareButtonStyle}" /><Button Width="75"Margin="10"Content="Button"Style="{DynamicResource AccentedSquareButtonStyle}" /></WrapPanel>

f:id:minami_SC:20160223010440p:plain

アイコン類のパッケージ

MetroCircleButtonは、アイコンリソースとともに使うといい感じになります。
アイコンなどはMarApps.Metro本体には含めず、MahApps.Metro.Resourcesという別パッケージで配布されています。

以下のコマンドで、色々なアイコン類を含んだリソースファイルがプロジェクトに追加されます。

Install-Package MahApps.Metro.Resources
アイコンの使い方

App.xamlのリソース定義に↓を加えておきます。

<ResourceDictionary Source="/Resources/Icons.xaml" />

すると、こんな風にリソースとして色んなアイコンが使えるようになります。

<Button Width="55"Height="55"Margin="10"Style="{DynamicResource MetroCircleButtonStyle}"><StackPanel Orientation="Vertical"><Rectangle Width="20"Height="20"><Rectangle.Fill><VisualBrush Stretch="Fill"Visual="{StaticResource appbar_add}" /></Rectangle.Fill></Rectangle><TextBlock Text="Add" /></StackPanel></Button>

f:id:minami_SC:20160223010553p:plain

TextBox

TextBoxの動作をカスタマイズするための以下のような添付プロパティが用意されています。

添付プロパティ名内容
TextBoxHelper.Watermarkテキストが空のときに説明文のような透かし文字を表示できます。
TextBoxHelper.ClearTextButton入力した文字をクリアするためのボタンが追加されます
<TextBox Width="120"Height="23"Margin="10"Controls:TextBoxHelper.Watermark="This is a textbox"TextWrapping="Wrap" /><TextBox Width="120"Height="23"Margin="10"Controls:TextBoxHelper.ClearTextButton="True"TextWrapping="Wrap" />

f:id:minami_SC:20160223003747p:plain

MahApps.Metroのカスタムコントロール類

MahApps.Metroでは、他にも色々と便利なコントロールが用意されています。

MetroProgressBar

Win8風なプログレスバーです。

<Controls:MetroProgressBar Width="120"Margin="5"Value="60" /><Controls:MetroProgressBar Width="120"Margin="5"IsIndeterminate="True"Value="60" />

f:id:minami_SC:20160223003801p:plain

ProgressRing

こんな感じのよく見かけるタイプのプログレス表示。
キャプチャ画像にしてしまうと、なんだかよくわかりませんが、、、くるくる回ってる進捗表示。

<Controls:ProgressRing IsActive="True" />

f:id:minami_SC:20160223003909p:plain

RangeSlider

下限値と上限値で範囲指定ができるスライダー。

<Controls:RangeSlider Width="130"Margin="10"Minimum="0"Maximum="100"LowerValue="10"UpperValue="70"MinRange="5"/>

↓こんな風に表示されます。
f:id:minami_SC:20160223004149p:plain

SplitButton/DropDownButton

SplitButtonはボタン右端の部分をクリックすると、コンボボックスみたいなドロップダウンリストが出てきて、要素を選択することができます。選択した要素がボタンのコンテンツとして表示されます。

SplitButton/DropDownButtonともに、ComboBoxなどと同じようにItemsSourceやDisplayMemberPathなどのプロパティを使ってリストに表示する要素を設定できます。

<Controls:SplitButton Margin="10"HorizontalAlignment="Center"VerticalAlignment="Center"HorizontalContentAlignment="Left"VerticalContentAlignment="Center"Icon="{DynamicResource appbar_alert}"ItemsSource="{Binding SampleList}"SelectedIndex="2" /><Controls:DropDownButton Width="120"VerticalContentAlignment="Center"Content="Test"Icon="{DynamicResource appbar_music}"ItemsSource="{Binding SampleList}"/>

f:id:minami_SC:20160223004208p:plain

Tile

win8以降のストアアプリ風なタイルのコントロール。

<Controls:Tile Title="Tile1"Width="100"Height="100"Count="1"TiltFactor="2" /><Controls:Tile Title="Tile2"Width="100"Height="100"Count="3"TiltFactor="2" />

f:id:minami_SC:20160223004222p:plain

ToggleSwitch

こんな風にトグルスイッチ表示ができるコントロールです。
主なプロパティはこんな感じ。

プロパティ名内容
Header項目のヘッダーを設定
OnLabelON状態の表示文字列
OffLabelOFF状態の表示文字列
IsCheckedトグル状態がONになっているか
<Controls:ToggleSwitch Header="項目名" /><Controls:ToggleSwitch Header="ヘッダー"OnLabel="オン"OffLabel="オフ"IsChecked="True"/>

f:id:minami_SC:20160223004247p:plain

NumericUpDown

数値入力用のテキストボックスで、コントロール右端に数値を増減させるためのボタンが付いたコントロールです。

プロパティ名内容
Minimum下限値を設定
Maximum上限値を設定
Interval一回のアップダウンでの変化量を設定
Speedup+-ボタンを押し続けたときに、数値の変化する速度を上げるかどうかを設定
小数点を持つかどうかを設定
InterceptArrowKeys上下のカーソルキーで、数値の増減をできるようにするかを設定。Falseにするとカーソルキー動作が無効になります
InterceptMouseWheelマウスのホイールで数値の増減をできるようにするかを設定。
InterceptManualEnterキーボードから数値以外のテキスト入力を抑制するかどうかを設定。
HideUpDownButtons+-のボタンを非表示にします
StringFormat数値表示のフォーマット指定
<Controls:NumericUpDown Minimum="0"Maximum="100"Interval="5" />

f:id:minami_SC:20160223004502p:plain

FlipView

UWPなどにある同名のコントロールと同じようなものです。
表示する要素は、以下の例のようにXAMLで直接定義もできますし、ItemsSourceプロパティで設定することもできます。

<Controls:FlipView BannerText="バナーテキスト"IsBannerEnabled="True"><Image Source="Images/1.jpg"Stretch="UniformToFill" /><Image Source="Images/2.jpg"Stretch="UniformToFill" /><Image Source="Images/3.jpg"Stretch="UniformToFill" /></Controls:FlipView>

f:id:minami_SC:20160223004622p:plain

こんなプロパティが用意されてます。

プロパティ名内容
BannerTextコントロール下部に表示するテキストを設定
IsBannerEnabledBannerTextを表示するか否かを設定
Dialogs系コントロール

今度はダイアログ表示用のコントロール。
全画面でメッセージを表示するMessageDialogと、プログレス表示もできるタイプのProgressDialogというクラスがあります。

それぞれ、ShowMessageAsync/ShowProgressAsyncというメソッドで開くことができます。

<WrapPanel Grid.Row="1"><Button Margin="10"Click="ShowMessageDialog"Content="Show MessageDialog" /><Button Margin="10"Click="ShowProgressDialog"Content="Show ProgressDialog" /></WrapPanel>
// 以下のusing文を追加using MahApps.Metro.Controls.Dialogs;
// 中略private async void ShowMessageDialog(object sender, RoutedEventArgs e)
        {
            await this.ShowMessageAsync("タイトル", "本文");
        }

        private async void ShowProgressDialog(object sender, RoutedEventArgs e)
        {
            var pdc = await this.ShowProgressAsync("タイトル", "プログレス表示", true);

            for(var i=0; i<10; i++)
            {
                pdc.SetProgress(1.0 / 10 * i);
                await Task.Delay(100);
            }
            await pdc.CloseAsync();
        }

↓MessageDialogはこんな感じの表示です。
f:id:minami_SC:20160223004704p:plain

続いてProgressDialog。MessageDialogの下部にプログレス表示がくっついた感じです。
f:id:minami_SC:20160223004720p:plain

ここでは省略しますが、CustomDialogというクラスを使うと、任意の要素をこの形のダイアログで表示することができるようです。

Flyouts

win8系のストアアプリで使われてたFlyoutコントロールを再現したもの。
正直、デスクトップアプリだと、この辺のコントロールはあんま使わないかな、って気がします。。

使い方はこんな感じ。

<Controls:MetroWindow x:Class="MahAppsMetroTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:MahAppsMetroTest"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:sys="clr-namespace:System;assembly=mscorlib"Title="MainWindow"Width="500"Height="350"GlowBrush="{DynamicResource AccentColorBrush}"mc:Ignorable="d"><Controls:MetroWindow.Flyouts><Controls:FlyoutsControl><Controls:Flyout x:Name="flyout"Width="200"Header="Flyout"Position="Right"><Button Width="75"Margin="10"HorizontalAlignment="Left"VerticalAlignment="Top"Content="Button" /></Controls:Flyout></Controls:FlyoutsControl></Controls:MetroWindow.Flyouts><Grid Margin="10"><Button Width="75"Margin="10,10,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Click="ShowFlyout"Content="Show Flyout" /></Grid></Controls:MetroWindow>

コードビハインドから以下のようにIsOpenプロパティをtrueにすることでFlyoutを表示できます。

privatevoid ShowFlyout(object sender, RoutedEventArgs e)
        {
            this.flyout.IsOpen = true;
        }

もちろん、XAML上でIsOpenプロパティをバインドして開いてもOK!!
f:id:minami_SC:20160223004744p:plain

主なプロパティはこんな感じ。

プロパティ名内容
Positionフライアウトを表示する位置を指定(Left/Right/Top/Bottomから選択)
Theme(Adapt/Inverse/Dark/Light/Accentから選択)

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

$
0
0

2月の月例アップデートとしてVSCode 0.10.10が出ました。
今回もざっと主な新機能・変更点など、個人的に目についた部分をまとめておきたいと思います。

言語関係

JavaScriptの言語サービスがSalsaに切り替え

前回のリリースでは、プレビュー扱いになっていたSalsaですが、今回からはデフォルトで有効な状態になっています。

Salsaの動作

こんな風に、ES5スタイルなJavaScriptのクラス定義やプロパティ定義も認識し、インテリセンスでの補完にも対応しています。
f:id:minami_SC:20160315004106p:plain
また、jsdocスタイルのコメントを認識し、インテリセンスでその情報を表示してくれます。

VSCode内蔵のLinter廃止

以前までは、javascript.validate.lint.*などの設定で、VSCodeの標準のLintツールの動作をカスタマイズできるようになってました。
今回のバージョンからは、この標準のLintツールは廃止されました。
この手の静的解析ツールは、自分で別途好みのものを使うように、ってスタンスのようです。
jshint, eslintなどのツールと、VSCode用の拡張機能を組み合わせて、自分好みの設定にするとよいでしょう。

TypeScript関係

VSCode内部で使用するTypeScriptが1.8.2になりました。

C#関係

C#を用いた開発をするためには、以下の拡張機能をインストールします。
https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp

とりあえず、こんな風に極力VSCode本体の機能が肥大しすぎないようにしているのかな?

ちなみに、C#のファイルを開くと、この拡張機能をインストールするか確認するメッセージが出てくるようです。

エディタ関係

コードの折り畳み

VSCodeへの最も大きな要望だった、コードの折り畳み機能が実装されました!!
https://visualstudio.uservoice.com/forums/293070-visual-studio-code/suggestions/7752321-add-code-folding-support

こんな風にエディタ領域の 左側に、折りたたみに関するUIが表示されます。
f:id:minami_SC:20160315004124p:plain
コードを折りたたむための、ボタンなどは、カーソルをこの領域の上にホバーさせた時に表示されます。

コードを折りたたんでいる箇所の表示は常に表示されます。

ショートカット

コードの折り畳みに関して、以下のようなショートカットが追加されました。

キー内容
Ctrl+Shift+[カーソルのある領域を折り畳み
Ctrl+Shift+]カーソルのある個所の折り畳み解除
Ctrl+Shift+Alt+[エディタ上のすべての折り畳み可能な箇所を折り畳み
Ctrl+Shift+Alt+]エディタ上のすべての折り畳みを解除

インデントの調整

現在のインデントの設定が、ウィンドウ下部のステータスバーに表示されるようになりました。
f:id:minami_SC:20160315004205p:plain
また、ここをクリックすると、インデントの設定を変えられるようになってます。

ルーラーの設定

editor.rulersという設定で、テキストエディタ領域にルーラー表示を出来るようになりました。

このプロパティは、配列で指定する形式になっているので、複数のルーラー指定を行うことができます。
例えば、"editor.rulers": [80, 120]と設定すると、以下のように二つのルーラーが表示されるようになります。
f:id:minami_SC:20160315004246p:plain

デフォルトの改行コード設定

files.eolという設定で、デフォルトの改行コードの指定ができるようになりました。
"\n"とか、"\r\n"などを指定します。

その他細かい点

  • インテリセンスのパフォーマンス向上
  • WebWorkerの改善

workbench

定義を横に開く

Ctrl+K F12で、定義箇所を、別タブとして横に開きます。
個人的にこれはあんま使わなそう…

現在のファイル以外すべて閉じる

現在見ているファイル以外を閉じる機能が追加されました。

f:id:minami_SC:20160315004305p:plain
ちょっとメンドイですが、Ctrl+K Ctrl+Shift+Wというショートカットも用意されています。

拡張機能のおススメ機能

vscodeの利用状況に応じて、オススメの拡張機能を提示してくれる機能が追加されました。
何に基づいてオススメしてるのが、よくわかりませんが・・・

よく編集してるファイルの拡張子とか見てるのかな??

vscodeの拡張機能ギャラリーのパフォーマンス向上

拡張機能のギャラリーで、サーバー側のパフォーマンス向上をしたそうです。
ext installなどのコマンドで拡張機能を検索した際の応答が改善されているとのこと。

debugging

launch.json

launch.jsonの各種パス指定で、今までは、絶対パスでなければ相対パスとして扱っていました。
今回のバージョンからは、基本的にパス指定は絶対パスとして扱われるようになります。

これは、前回のアップデート時に予告されてた変更ですね。

プロジェクトのルートからの相対パスを指定したい場合には、${workspaceRoot}/というパス指定と組み合わせて行うようにする必要があります。

preLaunchTaskの動作改善

launch.jsonで設定する、preLaunchTaskの動作に以下のような修正が入りました。

  • preLaunchTaskがエラーで失敗した場合、その後のデバッガのプロセスを開始しないように変更
  • preLaunchTaskが、watch系の動作だった場合、preLaunchTaskが完了してアクティブな状態になるまで、デバッガを開始しないように変更
  • tasks.jsonが存在しないのに、preLaunchTaskが設定されていた場合、tasks.jsonを作るようにメッセージが出るようになった

nodemonへの対応

Node.jsを使った開発をする際、コードの変更を検知しnodeのプロセスを再起動してくれるnodemonというツールがあります。
VSCodeのデバッガがこのnodemonに対応し、nodemonでのプロセス再起動時に自動でアタッチしなおすことができるようになったみたいです。
これは近いうちに試してから、別途メモしようかと思います。

関数のブレークポイント

Debugタブ下部の、BREAKPOINTSの「+」ボタンを押すと、関数名指定でブレークポイントを作成できるようになりました。
f:id:minami_SC:20160315004751p:plain

ただし、この関数のブレークポイントは色々と条件があるようで、

  • グローバル関数でなければ設定できない
  • 関数が定義された後(デバッガ起動後?)でなければ設定できない などの制限があるようです。

Mono Debugging

Monoのデバッグ機能はオプション扱いになりました。
この機能を使う場合には別途拡張機能のインストールを行う、という形になりました。

その他

  • Telemetry Opt Out
    • VSCodeはアプリの操作情報やクラッシュ時のレポートなどをMSに送信する、という機能がついてます。
    • この手のユーザー情報の送信は、setting.jsontelemetry.enableTelemetryの設定から無効にすることができます。
  • コマンドライン引数
    • VSCodeに「--help」や「--version」などの引数を指定して起動すると、コマンドラインオプションのヘルプが出たり、バージョン表示がされたりするようになりました。
  • Accessibility
    • ハイコントラストのテーマが、Win以外の環境(Mac/Linux向け)にも追加されました。
  • などなど

MADOSMAにWindows 10 Mobileの更新が来ました

$
0
0

やっと来た!!

そのうちOTAアップデート来るだろ、と思って待ってたら、、、なかなか来なくてずいぶんやきもきしましたが・・・
ようやく既存Win8.1端末への更新がやってきました。

Upgrade Advisorアプリをインストールして、あとはアプリの指示に従っていくと、Win10の更新が取得できるようになりました。 Upgrade Advisor – Microsoft ストアの Windows アプリ

f:id:minami_SC:20160318110618p:plain:w200

週末はこれで色々遊べそう!!
やったね♪

WPF用にLightBox的なポップアップ表示をするライブラリを作ってみた

$
0
0

こんな風に、LightBox的なダイアログ表示を行うライブラリを作ってみました。
f:id:minami_SC:20160403174651g:plain

概要

WPFLightBox的なダイアログ表示を行うライブラリです。
ダイアログは、別ウィンドウではなく、呼び出し元ウィンドウ上にAdornerとして重ねて表示します。

コードビハインドからMessageBoxを呼ぶのと同じ感覚で、イイ感じのLightBox表示ができます。

LightBox.Show(this, new SampleDialog());

ざっくり特徴をまとめると以下のような感じ。

  • WindowのXAMLコードには手をいれずに、元のウィンドウ上にLightBoxを重ねて表示できる。
  • モーダル/モードレスどちらの表示にも対応
    • Show/ShowDialog/ShowAsyncの3種類の呼び出し方法
  • LightBoxの中身には、任意のFrameworkElement派生クラスの要素を表示できる。
    • ImageコントロールのようなPrimitiveな型や、独自に作成したUserControlなどを表示可能。
  • アニメーション対応
    • LightBoxを表示するとき/閉じるときにアニメーションを付けることができます。
  • XAMLで各種デザインをカスタマイズ可能
  • 複数ダイアログの同時表示

コードは短めですが、割とカスタマイズ性の高いライブラリになっているのでは、と思います。

使い方

インストール

Nugetからの以下のコマンドでインストールできます。

Install-Package Lighty

また、GUIからでもLightyと検索すれば見つかります。
f:id:minami_SC:20160403174800p:plain

コイツをインストールすればOK!!

準備

lightyの機能を、c#コードから利用する場合には、以下の名前空間のusingを追加しておきます。

using SourceChord.Lighty;

また、xaml側でlightbox表示のカスタマイズをしたい場合には、このxml名前空間を使います。

xmlns:lighty="clr-namespace:SourceChord.Lighty;assembly=Lighty"

lightbox表示

lightyを使ってポップアップ表示を行うには、コードビハインドから以下のコードを呼び出します。
(SampleDialogは任意のユーザーコントロール)

LightBox.Show(this, new SampleDialog());

すると、このようにウィンドウ全体が暗くなって、ダイアログが表示されます。
f:id:minami_SC:20160403174813p:plain

第一引数には、LightBox表示を行う領域。
第二引数には、表示するコンテンツを指定します。
ウィンドウ全体に表示したい場合には、上記の例のようにコードビハインドから呼び出す際にthisを指定すればOKです。

モーダル/モードレス/Asyncな表示

ダイアログ表示用に、以下の3種類のメソッドを用意しています。

Method NameDescription
ShowLightBoxをモードレス表示します。
ShowDialogLightBoxをモーダル表示します。すべてのLightBoxを閉じるまで、後続のコードは実行しません。
ShowAsyncShow()メソッドのAsyncバージョン。asyncメソッド内でawaitと併用して使うことができます。

それぞれ、こんな風に呼び出すことができます。

private async void OnClickButton(object sender, RoutedEventArgs e)
        {
            // modeless dialog
            LightBox.Show(this, new SampleDialog());

            // async method
            await LightBox.ShowAsync(this, new SampleDialog());

            // modal dialog
            LightBox.ShowDialog(this, new SampleDialog());
        }
表示内容の指定

Showメソッドなどの第二引数には、FrameworkElement派生の任意のオブジェクトを指定できます。

usercontrolをコンテンツとして表示

LightBox.Show(this, new SampleDialog());

f:id:minami_SC:20160403174813p:plain:w250

任意のエレメントを表示

            var image = new Image();
            image.Source = new BitmapImage(new Uri("Images/1.jpg", UriKind.Relative));
            LightBox.Show(this, image);

f:id:minami_SC:20160403174837p:plain:w250

任意の領域への表示

Showメソッドなどの第一引数には、XAMLのロジカルツリー上の任意の要素を指定できます。
こんな風に、任意のgridなどを指定して、その要素上にLightBox表示ができます。

<Window x:Class="LightySample.Window1"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:lighty="clr-namespace:SourceChord.Lighty;assembly=Lighty"xmlns:local="clr-namespace:LightySample"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="Window1"Width="500"Height="300"mc:Ignorable="d"><Grid><Button x:Name="button"Width="75"Margin="10,10,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Click="button_Click"Content="Button" /><Grid x:Name="subGrid"Width="300"Height="200"Margin="10"HorizontalAlignment="Right"VerticalAlignment="Bottom" /></Grid></Window>
privatevoid button_Click(object sender, RoutedEventArgs e)
        {
            LightBox.Show(this.subGrid, new SampleDialog());
        }

f:id:minami_SC:20160403174900p:plain

ダイアログの閉じ方

ダイアログを閉じるには以下のような方法があります。

ApplicationCommandを利用
WPFの定義済みルーティングコマンドApplicationCommands.Closeを実行すると、そのコマンドを発行した要素を含むダイアログが閉じられます。

<Button Width="75"Margin="10"HorizontalAlignment="Center"VerticalAlignment="Bottom"Command="ApplicationCommands.Close"Content="Close" />

コードビハインドから閉じる
ユーザーコントロールを表示している場合、ボタンクリック時にダイアログを閉じたい、というような場合には、以下のようにCloseメソッドを呼び出します。 LightyでFrameworkElementにCloseという拡張メソッドを定義しているので、using SourceChord.Lighty;名前空間を設定していれば、Close()拡張メソッドでダイアログを閉じることができます。

privatevoid Button_Click(object sender, RoutedEventArgs e)
        {
            // Lightyで定義している、ダイアログクローズ用の拡張メソッド// ※「using SourceChord.Lighty;」が必要this.Close();
        }

背景領域のクリック
デフォルトの状態だと、LightBox表示の背景部分をクリックすると、すべてのダイアログ表示を閉じるようになっています。
(後述するCloseOnClickBackgroundプロパティでカスタマイズ可能です。)

MVVMとの連携

MVVM的にVMから表示する方法とかは?って話ですが、その手のMVVMとの連携は用意していません。
ダイアログ表示のような動作は完全にView側の責務だと思うので、VMからライトボックス表示をさせる方法を提供するのは、レイヤーが異なる話になるかな、と。

通常のWindowをShow/ShowDialogするのと同じ感じで表示できるので、MVVMな開発スタイルと併用したい場合は、自分が使ってるMVVMフレームワークの作法に合わせて、LightyのShow/ShowDialog/ShowAsyncメソッドを呼び出してもらえばよいかと思います。

応用編

表示のカスタマイズ

LightyではXAMLの機能をフルに使って、表示方法を自由にカスタマイズできます。

表示方法は、WindowのリソースとしてLightBoxクラスのデフォルトスタイルを設定することでカスタマイズできます。
(Windowのリソースで定義すれば、ウィンドウ単位でのスタイルの設定になり、App.xamlで定義すればアプリケーション全体の設定として定義することができます。)

PropertyDescription
TemplateLightBoxとして表示する領域全体の定義。LightBoxの背景部分などはここでカスタマイズします。
ItemsPanel各ダイアログを格納するItemsPanelを設定します。StackPanelなどを併用することで、複数ダイアログを並べて表示することもできます。
ItemContainerStyle各ダイアログを格納するコンテナのスタイルを設定します。ダイアログに共通のデザインを適用したい場合などは、ここで設定できます。
CloseOnClickBackgroundLightBoxの背景領域をクリックした際に、すべてのダイアログを閉じるか否かを設定します。(デフォルトはTrue)

こんな風にカスタマイズできます。

<Window.Resources><Style TargetType="{x:Type lighty:LightBox}"><Setter Property="Template"><Setter.Value><ControlTemplate><Grid Background="#88F5F5DC"><ItemsPresenter /></Grid></ControlTemplate></Setter.Value></Setter></style><Setter Property="CloseOnClickBackground"Value="False" /></Window.Resources>

f:id:minami_SC:20160403174920p:plain:w300

ビルトイン スタイル

いくつかのデザインやアニメーションを、ライブラリ側で標準で定義しています。
これらのビルトインスタイルを使えばお手軽に表示方法をカスタマイズできます。

準備
ビルトインスタイルを使うには、まずApp.xamlで以下のリソースディクショナリを読み込んでおきます。

<ResourceDictionary Source="pack://application:,,,/Lighty;component/Styles.xaml" />

するとLightBoxのデフォルトスタイルを設定する際に、StaticResourceでビルトインスタイルを指定できるようになります。

<Window.Resources><Style TargetType="{x:Type lighty:LightBox}"><Setter Property="Template"Value="{StaticResource DarkGlassTemplate}" /><Setter Property="ItemsPanel"Value="{StaticResource HorizontalPanel}" /><Setter Property="ItemContainerStyle"Value="{StaticResource PhotoCardStyle}" /></Style></Window.Resources>

詳細は割愛しますが、現時点では以下のようなスタイル類を定義しています。

PropertyName
TemplateDarkBackgroundTemplate
LightBackgroundTemplate
DarkGlassTemplate
LightGlassTemplate
ItemsPanelHorizontalPanel
VerticalPanel
ItemContainerStyleClosableContainerStyle
PhotoCardStyle
AnimationsFadeInAnimation
FadeOutAnimation
ZoomInAnimation
ZoomOutAnimation

※ビルトインスタイルは、今後もう少し手を入れたり追加したりしていこうかと思ってます。

個人的におすすめのスタイルは、「DarkGlassTemplate/LightGlassTemplate」
iOS風のポップアップ表示みたいに背景に強めのブラーをかけてからダイアログ表示をします。
f:id:minami_SC:20160403175016p:plain

以前↓で書いた方法を併用して、ウィンドウ全体に強烈なぼかしをかけても、パフォーマンスが落ちないように工夫してます。

アニメーション

LightBoxを表示したり閉じたりする際のアニメーションもカスタマイズ可能です。
以下のようなプロパティを用意しています。

PropertyDescription
InitializeStoryboardLightBoxの背景部分を最初に表示する際のアニメーション
DisposeStoryboardLightBoxの背景部分を消す際のアニメーション
OpenStoryboardLightBoxとして各ダイアログを表示する際のアニメーション
CloseStoryboardLightBoxとして各ダイアログを閉じる際のアニメーション

こんな風に設定をします。

<Window.Resources><Style TargetType="{x:Type lighty:LightBox}"><Setter Property="InitializeStoryboard"Value="{StaticResource ZoomInAnimation}" /><Setter Property="DisposeStoryboard"Value="{StaticResource ZoomOutAnimation}" /><Setter Property="OpenStoryboard"Value="{StaticResource ZoomInAnimation}" /><Setter Property="CloseStoryboard"Value="{StaticResource ZoomOutAnimation}" /></Style></Window.Resources>
アニメーションの並列実行

デフォルトでは以下のアニメーションは、シーケンシャルに順次実行します。

  • InitializeStoryboard⇒OpenStoryboard
  • CloseStoryboard⇒DisposeStoryboard

(背景を暗くして、そのアニメーションが終わったら、各ダイアログを表示するためのアニメーションを開始する、、など。)

これらを並列で同時に実行したい場合には以下のプロパティを設定します。

PropertyDescription
IsParallelInitializeInitializeStoryboard & OpenStoryboardを並列に実行するか否かを指定します。(デフォルトはFalse)
IsParallelDisposeCloseStoryboard & DisposeStoryboardを並列に実行するか否かを指定します。(デフォルトはFalse)

複数ダイアログの同時表示

Lightyでは、複数のダイアログを同時表示することもできます。

以下のようにItemsPanelをStackPanelなどにしておき、Showメソッド複数回呼び出すと複数ダイアログの表示をすることができます。

<Window.Resources><Style TargetType="{x:Type lighty:LightBox}"><Setter Property="ItemsPanel"Value="{StaticResource HorizontalPanel}" /></Style></Window.Resources>
private async void button_Click(object sender, RoutedEventArgs e)
        {
            LightBox.Show(this, new SampleDialog());

            await Task.Delay(1000);
            LightBox.Show(this, new SampleDialog());

            await Task.Delay(1000);
            LightBox.Show(this, new SampleDialog());
        }

f:id:minami_SC:20160403175049p:plain:w300

その他

MahApps.Metroなどのライブラリと組み合わせて使うこともできます。
↓こんな感じ。
f:id:minami_SC:20160403175108p:plain:w300

こういうライブラリと組み合わせることで、お手軽にかっこいいUIを作れるのでは、と思います。

VSCode1.0/1.1の新機能・変更点

$
0
0

ここのところ、ちょっと立て込んでてあまりVSCodeいじったりできなかったのですが、この間にVSCodeは1.0/1.1とリリースされました。 ということで、VSCode1.0/1.1の新機能や変更点で、個人的にコレは!!と思った点をまとめておこうと思います。

ローカライズ関連

VSCodeのUI全般がローカライズされて、OSの表示言語などに応じて表示言語が切り替わるようになりました。
V1.0では、UIで中華フォント表示される部分があったり、コマンドパレットの文字列までローカライズされ、コマンドパレットが実質的に使い物にならなくなる、、、など、散々な状況でしたが、この辺はV1.1でかなり改善されました。

V1.0の画面V1.1の画面
f:id:minami_SC:20160515235213p:plainf:id:minami_SC:20160515235222p:plain

また、コマンドパレットも、日本語/英語どちらでも検索できるようになっています。
f:id:minami_SC:20160515235411p:plain
コマンドパレットに表示される項目は、英語の文字列とローカライズされた文字列が並んで表示されます。

言語切替

デフォルトの状態では、VSCodeはOSのシステム設定の言語で表示をします。
別の言語を指定して起動したい場合には、以下のどちらかで。

  • コマンドラインから

    • 以下のようにコマンドライン引数に、--locale=[ロケール指定]とすると、指定した言語での表示に切り替わります。 f:id:minami_SC:20160515235432p:plain
      f:id:minami_SC:20160515235438p:plain
  • 設定ファイル

    • locale.jsonというファイルで、言語指定することもできます。
    • Configure Languageというコマンドでこのファイルを開くことができます。

f:id:minami_SC:20160515235615p:plain

V1.0での変更点

Git連携機能

f:id:minami_SC:20160515235631p:plain
VSCode起動時に、システムにインストール済みのGitのバージョンが確認されるようになりました。 VSCodeでは2.0.0以降のバージョンが必要という旨の警告表示がされます。 VSCodeのgit連携機能を色々使うなら、システムにインストールしているGitも2.x系にバージョンアップしたほうがよさそうです。

Windows環境でGitのインストールをするには、以下の記事がとても参考になります。
https://opcdiary.net/?page_id=27065

言語関係

JavaScript

ドキュメントにjsconfig.jsonに関する説明など色々付け加えたとのこと。
https://code.visualstudio.com/docs/languages/javascript

自分は、JavaScriptはあまり直接使わないので、このへんはスキップ。
詳細はこちら↓
https://code.visualstudio.com/updates#_languages-javascript

エディタ関連

ファイルの関連付け

ファイルの拡張子ごとに、どのプログラミング言語として扱うかの関連付けを指定できるようになりました。

settings.jsonfiles.associationsという項目で、関連付けの設定をします。

"files.associations": {"*.ejs": "html"}

以下のように、フォルダ指定で関連付けをすることもできます。

"files.associations": {"**/views/*.ejs": "html"}
矩形選択

Alt+Shiftを押しながらマウスドラッグで、矩形選択ができるようになりました。
本家VSはAlt+ドラッグなので、微妙にショートカットが違うのが気になる・・・けど、まぁ便利です。

コード折り畳みの新ショートカット

Ctrl+K Ctrl+1, Ctrl+K Ctrl+2・・・Ctrl+K Ctrl+5みたいなショートかっとで、1段2段・・・と折り畳みを行う階層の深さを指定して、折り畳みを行えるようになりました。

スペース/タブ表示の切り替え

今までもsetting.jsonで切替できましたが、お手軽に切替できるようになりました。
コマンドパレットから、Toggle Render Whitespaceというコマンドで切替できます。
f:id:minami_SC:20160515235720p:plain
デフォルトでは、ショートカットは割り当たっていないので、ショートカットで切り替えたい場合は、自分で設定しておく必要があります。

変更監視対象ファイルの除外指定

settings.jsonで、files.watcherExcludeという項目を設定することで、VSCodeによるファイルの変更監視対象から除外することがで着るようになりました。

VSCodeのデフォルトでは、以下のフォルダの除外設定がされているようです。

"files.watcherExclude": {"**/.git/objects/**": true,
    "**/node_modules/**": true}

ですが、この設定はあまり積極的に利用するのは推奨されてないようです。
VSCodeでフォルダを開いたときに、あまりにも大きなCPU負荷がかかっている、というようなときに、最後の手段として使うため、、ってイメージでしょうか。

デバッガ

Runアクション

今までは、VSCodeから何か実行するには、デバッガでの実行しかありませんでした。

V1.0からはCtrl+F5を押すと、デバッガは使わずにVSCodeのコンソールを経由して実行できます。
これは便利!!
f:id:minami_SC:20160515235738p:plain

V1.1での変更点

V1.1での変更点も、いくつかかいつまんで見てみます。

エディタ

  • エディタのタブ領域のボーダーや、エクスプローラとエディタの境界部分などをダブルクリックすると、各領域をリサイズするようになりました。
  • 閉じたファイルを再度開くコマンド
    • Ctrl+Shift+Tで、閉じたファイルを再度開くようになりました。
  • Emmet記法のタブキーでの補間の無効化

タブのデザイン案

これは変更点じゃないですが、今後の気になる話題なので。。。
現在のVSCodeは、こんな風に横方向に分割して複数テキストエディタ領域を表示できるようになっています。
ですが、一般的なタブエディタみたいな操作性のタブを導入するため、どんなデザインにするか、という議論がされていたようです。
https://github.com/Microsoft/vscode/issues/224#issuecomment-213015687
で、デザインの方向性が決まったようで、このタブ機能の実装を始めるみたいです。
これは今後の期待の新機能になりそうですねw

その他

コンソールを開くツールの選択

いままで、Windows環境では、Ctrl+Shift+Cのショートカットで、コンソールを開くことができましたが、この時に開かれるのは、コマンドプロンプト固定でした。
V1.1からは、settings.jsonで、以下の設定を変えることで、この時に開くツールを別のものにすることができます。

"externalTerminal.windowsExec": "powershell"

こんな風にすると、コマンドプロンプトではなく、PowerShellを開くことができたりします。

他にも、細かい修正点などはありますが、個人的に目ぼしい変更点は、こんなところかな、と。

typings1.0を使ってみた

$
0
0

もうずいぶんと前ですが、
TypeScriptの型定義ファイルの取得は、tsdが非推奨になり今後はtypingsというライブラリが推奨されるようになりました。

そして、このtypingsの1.0がでました。
今までなんだかんだで、tsdを使い続けてましたが、重い腰を上げて、typingsを使うように移行してみようと思います。

コマンド体系が、npmと似た感じなので、tsdよりも馴染みやすいコマンドとなってます。
ただし、typingsでは型定義ファイルの提供元が複数リポジトリから構成されています。
自分が取得しようとしているライブラリの種類によって、インストールのコマンドを使い分ける必要があるので要注意。

参考サイト

とりあえず、↓のドキュメント読めばだいたい何とかなりそう。

typings/docs at master · typings/typings · GitHub

あと、typings 1.0からは、コマンドなどが少し変わってるので注意が必要です。

1.0からの変更点などは、以下の記事が参考になります。
Typings 1.0.x 変更点 - Qiita
typings を 0.x から 1.x にアップデートしたときにハマったこと - Qiita

基本的な使い方

インストールは以下のコマンドで。

npm install typings -g

この手のツールは、頻繁に使うからグローバルにインストールしておいた方が便利な気がします。

typings.jsonの生成

npmやtsdなんかと同じように、typingsでも依存関係を記述したjsonファイルに記録しておくことができるようになってます。

typings.jsonの生成は、以下のコマンドでOK。

typings init
tsdからのアップグレード

tsd.jsonがある階層で以下のコマンドを打つと、tsdで使用していた型定義ファイルへの依存関係を引き継いだtypings.jsonを生成できます。

typings init --upgrade

型定義の検索

typingsでは、以下のような感じで、型定義ファイルの検索ができます。

typings search [パッケージ名]
厳密な検索

以下のように--nameオプションを伴って検索をすると、ライブラリ名の部分一致ではなく、完全に名前が一致したものだけが表示されます。

typings search --name [パッケージ名]

で、こんな風に同じパッケージ名でも複数のものが出てきたりします。
f:id:minami_SC:20160522111935p:plain

typingsでは、同じライブラリでも複数の型定義ファイルのソースがあるため、このように複数のソースの情報が出てくるものもあります。

型定義のインストール/アンインストール

例えば、Definitely Typedのリポジトリから、nodeの型定義ファイルを取得するのは、↓のコマンド。

typings install dt~node --global --save

dt~などのような部分が、型定義ファイルの取得元の指定です。
dt~では、今までtsdで使ってたDefinitely Typedのリポジトリにある型定義ファイルを取得します。

アンインストールは↓で。

typings uninstall node --global  --save

typings.jsonの記述も消したい場合は、インストール時と同じように、--save, --save-devなどの指定をします。

アンインストールするときはdt~のような、取得元を指定するような記述は不要です。
また--globalでインストールしたものは、アンインストール時も同じように--global指定が必要なので注意。

バージョン指定でインストール

こんな風にライブラリのバージョン指定をして、型定義ファイルの取得をすることができます。
こうすると、node.js 0.12系の型定義ファイルを取得できます。

typings install dt~node@0.12.0 --global

これは便利!!


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

$
0
0

今月も月例のアップデートがリリースされました。
https://code.visualstudio.com/updates#vscode
個人的には、vscode内にターミナルが統合されたのがうれしい!!

今回も、個人的に目ぼしい変更点をピックアップしてまとめておこうと思います。

タブの実装状況

これは、今回のアップデートの内容ではありませんが、、、
タブの実装についての、報告がありました。
https://code.visualstudio.com/updates#_workbench

来月のアップデートで、この辺の機能を少しずつ取り込み始めるみたいです。
メッチャ楽しみですね♪

エディタ

テキスト検索からのマルチカーソルの一括選択

テキスト検索時にAlt+Enterで、検索で一致している箇所をマルチカーソルで一気に選択してくれるようになりました。

この状態でAlt+Enterを押すと、
f:id:minami_SC:20160610003639p:plain

このようにすべての一致箇所が、マルチカーソルで選択された状態になります。
f:id:minami_SC:20160610003649p:plain

地味に便利系な機能ですね。

その他

他にも以下のような更新がありました。

ターミナルの統合

日本語キーボード環境だとCtrl+@で。
こんな風に、ウィンドウ下部にコンソールが出てきます。
f:id:minami_SC:20160610003721p:plain

現状ではこのターミナル上ではテキストのコピペなどの操作はできません。この辺は今後のアップデートで対応するみたいです。

コマンドでの拡張機能インストール

VSCodeの拡張機能インストールなどの操作を、コマンドラインから行えるようになりました。

よく使う拡張機能をインストールするコマンド一式を、バッチファイルとかシェルスクリプトで用意しておけば

  • VSCodeの再インストール時に、自分の普段使う開発環境を一気にインストール

などなど、色々と使い道があるのでは、と思います。

以下のようなコマンドが追加されています。

インストール済みの拡張機能の列挙
code --list-extensions
拡張機能のインストール
code --install-extension 拡張機能名
拡張機能のアンインストール
code --uninstall-extension 拡張機能名

WPFでの入力値検証・その9 ~エラー表示のローカライズ~

$
0
0

ずいぶん久しぶりだけど、WPFの入力値検証ネタ。

以前↓みたいなのをやりましたが、 このDataAnnotationsを使ったバリデーションを行う際に、エラーメッセージのローカライズをしてみました。

アプリのローカライズ

まずは、アプリ自体のローカライズ対応をします。
WPFアプリのローカライズ手順などは、以下の記事を参考に、resxファイルを各言語ごとに用意しておきます。
(今回は、動的な言語切替などはしないので、その辺は省略)

ここでは、デフォルトの英語表示用と、日本語表示用のja-JPのリソースだけこんな風に作成しました。
f:id:minami_SC:20160612144943p:plain

それぞれ、以下のような内容を設定します。
f:id:minami_SC:20160612144951p:plain
f:id:minami_SC:20160612144958p:plain

以前のメッセージ設定方法

以前のサンプルでは、↓のようにErrorMessageプロパティを使って、メッセージを直接設定していました。

privatestring inputString;
        [Required(ErrorMessage = "何か入力してください")]
        [StringLength(10, ErrorMessage = "10文字以内で入力してください")]
        [RegularExpression("[a-z]+", ErrorMessage = "a-zの文字列を入力してください。")]
        publicstring InputString
        {
            get { return inputString; }
            set { this.SetProperty(refthis.inputString, value); }
        }

ここに、先ほど作成したローカライズ対応のリソースの内容を設定してみます。

ローカライズに対応したメッセージ設定方法

resxファイルで定義したリソースを使うために、ErrorMessageではなく、
ErrorMessageResourceNameErrorMessageResourceTypeプロパティでエラー時のメッセージ設定を行います。

    class MainWindowViewModel : ValidateableBase
    {
        private string inputString;
        [Required(                   ErrorMessageResourceName = "EmptyMessage",           ErrorMessageResourceType = typeof(Properties.Resources))]
        [StringLength(10,            ErrorMessageResourceName = "LengthOverMessage",      ErrorMessageResourceType = typeof(Properties.Resources))]
        [RegularExpression("[a-z]+", ErrorMessageResourceName = "TextTypeInvalidMessage", ErrorMessageResourceType = typeof(Properties.Resources))]
        public string InputString
        {
            get { return inputString; }
            set { this.SetProperty(ref this.inputString, value); }
        }

        public MainWindowViewModel()
        {
            this.InputString = string.Empty;
        }
    }

これだけでOK!!

日本語環境では今まで通り、以下のような表示になります。
f:id:minami_SC:20160612145008p:plain

別言語での表示確認

これで、OSの表示言語に合わせてローカライズされた文字列が表示されるようになります。
わざわざOSの表示言語切り替えるのメンドイ・・・、って時には、Appクラスのコンストラクタなどで、以下のようにリソースの言語設定を切り替えるコードを追加して確認できます。

public App()
        {
            WpfApplication1.Properties.Resources.Culture = CultureInfo.GetCultureInfo("en-US");
        }

f:id:minami_SC:20160612145017p:plain

ただし、この方法で切り替えたときは、テキストのフォントなどまでは切り替わらないので、厳密には別言語OSの環境での表示とは若干異なったものとなるので注意。
ちゃんと別言語OSの環境と同じ表示にしたいときは、OSの設定で表示言語を切り替えましょう。

WPFでの入力値検証・まとめ

$
0
0

今まで何回かに分けて書いてきたWPFのバリデーション関係ネタについて、自分でもわりと見返すので、この入力値検証関連の記事をまとめておきます。

あと、各種バリデーション方法のサンプルコード一式を、以下のリポジトリに上げておきました。

色々書いたブログの記事とは微妙にリンクしていないサンプルですが・・・
まぁ、この手のバリデーション処理を実装するときには、少しは役に立つのでは、、、と思います。

サンプルの説明

  • Example1_WithException・・・例外を使った方法
  • Example2_ValidationRule・・・ValidationRuleを使った方法
  • Example2_1_ErrorTemplate・・・ErrorTemplateでエラー時の表示内容をカスタマイズ
  • Example3_IDataErrorInfo・・・IDataErrorInfoを使った方法
  • Example_INotifyDataErrorInfoフォルダ
    • Example4_INotifyDataErrorInfo・・・INotifyDataErrorInfoの基礎
    • Example5_DataAnnotations・・・・DataAnnotationsを使って、属性でバリデーション内容を指定
    • Example6_CustomValidation・・・・独自バリデーション条件の指定方法
    • Example7_MultiplePropValidation・・・複数のプロパティが絡むバリデーション
    • Example8_Localize・・・・・・・・エラー時の表示メッセージのローカライズ

関連リンク

WPFでの入力値検証 - SourceChord
WPFでの入力値検証・その2 ~INotifyDataErrorInfoを使ってみる~ - SourceChord
WPFでの入力値検証・その3 ~DataAnnotationsを使う~ - SourceChord
WPFでの入力値検証・その4 ~ErrorTemplateで検証結果の表示をカスタマイズ~ - SourceChord
WPFでの入力値検証・その5 ~INotifyDataErrorInfo実装のベースクラスを作成~ - SourceChord
WPFでの入力値検証・その6 ~複数のエラー表示への対応~ - SourceChord
WPFでの入力値検証・その7 ~独自の検証ロジックを作成する~ - SourceChord
WPFでの入力値検証・その8 ~複数のプロパティが絡むバリデーションを作成する~ - SourceChord
WPFでの入力値検証・その9 ~エラー表示のローカライズ~ - SourceChord

あと、関連してPrismのErrosContainerについて↓
Prism.Mvvmを使ってみる・その2 ~ErrorsContainerの使い方~ - SourceChord

各種バリデーション方法の使い分け

色々な方法を書いたけど、WPFでバリデーションをする場合は、基本的に以下2択のどっちかになるのでは、、、と思います。

  • ValidationRuleを使って、View側でバリデーション
  • INotifyDataErrorInfoを使って、VM側でバリデーション

ValidationRuleとINotifyDataErrorInfoの使い分け

ValidationRuleとINotifyDataErrorInfoの動作は、以下のような違いがあります。
それぞれ、適材適所で使っていくのがいいのかな、と思います。

自分は基本的にはINotifyDataErrorInfoを使うかなぁ、、って印象。

ValidationRuleの動作

ValidationRuleを使う場合は、現在UI上に入力されている値がVMに反映されるとは限りません。
⇒バリデーションした結果、値が有効だった場合だけ、バインドしているVMのプロパティに反映されます。

完全にView側で閉じたバリデーションをしておき、VMには常に有効な値しか入らない、という動作にしたい場合に向いています。

ValidationRuleを使った方法は、VM側では処理を行わずにView側でデータを加工するようなイメージです。
ちょうどValueConverterと似たような立ち位置でしょうか。

INotifyDataErrorInfoの動作

一方、INotifyDataErrorInfoを使った場合は、UIからバインドした値がバリデーションで無効な値、と判断される場合もその値がVM側プロパティにセットされます。(IDataErrorInfoも同様)
そのため、UIに入力された値をバリデーション処理で判定した結果、エラーが起きているかどうかもVM側からチェックできるようになります。

バリデーション処理自体をVM単体テストとして行いたいような場合には、こちらの方が向いているかと思います。

また、複数のプロパティが絡むような複雑なバリデーションも、こちらの方法の方が向くと思います。

WPF/UWP用にBootstrap風Gridレイアウトを行うライブラリを作ってみた~ResponsiveGrid~

$
0
0

超定番cssフレームワークBootstrapのグリッドシステムのようなレイアウトをXAML環境で行うためのライブラリを作ってみました。
f:id:minami_SC:20160702010414g:plain

WPF/UWPともに、Nugetから以下のパッケージをインストールすることで使えます。
NuGet Gallery | ResponsiveGrid 0.3.0
(WPF/UWPともに共通のパッケージとなっています。)

NugetのGUIResponsiveGridで検索すれば出てきます。
f:id:minami_SC:20160702010503p:plain
Nugetのパッケージマネージャ コンソールから、以下のコマンドでもインストールできます。

Install-Package ResponsiveGrid

最初はWPF向けに作り始めたけど、こういうレスポンシブなレイアウトは、デスクトップとモバイルを共通で開発できるUWPのような環境の方が需要ありそうなんで、WPFとUWP両対応なライブラリにしてみました。

使い方

準備

WPF/UWPでXAMLで使用する名前空間の設定方法が若干異なります。
とりあえず、以下のようなxmlnsの設定をすれば準備OK。

WPF

xmlns:rg="clr-namespace:SourceChord.ResponsiveGrid;assembly=ResponsiveGrid.Wpf"

UWP用

xmlns:rg="using:SourceChord.ResponsiveGrid"

レイアウトしてみる

とりあえず、Webデザインでよくありそうな、ヘッダー/フッターと、ナビゲーション領域、コンテンツ領域からなるレイアウトをしてみます。

XAMLでこんな風にレイアウトすると、、

<Window x:Class="ResponsiveGridSample.Wpf.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:ResponsiveGridSample.Wpf"xmlns:rg="clr-namespace:SourceChord.ResponsiveGrid;assembly=ResponsiveGrid.Wpf"mc:Ignorable="d"Title="MainWindow"Height="350"Width="525"><Grid><Grid.Resources><Style TargetType="{x:Type Border}"><Setter Property="BorderBrush"Value="Black" /><Setter Property="BorderThickness"Value="1.5" /><Setter Property="Background"Value="LightGray" /><Setter Property="CornerRadius"Value="5" /><Setter Property="Margin"Value="1" /><Setter Property="Height"Value="60" /></Style></Grid.Resources><rg:ResponsiveGrid Margin="10"BreakPoints="345, 567, 789"><Border><TextBlock Text="[Header]"/></Border><Border Height="80"rg:ResponsiveGrid.SM="3"><TextBlock Text="[Navigation]&#xa;-Top&#xa;-Menu1&#xa;-Menu2"/></Border><Border Height="80"rg:ResponsiveGrid.SM="9"><TextBlock Text="[Content]"/></Border><Border><TextBlock Text="[Footer]"/></Border></rg:ResponsiveGrid></Grid></Window>

ある程度以上のウィンドウ幅がある場合はこんな感じ。
f:id:minami_SC:20160702011249p:plain

ウィンドウの幅を縮めていくと、こんな風に表示が切り替わります。
f:id:minami_SC:20160702011302p:plain

プロパティ

プロパティ名Type内容
MaxDivisionint1行当たりのカラム分割数
BreakPointsBreakPoints classXS, SM, MD, LGのレイアウト切替を行うブレークポイントの設定
ShowGridLinesintカラム分割位置のガイド表示設定
GridコントロールのShowGridLinesと似た感じ。
MaxDivision

このプロパティで、1行のカラム数を設定できます。

<rg:ResponsiveGrid MaxDivision="24">
BreakPoints

こんな風に、XS, SM, MD, LGのレイアウト変更を行う閾値のサイズを設定できます。

<rg:ResponsiveGrid><rg:ResponsiveGrid.BreakPoints><rg:BreakPoints XS_SM="345"SM_MD="567"MD_LG="789"/></rg:ResponsiveGrid.BreakPoints>

WPFでは、こんな風に簡単に設定することもできます。

<rg:ResponsiveGrid BreakPoints="345, 567, 789">

(UWPではTypeConverterを自作できないんで、こういうことができません。。。)

ShowGridLines

このプロパティをTrueにすると、Gridコントロールなどと同じようにグリッド位置のガイドライン表示を行います。
ResponsiveGridでは、縦方向のカラム分割位置のみ点線表示を行います。

<rg:ResponsiveGrid ShowGridLines="True"><Border rg:ResponsiveGrid.XS="4" /><Border rg:ResponsiveGrid.XS="4" /></rg:ResponsiveGrid>

f:id:minami_SC:20160702010525p:plain

添付プロパティ

最初のサンプルで使用した、XS, SMなどのプロパティ以外にも、以下のようなプロパティを用意しています。
Bootstrap使ったことある人なら、なんとなく感覚で使えるのではないでしょうか。

プロパティ名Type内容
XS
SM
MD
LG
intXS, SM, MD, LGのそれぞれのカラム数
XS_Offset
SM_Offset
MD_Offset
LG_Offset
intオフセット指定。ここで指定した数だけ、自身より前に余白を作ります。
XS_Push
SM_Push
MD_Push
LG_Push
intここで指定したカラム数分だけ、本来の位置より右側に移動します。
XS_Pull
SM_Pull
MD_Pull
LG_Pull
intここで指定したカラム数分だけ、本来の位置より左側に移動します。

工夫した点など

配布形態

WPFとUWPに両対応するため、共有プロジェクトを作ってResponsiveGridクラスの実装コードを共有しています。
プロジェクト構成としてこんな感じ。
f:id:minami_SC:20160702010534p:plain

ResponsiveGrid.Sharedが共有プロジェクトで、コードの大部分はこの中でifdefで処理を分けながら書いてます。
で、ResponsiveGrid.WpfとResponsiveGrid.Uwpが、それぞれWPF用/UWP用のクラスライブラリプロジェクトで、ResponsiveGrid.Sharedの共有プロジェクトを参照しています。

WPFとUWPでは、XAMLの仕様で違う点が色々あったりしますが、
今回はPanel派生クラスをC#コードで書くだけだったので、名前空間の設定やプロパティ定義などを部分的にifdefで切り替えることで対応できました。

TODO

余力のある時に↓みたいなことをしようと思ってます。

  • もうちょっとサンプルコード類を充実させたい
  • Visibility関係のプロパティ類を作成(MDの時だけ表示/非表示みたいな設定とか)

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

$
0
0

毎月の月例アップデートですが、VSCode1.3がリリースされたので、今回も個人的にコレは!!と思った新機能や変更点をまとめておきます。
code.visualstudio.com

VSCodeも1.0リリース移行は色々な変更は落ち着いてくるのかな、と思ってたのですが、そんなことは全然なく、今回もたくさんの便利な機能がアグレッシブに追加されてきてます。
イイ感じですね!!

タブ

とうとう来ました!!
ユーザーからの要望No.1のヤツ♪

こんな風に、よく見るタイプのタブUIが実装されてます。 f:id:minami_SC:20160709154116p:plain

また、今までの分割表示が無くなるわけではなく、このように各分割されたエリア内にそれぞれタブが配置される感じとなっています。
f:id:minami_SC:20160709154128p:plain

一応、settings.jsonで以下のプロパティをfalseにすると、タブ機能を無効にできます。

    // Controls if opened editors should show in tabs or not.
    "workbench.editor.showTabs": false,

エディタ関連

先ほどのタブの実装もそうですが、今回のバージョンアップではエディタ関係のUIにたくさんの変更点があります。

OpenEdirors

今まで作業ファイル(Working files)と表示されてた部分が、OpenEditorsという名前に変わりました。
f:id:minami_SC:20160709154138p:plain

また、Ctrl+Tabなどでのファイル切替の動作も少し変更されています。
いままでは、過去に開いたファイルが、Ctrl+Tabで切替の対象ファイルとなっていましたが、今回のバージョンからはOpenEditorsに表示されているファイルだけが対象になっています。

このへんのUIは、だいぶわかりにくくて、混乱の元になってたので、かなりスッキリしたんじゃないかな。

Extensions View

拡張機能の検索やインストール、アンインストールなどの管理を行うためのUIが追加されました。
f:id:minami_SC:20160709154148p:plain

今までの、コマンドパレットに無理やり突っ込んだ感のあるUIよりも直感的で使いやすく、またインストール数とかレビュー評価の情報なども見えて、拡張機能を探しやすくなりました。

ドラッグ&ドロップでのタブ操作

ドラッグ&ドロップでタブを移動したり、エクスプローラなどからファイルをエディタにドロップして開いたりできるようになりました。

Preview Editors

「プレビュー表示」という状態が、タブのファイル名表示部分でわかるようになりました。
VSCodeのエクスプローラで、ファイルをシングルクリックしたときなどは、プレビュー表示としてファイルが開かれます。
別のファイルをプレビュー表示すると、以前のプレビュー表示タブで新しい内容が表示されます。こうすることで、あちこちのファイルの内容を確認してるときに、タブが大量に開かれることを防げるようなUIデザインになってます。

このプレビュー表示のタブは、以下の画像のように、ファイル名が斜体で表示されます。
f:id:minami_SC:20160709154206p:plain

プレビューで開かれたファイルに対して、以下のような操作をすると、プレビュー表示ではなく、普通に開かれた状態となり、Open Editorsの方に列挙されるようになります。

  • プレビュー表示のファイル内容を編集した場合
  • エクスプローラでファイルをダブルクリックしたとき
  • プレビュー表示のタブをドラッグ&ドロップで、どこか別の場所に移動したとき

文章だとよくわからないかもしれませんが、使ってみると結構しっくりくるんじゃないかな、と思います。

Problems Panel

Ctrl+Shift+Mで編集中のファイルやフォルダの問題点をリストアップする機能がありました。
今までは、このリスト表示はコマンドパレットの部分に表示されていました。
今回のバージョンアップで、以下のようなProblems Panelという専用の表示領域ができました。
f:id:minami_SC:20160709154231p:plain

ターミナル

VSCode統合のターミナル機能が色々改善されました。
f:id:minami_SC:20160709154243p:plain

主な変更点は以下のような感じ

  • 複数のターミナルを同時に起動できるようになった
    • コンボボックスから、ターミナルの切り替えができます。
  • 色々不具合修正
    • 日本語などの多バイト文字がうまく表示されない不具合が直った
    • コピー&ペーストなどのキーボードショートカットが効くようになりました
      • ただし、Ctrl+C, Ctrl+Vではなく、Ctrl+Insert, Shift+Insertというショートカットだけ対応しているようです。

f:id:minami_SC:20160709154251p:plain

Open Recent in new Window

コマンドパレットから最近開いたファイル/フォルダを開く際、Ctrlを押しながら項目を選択すると、新しいウィンドウで開くようになりました。

地味に便利系の機能です。

Global Search and Replace

今まで、開いてるフォルダ内の全検索はできたけど、置換はできませんでした。
(置換はファイル内のみの置換でした)

今回のアップデートで、開いてるフォルダ内のすべてのファイルに対して一括置換ができるようになりました。
f:id:minami_SC:20160709154303p:plain

マウスホイールでの拡大/縮小

Ctrlを押しながらマウスホイールの回転で、エディタの拡大/縮小ができるようになりました。

インデント・ガイド

settings.jsonに以下のような設定を加えると

    // Controls whether the editor should render indent guides
    "editor.renderIndentGuides": true

↓のような、インデントの深さを示すガイドライン表示ができるようになりました。
f:id:minami_SC:20160709154315p:plain

複数行の検索

f:id:minami_SC:20160709154323p:plain
ってか、今までできなかったんだ・・・知らんかった。

デバッガ

デバッガも色々と改善されてます。
とりあえず、目ぼしい点を箇条書き。

  • デバッガのツールバーが動かせるようになった
  • デバッガのUI上から、変数の値を変更できるようになった
  • ホバー時に、変数の型が表示されるようになった
  • Step Back
    • StepBackに対応したデバッガを使ってる場合は、このボタンがデバッガのツールバーに表示されるみたい。
    • Node.jsのデバッグ時には表示されないようです。
  • launch.json
    • OSごとの設定ができるようになった。
    • ↓のようにOS名のプロパティを書くことで、OSごとに別々の設定ができるようになりました。
{
   "type": "node",
   "request": "launch",
   "runtimeExecutable": "mynode",
   "windows": {
     "runtimeExecutable": "mynode.exe"
   }
}
  • Node.jsのデバッグ
    • すでに起動しているプロセスにアタッチできるようになった
    • launch.jsonに、以下のような設定を書いておくと、デバッガ起動時にプロセスを指定してアタッチすることができます。
        {
            "name": "アタッチ",
            "type": "node",
            "request": "attach",
            "address": "localhost",
            "restart": false,
            "sourceMaps": true,
            "outDir": null,
            "processId": "${command.PickProcess}"
        }

f:id:minami_SC:20160709154342p:plain

Monaco Editor

VSCodeのエディタ部分の核となっているMonaco Editorが、npmのパッケージとして公開されました。
これを使って、webアプリにリッチなテキストエディタ機能を持たせることができます。

https://www.npmjs.com/package/monaco-editor-core

また近いうちに、このパッケージ使って遊んでみようかな、と思います。

Viewing all 153 articles
Browse latest View live