Quantcast
Channel: かずきのBlog@hatena
Viewing all 1388 articles
Browse latest View live

Prism for Windows Runtimeを使ったODataの更新アプリサンプル


ReactivePropertyのNotifier系クラスのサンプルを書きました

ReactivePropertyのEventToReactiveのサンプル

ReactivePropertyとPrismでOData読み込み・更新のサンプル

Windows FormsでもReactiveProperty(同じ要領でXamarinでも使えます)

FlyoutをMVVMのViewModelから制御したい

SettingsFlyoutをViewModelから制御したい

ReactiveProperty v1.1リリースしました

$
0
0

今回はちょっと変更してます。

v1.0.xで追加した以下のプロパティを消しました。

  • ObserveErrors
  • ObserveHasNoError

かわりに以下の変更・追加が行われています。

ObserveErrorChangedプロパティの動作変更

Subscribeしたときに、現在のエラー情報を発行するようにしました。これまではSubscribeしても何か変化があるまで値を発行しませんでした。

過去と同じ動きにしたい場合は以下のようにSkip(1)をしてください。

rp.ObserveErrorChanged.Skip(1)

CombineLatestValuesAreAllFalseメソッドの追加

ObserveHasNoErrorが消えたかわりにObserveNoErrorから簡単にReactiveCommandが作れるようにCombineLatestValuesAreAllFalseメソッドを追加しました。(今まではAreAllTrueしかなかった)

以下のような感じで使えます。

// rp1, rp2, rp3全てエラーがなかったら押せるコマンド
ReactiveCommand command = new[]
  {
    rp1.ObserveHasError,
    rp2.ObserveHasError,
    rp3.ObserveHasError
  }
  .CombineLatestValuesAreAllFalse()
  .ToReactiveCommand();

インストール

NuGetから行ってください。

PM > Install-Package ReactiveProperty

Prism for Windows RuntimeでFileOpenPickerを出す方法

ユニバーサルWindowsアプリでPrismとReactivePropetyを使った状態で中断処理へ対応する

AppBarがあることを教えてくれるバーの作り方

ReactivePropertyを持つVMのコレクションをWindowsFormsのDataGridViewに表示する

$
0
0

遊びです。本気ではないので気を付けてください。

xin9leさんとチャットしながら作っててこんな拡張メソッドを用意しておけば。

publicstaticclass DataGridViewBindingExtensions
{
    publicstaticvoid BindTo<TCollectionItem>(
        this IEnumerable<TCollectionItem> self, 
        DataGridView dgv,
        params Func<TCollectionItem, DataGridViewCell>[] createCells)
    {
        foreach (var item in self)
        {
            var row = new DataGridViewRow();
            row.Cells.AddRange(createCells.Select(x => x(item)).ToArray());
            dgv.Rows.Add(row);
        }

        dgv.CellEndEdit += (_, e) =>
            {
                var cell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
                IReactiveProperty rp = (IReactiveProperty)cell.Tag;
                rp.Value = cell.Value;
            };
    }


    publicstatic TCell ToCell<TCell>(this IReactiveProperty self)
        where TCell : DataGridViewCell, new()
    {
        var cell = new TCell();
        cell.Value = self.Value;
        cell.Tag = self;
        return cell;
    }
}

こんな感じにDataGridViewに表示するコードが書けることがわかった。

var vm = new Form1ViweModel();
vm.People.BindTo(
    this.dataGridView1,
    x => x.Name.ToCell<DataGridViewTextBoxCell>(),
    x => x.Age.ToCell<DataGridViewTextBoxCell>());

DataGridViewには、あらかじめCellに対応するColumnを定義しておくこと。

本当に使えるかは置いておいて、こういうのを考えるのは楽しい。

SQLiteで最後に挿入したときに採番されたIDの値を取得する

$
0
0

auto incrementに設定してるIDの値をとりたい!ってことは往々にしてありますよね。そういうときは、こんなSQL発行すればいいみたいです。

select last_insert_rowid()

ReactivePropertyのコレクション

Prism for Windows Runtimeのライフサイクル


WPF4.5入門のPDFを更新しました

$
0
0

WPF4.5入門で書き続けてるWordファイルをPDF化してSlideShareにアップしました。 前回アップしたときよりも80ページちょい増えています。

残りあと少しの予定ですが、とりあえず今の進捗ということで。

WPF4.5入門 その56「コレクションのバインディング」

$
0
0

データバインディングでは、ここまで説明してきた単一項目のデータバインディングの他に、コレクションをバインディングすることができます。コレクションのデータバインディングは、IEnumerableを実装したコレクションなら、どれでも対象になります。その中でも、INotifyCollectionChangedインターフェースを実装したコレクションは、追加・削除などの変更操作をデータバインディングのターゲットと同期をとることが出来ます。 INotifyCollectionChangedインターフェースの実装は大変な作業なので、デフォルトでObservableCollectionという実装クラスが提供されています。特に理由がなければ、WPFでコレクションのデータバインディングを行う場合はObservableCollectionを使います。

NameとAgeプロパティを持つPersonクラスのコレクションをListBoxにデータバインディングする例を以下に示します。DataContextにPersonクラスのコレクションをMainWindowクラスのコンストラクタで設定します。

using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

namespace CollectionBindingSample01
{
    /// <summary>/// MainWindow.xaml の相互作用ロジック/// </summary>publicpartialclass MainWindow : Window
    {
        private ObservableCollection<Person> people;
        public MainWindow()
        {
            InitializeComponent();
            // DataContextにPersonのコレクションを設定するthis.people = new ObservableCollection<Person>(Enumerable.Range(1, 100)
                .Select(x => new Person
                {
                    Name = "tanaka" + x,
                    Age = (30 + x) % 50
                }));
            this.DataContext = people;
        }
    }
}

XAMLでは、DataContextのコレクションをItemsSourceにBindingして、ItemTemplateで整形して出力します。

<Window x:Class="CollectionBindingSample01.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow"Height="350"Width="525"><Grid><ListBoxItemsSource="{Binding}"><ListBox.ItemTemplate><DataTemplate><StackPanel><TextBlock Text="{Binding Name}" /><TextBlock Text="{Binding Age}" /></StackPanel></DataTemplate></ListBox.ItemTemplate></ListBox></Grid></Window>

表示は以下のようになります。

f:id:okazuki:20141029215031p:plain

コレクションのデータバインディングは、基本的に、このようにItemsSourceプロパティにバインドして、ItemTemplateで見た目を整える形になります。詳しくは、ListBoxやDataGridの解説の箇所を参照してください。

コレクションの変更通知

このサンプルプログラムは、ObservableCollectionを使用しているので、コレクションに追加や削除などを行うと、画面の表示も更新されます。先程のサンプルプログラムに、Menuを追加して、そこに追加とクリアのMenuItemを追加します。そして、イベントハンドラに以下のようなコードを記述します。

privatevoid MenuItemAdd_Click(object sender, RoutedEventArgs e)
{
    // コレクションに要素を追加する。this.people.Insert(0, new Person { Name = "追加したtanaka", Age = 100 });
}

privatevoid MenuItemClear_Click(object sender, RoutedEventArgs e)
{
    // 全削除this.people.Clear();
}

コレクションへの要素の追加と、コレクションから全要素の削除処理を行っています。サンプルプログラムを起動して追加メニューをクリックすると以下のようにコレクションに要素が追加され、それにあわせて表示も更新されます。

f:id:okazuki:20141029215149p:plain

クリアメニューをクリックすると、peopleコレクションの中身がクリアされ、それにあわせて表示もクリアされます。

f:id:okazuki:20141029215224p:plain

CollectionView

コレクションのデータバインディングを行うと、内部的には、直接コレクションがバインドされるのではなく、間にCollectionViewというものが暗黙的に生成されます。これをデフォルトのCollectionViewといいます。このデフォルトのCollectionViewを取得するにはCollectionViewSourceクラスのGetDefaultViewメソッドを呼んで取得します。

CollectionViewは、コレクション本体を操作することなく、要素の並び替えやフィルター、グルーピングなどを行うことが出来るコレクションに対するビューの役割を担います。Filter処理を行うには、CollectionViewのFilterプロパティにPredicate型のデリゲートを渡します。年齢が偶数の人のみを表示するコード例を以下に示します。

var collectionView = CollectionViewSource.GetDefaultView(this.people);
collectionView.Filter = x =>
    {
        // 年齢が偶数の人だけにフィルタリング
        var person = (Person)x;
        return person.Age % 2 == 0;
    };

ソート処理を行うには、SortDescritptionsプロパティにSortDescriptionを追加することでソートができます。SortDescriptionは、ソートのキーになるプロパティ名と、昇順・降順を表すListSortDirection列挙体を渡します。Ageプロパティをキーにして昇順にソートするコード例を以下に示します。

var collectionView = CollectionViewSource.GetDefaultView(this.people);
// Ageプロパティで昇順にソートする
collectionView.SortDescriptions.Add(new SortDescription("Age", ListSortDirection.Ascending));

グルーピングを行うには、GroupDescriptionsプロパティにPropertyGroupDescriptionを追加することでグルーピングが出来ます。PropertyGroupDescriptionには、グルーピングするプロパティ名と、必要に応じてプロパティ値を変換するためのIValueConverterを指定出来ます。Ageプロパティの10の位の値をもとにグルーピングを行うコード例を以下に示します。

class AgeConverter : IValueConverter
{
    publicobject Convert(objectvalue, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return (int)value / 10;
    }

    publicobject ConvertBack(objectvalue, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        thrownew NotImplementedException();
    }
}
// --------------------------------------------------------------------------------------
var collectionView = CollectionViewSource.GetDefaultView(this.people);
// Ageプロパティの10の位の値でグルーピングする
collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Age", new AgeConverter()));

グルーピングした結果を表示するには、ListBoxコントロールなどのGroupStyleプロパティにGroupStyleを設定する必要があります。GroupStyleのHeaderTemplateにDataTemplateを指定することで、グループ単位のヘッダー要素を指定出来ます。コード例を以下に示します。

<ListBox.GroupStyle><GroupStyle><GroupStyle.HeaderTemplate><DataTemplate><TextBlock Text="{Binding Name, StringFormat={}{0}0代}" /></DataTemplate></GroupStyle.HeaderTemplate></GroupStyle></ListBox.GroupStyle>

GroupStyleのHeaderTemplateでは、Nameプロパティでグルーピングするときにつかわれた値(今回の例では、10代なら1、20代なら2)が取得できます。そして、BindingのStringFormatプロパティを使ってデータバインディングの値の書式が指定できるため、”X0代”と表示するようにしています。

実行してグルーピング処理を走らせた結果の画面を以下に示します。

f:id:okazuki:20141029215512p:plain

CollectionViewSource

ここまで、デフォルトのCollectionViewを使ってきましたが、CollectionViewを明示的に作成することもできます。CollectionViewを作成するには、CollectionViewSourceクラスを使用します。CollectionViewSourceクラスのSourceプロパティにコレクションを設定すると、その型に応じて自動的にCollectionViewが内部で生成されます。CollectionViewの取得はCollectionViewSourceクラスのViewプロパティで行えます。CollectionViewSourceは、通常以下のようにXAMLのResourcesに定義します。Resourcesに定義したCollectionViewSourceは、BindingのSourceに指定して使います。

<Window x:Class="CollectionBindingSample03.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow"Height="350"Width="525"><Window.Resources><CollectionViewSourcex:Key="Source"Source="{Binding}" /></Window.Resources><Grid><DataGrid ItemsSource="{Binding Source={StaticResource Source}}" /></Grid></Window>

CollectionViewSourceには、CollectionViewと同様にソート、フィルタ、グルーピング機能があります。CollectionViewSourceでは、宣言的にこれらの機能を定義することが出来ます。

年齢の降順でソートして、年齢ごとにグルーピングする場合の定義は以下のようになります。

<CollectionViewSourcex:Key="Source"Source="{Binding}"><CollectionViewSource.GroupDescriptions><PropertyGroupDescription PropertyName="Age"/></CollectionViewSource.GroupDescriptions><CollectionViewSource.SortDescriptions><ComponentModel:SortDescription Direction="Descending"PropertyName="Age"/></CollectionViewSource.SortDescriptions></CollectionViewSource>

フィルタは、この例では示していませんが、Filterイベントを定義することで実現できます。

リアルタイムソート・フィルター・グルーピング

CollectionViewとCollectionViewSourceを使ったフィルタ・ソート・グルーピングは、適用した直後のコレクションの状態を元にして処理が行われます。フィルタ・ソート・グルーピングを行った後に、コレクションが変更されたらグルーピングなどの状態は更新されません。コレクションの変更に追随して、フィルタ・ソート・グルーピングも更新されるようにするには、CollectionViewSourceクラスで以下のプロパティにTrueを設定します。

  • IsLiveFilteringRequested:リアルタイムにフィルタリングをサポートしているCollectionViewの場合はリアルタイムにフィルタリングを行う。
  • IsLiveSortingRequested:リアルタイムにソートをサポートしているCollectionViewの場合はリアルタイムにソートを行う。
  • IsLiveGroupingRequested:リアルタイムにグルーピングをサポートしているCollectionViewの場合はリアルタイムにグルーピングを行う。

上記のプロパティでリアルタイムの情報更新を有効にしたうえで、LiveFilteringProperties、LiveSortingProperties、LiveGroupingPropertiesで、フィルタ・ソート・グルーピングを有効にするプロパティ名を指定します。Ageプロパティでリアルタイム機能を有効にする場合のXAMLを以下に示します。

<CollectionViewSourcex:Key="Source"Source="{Binding}"IsLiveFilteringRequested="True"IsLiveGroupingRequested="True"IsLiveSortingRequested="True"><CollectionViewSource.LiveSortingProperties><System:String>Age</System:String></CollectionViewSource.LiveSortingProperties><CollectionViewSource.LiveGroupingProperties><System:String>Age</System:String></CollectionViewSource.LiveGroupingProperties><CollectionViewSource.LiveFilteringProperties><System:String>Age</System:String></CollectionViewSource.LiveFilteringProperties></CollectionViewSource>

これに、通常のソート機能・グルーピング機能などを追加します。今回の例ではAgeプロパティのみがリアルタイムに反映されます。

<CollectionViewSource.GroupDescriptions><PropertyGroupDescription PropertyName="Age"/></CollectionViewSource.GroupDescriptions><CollectionViewSource.SortDescriptions><ComponentModel:SortDescription Direction="Descending"PropertyName="Age"/></CollectionViewSource.SortDescriptions>

現在の選択項目をバインディングする方法

CollectionViewは、現在の選択項目を管理しているので、同じCollectionViewをバインドしているもの同士では、選択項目を同期することが可能です。選択項目を同期するには、Selectorコントロール(ListBoxなどの親クラス)のIsSynchronizedWithCurrentItemをTrueにする必要があります。選択中の項目をバインドするには、”/”を使ってコレクションとバインドします。たとえば、Categoriesというコレクションにバインドしていて、それの選択中の項目のNameプロパティをバインドするためには”Categories/Name”のようにバインディングのPathを指定します。この”/”を使ったバインディングは、親子だけでなく孫やその先までコレクションがある限り指定できます。例えば、Peopleコレクションの選択中の項目のChildrenプロパティ(これもコレクション)の選択中の項目のNameプロパティは“People/Children/Name”のように指定できます。

例として以下のようなPersonクラスをバインドしたケースを示します。

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace CollectionBindingSample04
{
    publicclass Person : INotifyPropertyChanged
    {
        publicevent PropertyChangedEventHandler PropertyChanged;
        privatevoid SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            field = value;
            var h = this.PropertyChanged;
            if (h != null) { h(this, new PropertyChangedEventArgs(propertyName)); }
        }

        privatestring name;

        publicstring Name
        {
            get { returnthis.name; }
            set { this.SetProperty(refthis.name, value); }
        }

        privateint age;

        publicint Age
        {
            get { returnthis.age; }
            set { this.SetProperty(refthis.age, value); }
        }

    }
}

このクラスのコレクションを持ったDataContextに格納するクラスを以下に示します。

using System.Collections.ObjectModel;

namespace CollectionBindingSample04
{
    publicclass MainWindowViewModel
    {
        private ObservableCollection<Person> people;

        public ObservableCollection<Person> People
        {
            get { return people; }
            set { people = value; }
        }

    }
}

MainWindowのコンストラクタでDataContextに設定します。

public MainWindow()
{
    InitializeComponent();
    this.DataContext = new MainWindowViewModel
    {
        People = new ObservableCollection<Person>(
            Enumerable.Range(1, 100)
               .Select(x => new Person
               {
                   Name = "tanaka" + x,
                   Age = (30 + x) % 50
               }))
    };
}

このMainWindowViewModelクラスのPeopleをDataGridにバインドします。バインド時に、IsSynchronizedWithCurrentItemをTrueに設定して、選択項目の同期を有効にします。

<!-- 選択項目を同期するように設定してバインドする --><DataGrid IsSynchronizedWithCurrentItem="True"ItemsSource="{Binding People}"/>

選択項目をバインドするためにPeople/NameのようにPathを指定してデータバインディングを行います。

<!-- 現在選択中の項目をバインドする --><Label Content="名前"/><TextBox TextWrapping="Wrap"Text="{Binding People/Name}"/><Label Content="年齢"/><TextBox TextWrapping="Wrap"Text="{Binding People/Age}"/>

実行すると選択項目がTextBoxに表示されることが確認できます。

f:id:okazuki:20141029220135p:plain

別スレッドからのコレクションの操作

データバインディングしたコレクションは、通常UIスレッドから操作する必要がありますが、WPFでは、BindingOperations. EnableCollectionSynchronizationメソッドを呼び出すことで、コレクションをUIスレッド以外から操作できるようになります。EnableCollectionSynchronizationメソッドは、第一引数にコレクションを渡して、第二引数にコレクションを操作するときに使用するロックオブジェクトを指定します。使用例を以下に示します。

publicpartialclass MainWindow : Window
{
    private Timer timer;
    private ObservableCollection<Person> people;

    public MainWindow()
    {
        InitializeComponent();
        // コレクションをDataContextに設定するthis.people = new ObservableCollection<Person>();
        this.DataContext = this.people;

        // コレクションの操作をロックするように設定
        BindingOperations.EnableCollectionSynchronization(this.people, newobject());

        // 別スレッドからコレクションを操作するthis.timer = new Timer(1000);
        this.timer.Elapsed += (_, __) =>
            this.people.Add(new Person { Name = "tanaka " + this.people.Count });
        this.timer.Start();
    }
}

XAMLを以下に示します。

<Window x:Class="CollectionBindingSample05.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow"Height="350"Width="525"><Grid><DataGrid ItemsSource="{Binding}" /></Grid></Window>

BindingOperations.EnableCollectionSynchronizationの行をコメントアウトすると、例外が出てアプリケーションが終了することが確認できます。バックグラウンドスレッドでコレクション操作をするときは、自分でUIスレッドで操作をするようにするか、ここで紹介した、BindingOperations.EnableCollectionSynchronizationメソッドを使用しましょう。

過去記事

WPF4.5入門 その57「コマンド」

$
0
0

WPFには、ICommandインターフェースというユーザーの操作を抽象化する仕組みがあります。ICommandインターフェースは、以下のように定義されています。

publicinterface ICommand
{
        //     コマンドを実行するかどうかに影響するような変更があった場合に発生します。event EventHandler CanExecuteChanged;

        //     現在の状態でこのコマンドを実行できるかどうかを判断するメソッドを定義します。//// パラメーター://   parameter://     コマンドで使用されたデータ。 コマンドにデータを渡す必要がない場合は、このオブジェクトを null に設定できます。//// 戻り値://     このコマンドを実行できる場合は true。それ以外の場合は false。bool CanExecute(object parameter);

        //     コマンドの起動時に呼び出されるメソッドを定義します。//// パラメーター://   parameter://     コマンドで使用されたデータ。 コマンドにデータを渡す必要がない場合は、このオブジェクトを null に設定できます。void Execute(object parameter);
}

コマンドが実行可能かどうかという状態に変化があったことを通知するCanExecuteChangedイベントと、実際にコマンドが実行可能かどうかを返すCanExecuteメソッドがあります。そして、コマンドの処理を実行するためのExecuteメソッドがあります。

ICommandは、ICommandSourceという以下のようなインターフェースを実装したクラスに対して設定することが出来ます。ICommandSourceは以下のように定義されています。

//     コマンドを呼び出す方法を認識しているオブジェクトを定義します。publicinterface ICommandSource
{
    //     コマンド ソースが呼び出されると実行されるコマンドを取得します。//// 戻り値://     コマンド ソースが呼び出されると実行されるコマンド。
    ICommand Command { get; }

    //     コマンドの実行時にコマンドに渡すことのできるユーザー定義データの値を表します。//// 戻り値://     コマンド固有のデータ。object CommandParameter { get; }

    //     コマンドが実行されているオブジェクト。//// 戻り値://     コマンドが実行されているオブジェクト。
    IInputElement CommandTarget { get; }
}

実行するコマンドを取得するためのCommandプロパティと、コマンドに渡すためのCommandParameterが定義されています。CommandTargetは後述するRoutedCommandのみに適用される特殊なプロパティなのでここでは省略します。ICommandSourceインターフェースは、ButtonBase(ボタン系コントロールの基本クラス)やMenuItemなど、ユーザーがアクションを実行するコントロールに主に実装されています。

WPFでのICommandインターフェースの実装クラスのRoutedCommandクラスは、CommandBindingという仕組みを通じてユーザーのアクションと処理を結びつける機能を持っています。RoutedCommandクラスは、以下のように、クラスの静的メンバーとして定義して使用します。

publicpartialclass MainWindow : Window
{
    publicstatic RoutedCommand AlertCommand = new RoutedCommand();

    public MainWindow()
    {
        InitializeComponent();
    }
}

このRoutedCommandと、実際の処理を結びつけるには、UIElementクラスに定義されているCommandBindingsプロパティにCommandBindingを設定して行います。一般的にWindowクラスのCommandBindingsプロパティを使って以下のように定義します。

<Window.CommandBindings><CommandBinding Command="{x:Static local:MainWindow.AlertCommand}"Executed="CommandBinding_Executed"CanExecute="CommandBinding_CanExecute"/></Window.CommandBindings>

CommandBindingクラスのCommandプロパティに、先程定義したCommandのインスタンスを設定します。そして、ExecuteイベントにCommandの実行時の処理のイベントハンドラと、CanExecuteイベントにCommandの実行可否の処理のイベントハンドラを設定します。ここでは、仮にCheckBoxコントロールが画面にあり、CheckBoxコントロールにチェックがされているときだけ実行可能なコマンドを定義します。画面にCheckBoxコントロールと、コマンドを実行するためのButtonを置きます。ButtonのCommandプロパティには、CommandBindingに設定したものと同じ、MainWindowクラスのAlertCommandプロパティを設定します。

<Window x:Class="CommandSample01.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:CommandSample01"Title="MainWindow"Height="350"Width="525"><Window.CommandBindings><CommandBinding Command="{x:Static local:MainWindow.AlertCommand}"Executed="CommandBinding_Executed"CanExecute="CommandBinding_CanExecute"/></Window.CommandBindings><StackPanel><CheckBox x:Name="checkBox"Content="CanExecute"/><Button Content="AlertCommand"Command="{x:Static local:MainWindow.AlertCommand}" /></StackPanel></Window>

そして、コードビハインドでイベントハンドラの処理を記述します。

publicpartialclass MainWindow : Window
{
    publicstatic RoutedCommand AlertCommand = new RoutedCommand();

    public MainWindow()
    {
        InitializeComponent();
    }

    privatevoid CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("Hello world");
    }

    privatevoid CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = this.checkBox.IsChecked.Value;
    }
}

全体のつながりを説明すると、CommandBindingとButtonのCommandに同じコマンドのインスタンスを設定することで、この2つを繋げます。そして実際の処理は、CommandBindingのイベントハンドラで行います。実行結果を以下に示します。

実行すると、以下のようにボタンが押せない状態で起動します。

f:id:okazuki:20141029220803p:plain

CheckBoxコントロールにチェックを入れると、ボタンが押せるようになります。これはCommandBindingに設定したCanExecuteイベントのイベントハンドラで行っている処理でCheckBoxコントロールのチェック状態を見てイベント引数のCanExecuteプロパティに実行可否の値を設定しているためです。

f:id:okazuki:20141029220835p:plain

そして、Buttonコントロールをクリックすると、CommandBindingのExecuteイベントのイベントハンドラが実行されてMessageBoxが表示されます。

f:id:okazuki:20141029220902p:plain

このように、RoutedCommandクラスと、CommandBindingクラスを使うことで操作を表すコマンドと実際の処理を分離して記述することが出来ます。

コマンドは、InputBindingを使うことで簡単にキーボードショートカットやマウスジェスチャーに対応させることが出来ます。WindowなどのInputBindingsプロパティに、KeyBindingを設定することでキーボードショートカットとコマンドの関連付けを行うことが出来ます。KeyBindingのModifiersプロパティに修飾キーを設定して、Keyプロパティにキーを設定して、Commandプロパティに該当するキーボードが押されたときに実行する処理を表すコマンドを設定します。例として、先程のAlertCommandをCtrl+Alt+Aを押したときに表示するようにするコードを示します。

<Window.InputBindings><KeyBinding Modifiers="Alt+Control"Key="A"Command="{x:Static local:MainWindow.AlertCommand}" /></Window.InputBindings>

実行して、CheckBoxコントロールにチェックを入れた状態でCtrl+Alt+Aを押すとMessageBoxが表示されます。

これまでの例では、自分でRoutedCommandのインスタンスを用意したものを使用しましたが、WPFでは組み込みで、アプリケーションによくあるコマンドがあらかじめ定義されています。コピーやペーストなどの一般的な操作をRoutedCommandで定義する場合は、下記のApplicationCommandsクラスに定義されているものを使用するとよいでしょう。

このように、WPFでは、組み込みのICommandインターフェースの実装が提供されています。しかし、ICommandインターフェースを実装していれば、InputBindingなどの機能は使うことが出来ます。最近のWPFをはじめとするXAMLを使った開発ではICommandインターフェースを実装してExecuteやCanExecuteの処理をデリゲートで受け取るDelegateCommand(RelayCommandという名前の場合もある)という実装が使われるのが一般的です。これらのコマンドの独自実装については後述します。

過去記事

Windows 8とかでMSアカウント使ってるとAzure仮想マシンにRDPで認証で蹴られる

$
0
0

MSCCでMSDN Subscription Ultimate民が量産されたところなので、Azure上に仮想マシン立ててスグにこわせる開発環境として使おうとしてる人もいると思います。

そこで、たまに表題の件でつまずくことがあります。

ログインのときに認証ではじかれた場合は以下のことを試してみてください。

アカウント名を以下のようにして入れる。

.\アカウント名

こうするとうまくいくと思います。

Blend for Visual Studio 2015が事件です

$
0
0

先日DownloadできるようになったVisual Studio 2015 Previewですが、こいつのBlendがやばいです。どうやばいってVisual StudioのShellをベースにBlendが再構築されたみたいな感じになってます。具体的には以下のウィンドウが普通にある。

  • Solution Explorer
  • Team Explorer

コードエディタがVisual Studioと同じ。インテリセンス、コードスニペット、etc...が全て使える。

テーマを青とかにすると、どう見てもVisual Studioですありがとうございます。

f:id:okazuki:20141113010450p:plain

コードエディタ

今までBlendのコードエディタは貧弱なものでした。どれくらい貧弱かというと見るにはいいけど、Visual Studioに比べるとコード補完も何もないというお粗末なものでした。

今回のBlendでは、Visual Studioと同じコード補完機能が備わってるっぽいです。ためしにコードスニペットとか登録してみたりしたら普通に動きました。インテリセンスもがっつり動きます。

XAMLでAlt + F12してコードを参照してる状態。

f:id:okazuki:20141113011016p:plain

デザイナ

デザイナはBlend従来通りっぽいです。Assets, State, Data, Document Outlineがあります。

ソリューションエクスプローラー・チームエクスプローラー

ソリューションエクスプローラーがそのまま使えるので、フォルダとかにきちっと対応してくれます。あと、チームエクスプローラーも統合されてるので、がっつりチーム開発にも対応できます。

無くなったもの

新規作成で、WPF, Sliverlight, Universal Windows appなどのアプリが作れるのですが、スケッチフローがなくなってますorz 残念ですが、これからはストーリーボーディングを使おうってことなんですかね。

まとめ

というかVisual Studio 2015にBlendの機能が入ればいいんじゃないかというくらいBlendがVisual Studioちっくになってます。これまでVisual Studioをメインの環境として必要に応じてBlendを開くという形でしたが、Visual Studio 2015からは、クライアント系の開発をするときはBlend for Vsiual Studio 2015だけ開いてれば開発出来るようになってる感じです。

個人的には歓迎。でもVSに統合されるのが一番だと思うなぁ。

Viewing all 1388 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>