スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: スポンサー広告

一度のPropertyChangedで全バインディングを更新するには?

WPF MVVM - Can a single PropertyChanged update all the Data bindings of a DataTemplate?
一度のイベント励起で、そのクラス(ViewModel)とのデータバインディングを更新する方法か。プロパティ名無しで送ったらそういう事になるんだ。知らなかった。DataGridの行(アイテム)の表示を更新するときなんかに使えそう。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

SQLの発音は?

 SQLの発音は、「エスキューエル」しかないと思ってたら、そうでもなかったという話。
 うちの上司が、アメリカ出張で向こうの人よ話した時、SQLを「シークエル」と言ってたらしい。へーとその時は思ったんだけど、ぐぐってみたら、
なぜSQLは「シークェル」と発音するのか?
なんてのがあり、これによれば、SQLの原型となったものが「シークエル」らしい。まあ、これも結局、「へー」と思っただけなんだけど、そういう昔々的な名残の発音を使うものなんだなと思った。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: 未分類 | コメント: 0 | トラックバック: 0

DataGrid, 脱Reflection(壱)

 WPF4のDataGridで表示される各行(アイテム)は、ItemsSourceプロパティにバインディングされたコレクションのアイテムとバインディングされる。そして、各列の値は、Bindingで指定されたPathを使ってそのアイテムのプロパティから取得される。この「アイテムから指定プロパティの値を取得する」は、どう実装されているかと言えば、やはりというか、当然リフレクションが使われている。このリフレクションでプロパティの値を取得するという処理は、パフォーマンス的にあまりよろしくない。一つや二つであれば無視できるレベルであるんだけど、数が増えれば無視できなくなる。
 例えば、四十個のプロパティとバインディングする行が四万行あるとすれば、リフレクションの呼び出しは、40,000行x40個 = 1,600,000回になる。四十個なんてあるの?と思うが、実際の仕事において、四十個なんてザラである。自分が開発に関わったアプリケーション(Windows.Forms)の中では、最大の列数は百十個超だ。まあ、百個超のプロパティを持つアイテムを万単位でバインディングすると、WPFだのWindows.Formsだの関係なく、メモリがやばい事になるんだけど(こっちはこっちでデータ仮想化というテーマで記事にできるんだけど、本稿とは別の話になる。ぶっちゃけ、データ仮想化の方が大事だったりする)。
 じゃあ、このリフレクションを止めて他の方法にしたいと思っても、リフレクションを使うのはWPFであって、開発者が手を出せない領域であったりする。どうにかならないかと調べたら、TypeDescriptionProvider属性を設定すると、リフレクションよりそちらを優先するらしいという記事があった(正確には、ICustomTypeDescriptorインターフェースのGetPropetiesメソッドが最優先される。このインターフェースが、TypeDescriptionProvider属性クラスと密接に関係する)。この辺り、結構ややこしく、調べるのに時間がかかったけど、
TypeDescriptionProvider
ICustomTypeDescriptor
PropertyDescriptor
この3つを上手く実装すれば、脱リフレクションへの道が開けるかもという事がわかった。

PropertyDescriptorクラスは、プロパティの情報取得、値の設定や取得にも使えるクラス。PropertyDescriptorというクラスそのものは、抽象クラス。抽象クラスなんだけど、こんな記述がある。

通常、abstract メンバーはリフレクションによって実装されます。 リフレクションの詳細については、.NET Framework のリフレクション のトピックを参照してください。

ああ、ということは、PropertyDescriptor含めて、各クラスを継承して自分で実装しないと、意味ないってことですね。
続く。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

画面の起動はビューの仕事らしい(参)

起動するだけならできたけど、当然それだけではどうにもならない。起動前と終了後で、呼び出し側のViewModelで何らかの処理をできるようにする。
/// <summary>実行前処理コマンド</summary>
public static readonly DependencyProperty PreviewStartCommandProperty =
DependencyProperty.Register("PreviewStartCommand",
typeof(ICommand),
typeof(StartWindowAction),
new FrameworkPropertyMetadata(null));

/// <summary>
/// 実行前処理コマンドを取得|設定します。
/// </summary>
public ICommand PreviewStartCommand {
get { return (ICommand)GetValue(PreviewStartCommandProperty); }
set { SetValue(PreviewStartCommandProperty, value); }
}

/// <summary>起動画面終了時処理コマンド</summary>
public static readonly DependencyProperty CallbackClosedCommandProperty =
DependencyProperty.Register("CallbackClosedCommand",
typeof(ICommand), typeof(StartWindowAction),
new FrameworkPropertyMetadata(null));

/// <summary>
/// 起動画面終了時処理コマンドを取得|設定します。
/// </summary>
public ICommand CallbackClosedCommand {
get { return (ICommand)GetValue(CallbackClosedCommandProperty); }
set { SetValue(CallbackClosedCommandProperty, value); }
}
PreviewStartCommandプロパティは、実行前、CallbackClosedCommandプロパティは実行後に処理されるコマンドオブジェクトを指定する。これらがそう機能するように、Executeメソッドのロジックを変更する。
var dataContext = startWindow.DataContext;

if (dataContext != null) {
//BaseWindowVM継承クラスをViewModelとして使っている時
//IsMoalプロパティを渡しておこう。
if (dataContext is BaseWindowVM) {
(dataContext as BaseWindowVM).IsModal = this.IsModal;
}

if (this.PreviewStartCommand != null &&
this.PreviewStartCommand.CanExecute(startWindow.DataContext)) {
this.PreviewStartCommand.Execute(startWindow.DataContext);
}
}
ただ、この処理は、Window生成と同時にViewModelも生成され、DataContextにセットされている事が前提となる。そうならないロジックで実装するときは、DataContextChangedイベントへハンドラを登録し、DataContext設定時に実行する。
//dataContext未設定って事あるのかも?
startWindow.DataContextChanged +=
new DependencyPropertyChangedEventHandler(
StartWindow_DataContextChanged);

/// <summary>
/// 起動ウィンドウ DataContext変更イベント用メソッド
/// </summary>
/// <param name="sender">イベントソース</param>
/// <param name="e">イベントデータ</param>
private void StartWindow_DataContextChanged(
object sender, DependencyPropertyChangedEventArgs e) {

if (e.OldValue != null) {
if (e.OldValue is IDisposable) {
(e.OldValue as IDisposable).Dispose();
}
}

if (e.NewValue != null) {

if (sender is Window &&
e.NewValue is BaseWindowVM) {
//Modal設定
(e.NewValue as BaseWindowVM).IsModal =
WindowBehavior.GetIsModal(sender as Window).GetValueOrDefault();
}

if (this.PreviewStartCommand != null &&
this.PreviewStartCommand.CanExecute(e.NewValue)) {
this.PreviewStartCommand.Execute(e.NewValue);
}
}

return;
}
次に事後にCallbackClosedCommandコマンドが呼び出される様に実装する。モーダルかモードレスかで実装が変わる。
if (this.IsModal) {
startWindow.Owner = this.ownerWindow_;
var dialogResult = startWindow.ShowDialog();

if (dataContext != null &&
dataContext is BaseWindowVM) {
(dataContext as BaseWindowVM).DialogResult = dialogResult;
}

if (this.CallbackClosedCommand != null) {
//コマンドパラメータにはViewModelが設定されます。
this.CallbackClosedCommand.Execute(dataContext);
}

startWindow.DataContext = null;

} else {
startWindow.Closed += new EventHandler(StartWindow_Closed);
startWindow.Show();
}

/// <summary>
/// 起動ウィンドウ終了イベント用メソッド
/// </summary>
/// <param name="sender">イベントソース</param>
/// <param name="e">イベントデータ</param>
private void StartWindow_Closed(object sender, EventArgs e) {

var closedWindow = sender as Window;
closedWindow.Closed -= new EventHandler(StartWindow_Closed);
var dataContext = closedWindow.DataContext;

if (this.CallbackClosedCommand != null) {
//コマンドパラメータにはViewModelが設定されます。
this.CallbackClosedCommand.Execute(dataContext);
}

closedWindow.DataContext = null;
closedWindow.DataContextChanged -=
new DependencyPropertyChangedEventHandler(
StartWindow_DataContextChanged);
return;
}
・モードレス起動時は、Ownerプロパティに親ウィンドウを設定しない。
 設定してしまうと、親ウィンドウを最小化させただけで閉じてしまうから。
・モーダル起動のみDialogResultプロパティを使うことができる。
 モードレスで使おうとすると、たしか例外が発生したような。
・モードレスは、別スレッド的な動きになるので、Closedイベント内でCallbackClosedCommandを実行する。
・コマンド引数には、ViewModelを渡す。
使い方
<tw:StartWindowAction x:Name="ShowWindow2"
WindowType="{x:Type local:Window2}"
IsModal="False"
PreviewStartCommand="{Binding PreviewShowWindow2Command}"
CallbackClosedCommand="{Binding ClosedWindow2Command}"
/>

これで、事前に値を渡したり、事後に結果を取得できるようになる。ただ、これも単一起動用。要件によっては複数起動もありうるので、その時はもう少し改造が必要になる。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

凝り過ぎるのもバグの元ってか

繰り返し文をLINQっぽくで書いたうちの2つのメソッド
using System;
using System.Collections;
using System.Collections.Generic;

namespace Jp.TwWorks {

public static partial class Utils {

/// <summary>
/// 途中終了可能なforeach
/// </summary>
/// <param name="srcList">リスト</param>
/// <param name="action">メソッド</param>
public static void ForEach<T>(
IEnumerable<T> srcList,
Action<T> action) {
if (srcList == null) {
return;
}

foreach (T element in srcList) {
action(element);
}

return;
}

/// <summary>
/// 途中終了可能なforeach(break機能追加)
/// </summary>
/// <param name="srcList">リスト</param>
/// <param name="action">メソッド</param>
public static void ForEach<T>(
IEnumerable<T> srcList,
Func<T, bool> action) {
if (srcList == null) {
return;
}

foreach (T element in srcList) {
if (!action(element)) {
break;
}
}

return;
}
}
}
これを利用するとき、ある特定の書き方をすると、ループしないことがわかった。
class Class1 {

public Class1() {
BoolProp = true;
return;
}

public bool BoolProp {
get; set;
}
}

var list = new List<Class1>() {new Class1(), new Class1(), new Class1()};
Utils.ForEach(list, element => element.BoolProp = false);

ビルドは通る。
実行も例外なく終了する。
しかし、falseになるのは最初の1つだけである。自分的には戻り値無しのActionのつもりで渡していたのだけれど、コンパイラは、代入した後にそのプロパティ値を返すFuncと認識している。ループさせる事を想定しているんのに、ループしない。そんな意図しない動きになっていた。うん。こんな事が起きるくらいなら、引数がActionの方だけで良いな。凝りすぎるのも考えものだ(ーー;。

当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

画面の起動はビューの仕事らしい(弐)

画面の起動はビューの仕事らしい(壱)の続き。
依存関係プロパティを実装する。
/// <summary>Windowタイプ</summary>
public static readonly DependencyProperty WindowTypeProperty =
DependencyProperty.Register("WindowType", typeof(Type),
typeof(StartWindowAction),
new FrameworkPropertyMetadata(null,
OnCanStartWindowPropertiesChanged));

/// <summary>
/// Windowタイプを取得|設定します。
/// </summary>
public Type WindowType {
get { return (Type)GetValue(WindowTypeProperty); }
set { SetValue(WindowTypeProperty, value); }
}

/// <summary>モーダル起動させるかどうか</summary>
public static readonly DependencyProperty IsModalProperty =
DependencyProperty.Register("IsModal",
typeof(bool),
typeof(StartWindowAction),
new FrameworkPropertyMetadata(true));
/// <summary>
/// モーダル起動させるかどうかを取得|設定します。
/// </summary>
public bool IsModal {
get { return (bool)GetValue(IsModalProperty); }
set { SetValue(IsModalProperty, value); }
}

両方のプロパティの役割は名前そのまま。WindowTypeプロパティの方は、設定の有無で実行可否が変わるので、OnCanStartWindowPropertiesChangedメソッドでコマンドの実行可否を再評価させる。
/// <summary>
/// CanStartWindowプロパティ値変更イベント用メソッド
/// </summary>
/// <param name="dpObj">対象オブジェクト</param>
/// <param name="e">イベントデータ</param>
private static void OnCanStartWindowPropertiesChanged(
DependencyObject dpObj,
DependencyPropertyChangedEventArgs e) {

var action = dpObj as StartWindowAction;

if (action == null || action.IsDesignMode()) {
return;
}

//実行可否判定の再評価を通知します。
//値ではなく、値の変更がトリガーになります。
if (this.canExecuteChangedHandler_ != null) {
this.canExecuteChangedHandler_(this, System.EventArgs.Empty);
}
return;
}
次に、ICommandインターフェースを実装する。
/// <summary>コマンド実行可否判定変更イベント用ハンドラ</summary>
private System.EventHandler canExecuteChangedHandler_;

/// <summary>
/// コマンド実行可否判定変更イベント用ハンドラ
/// </summary>
public event System.EventHandler CanExecuteChanged {
add {this.canExecuteChangedHandler_ += value;}
remove {this.canExecuteChangedHandler_ -= value;}
}


/// <summary>
/// 当コマンドを実行します。(Windowを起動します)
/// </summary>
/// <param name="parameter">コマンドパラメータ</param>
public void Execute(object parameter) {
var startWindow = (Window)System.Activator.CreateInstance(this.WindowType);
WindowBehavior.SetIsModal(startWindow, this.IsModal);

if (this.IsModal) {
startWindow.ShowDialog();
} else {
startWindow.Show();
}

return;
}

/// <summary>
/// 当コマンドを実行できるかどうかを判定します。
/// </summary>
/// <param name="parameter">コマンドパラメータ</param>
/// <returns>true:実行可能</returns>
public bool CanExecute(object parameter) {

if (this.WindowType == null ||
!typeof(Window).IsAssignableFrom(this.WindowType)) {
//未設定だったり、Window継承クラスじゃなかったり。
return false;
}

return true;
}
 XAMLはこうなる。
<tw:StartWindowAction x:Name="ShowWindow2"
WindowType="{x:Type local:Window2}"
IsModal="True"
/>
この実装でも起動だけならできる。そう、起動だけなら。
続く。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

画面の起動はビューの仕事らしい(壱)

 MVVMを使ってて悩む事には、メッセージボックスの表示実装の他に、画面の起動というものもある。
 MVVMでは、画面の起動というものは、ビュー(V)の仕事であるらしく、ViewModel側でWindowのインスタンス生成や、ShowDialogメソッド呼び出しなんてコードを書いてはいけません、という暗黙の縛りがある。まあ、縛りなんかなくても、ViewModelクラス内でそういうのはなんとなくもやっとする。Livetなんかでは、InteractionMessageTriggerなんかを駆使して起動するらしい
 Livetを使わっていない仕事用開発では、これ(画面起動)もまたコマンドクラスの実装でどうにかする事にした。CommandのExecuteを実行することで、画面を起動させる。このコマンドをバインドする事でView側からも実行できる。ただ、これは単にコマンドをかぶせただけで、View側に持って行きましたなんて言える物でもない。で、個人的に改めて検討する事にして、このあたりをコントロール化してView(XAML)側に埋め込めないものかと考えた(仕事では、一度実装して動き出したものは、おいそれと入れ替えることはできない。影響範囲が広くなればなるほど、再テストのコストが大きくなり、入れ替える事によるメリットが、そのコストを上回らない限りGoサインはでない)。
 で、ICommandインターフェースを実装するのは変えないけど、XAMLにも配置できるよう、FrameworkElementクラスを継承するようにしてみた。FrameworkElementにしたのは、Loadedイベント、Unloadedイベントがあるのがこのクラスだからである。
続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

嵌ってしまったので、反省を込めて

結構前に、依存関係プロパティに適用される優先順位(基本値)で書いていたのに、すっかり忘れてて、後輩ともどもはまってしまったので、もう一度書いておこう。

○初期の背景色を設定し、Triggerによって背景色を返る(今回のトリガーはIsFocusedプロパティ)。

(誤)これは動作しない。
<TextBox Text="" Background="Khaki">
<TextBox.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="LightPink"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Resources>
</TextBox>
TextBoxタグに直接設定した値はローカル値となり、最優先で採用される。その際、Styleで設定したTriggerまで見に行かず、結果的に動いてないように見える。
(正)これが動作する。
<TextBox Text="">
<TextBox.Resources>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Khaki"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="LightPink"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Resources>
</TextBox>
今度はすぐ気づきますように…。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

速(早)すぎると言えなくなってきた。

最新版Windowsの正式名称はWindows8.1に:スタート画面から簡単に無償アップデートできる模様
Windowsの最新版(Windows8.1)が今年中にリリースされるそうな。しかも、無償で。

スタートボタン(っぽいのが)復活
・標準ブラウザはIE11に。

ついにサービスパックも1で止まるようになったのか。まあ、無償提供っぽいので、これがSP1と思っても良いのかもしれない。しかし、Windowsストアからダウンロードか。時代の流れだな。
 しかし、IEのバージョンもどんどん上がっているな。もはや昔々になってしまったけど、IE5~6の頃はWEBアプリ開発をメインにしていた。WEBアプリでクライアントとして使うWebブラウザ、特にIEは各バージョンでJavascriptふくめ、色々な挙動が変わる。バージョンが少ない頃はちょっとの動作確認でどうにかなったが、こうも広がるとめんどくさい事このうえない話となる。IE6、黒歴史なIE7,IE8,IE9,出たてのIE10。確認するだけで、どれだけの手間やら。IETesterにも限界があるぞ!といいたい。実際、IETesterと実際のブラウザでも挙動が違うときがある。それにインストールされているプラグインの種類にも。「クライアントOSのバージョンに依存しません!」っていう謳い文句も今となっては通らないし。 現在MSが推している(ような)XAMLもいつまで使えることやら。早々に切られても次に移れるように準備と心構えは必要という事だろうか。しかし、
アプリを開発する側としては、ほしいのは新機能ではなく、速度と安定性なんだけどね。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: Windows | コメント: 0 | トラックバック: 0

多言語化、或いはローカライズ(四)

 前回の多言語化、或いはローカライズ(参)で、ローカライズにリソースファイルを使てみた。これ以外にやるのであれば、外のファイルに出すか、データベース内のテーブルに持たせるくらいかな。仕事ではデータベース内のテーブルを基本として、使ったリソースは、ローカルファイルにキャッシュする方法を採っている。
 文字列リソースをデータベースに持たせる理由は、他の複数のプロジェクトと共有する事。企業は、内部に固有の単語を持ち、それで話(打ち合わせ)をすることが多い。そうでなくても共通な単語は多い。例えば、部門、社員、営業、販売、勘定科目、等々。共通的な単語はそうそう変わらない。でも、ローカルな単語、特に新しく作られた単語はコロコロ変わる。開発中に変わると非常にめんどくさい。この単語を開発画面のあちこちに使っていると、逐一検索して修正するという手間が発生する。
 そういう手間をなくす為の共有化なんだけど、上でやった実装方法では、共有化が難しい。
  ・TranslateManagerが扱えるプロバイダ(リソースファイル)が1つだけ。
  ・複数持てたとしても、マークアップを使用するとき、どのリソースを使うかを指定する必要がある。
リソースを共有する為だけのライブラリプロジェクトを作ったとして、それを参照できる方法が必要なので、そういう感じに実装を変更してみる。

続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

(覚書)SharpDevelopにおけるリソースファイルの追加方法

VisualStudio系なら比較的簡単にできるリソースファイルの追加が、SharpDevelopだとそう簡単にできないので、メモっておく。
○リソースファイルを追加する
 ・Propertiesフォルダ上で右クリック→「Add」→「New Item」を選択する。
20130601_1
 ・「Misc」→「Empty Resource File」を選択して、ファイル名を入力後にOKを押す。

この時点で文字列を追加できるけど、追加したリソースを参照できない。

○カスタムツールで実行する。
 ・リソースファイル上で右クリックを押し、「Property」を選択する。
 ・プロパティグリッド内にある「Custom Tool」に「ResXFileCodeGenerator」を選択する。publicにして公開したいときは、「PublicResXFileCodeGenerator」を選択する。
 ・リソースファイル上で右クリックを押し、「Execute Custom Tool」を選択する。

これで、(リソース名).Designer.csというファイルができ、外部からも参照できる。
20130601_2

まあ、まんま書いてくれてるページがあってこそなんですけどね。
Using Resources
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。