スポンサーサイト

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

ソースコード、見っけ。

.NET Framework Libraries
.NET Framwworkのソースコード。
C#4, C#4.5の両方がある。サイズはだいたい130MBくらいかな?
その気になれば、VS2010とかでデバッグ実行した時に、.NETの内部までステップ実行できたりする、かも。
Step Into .NET Framework 4.0 Source Code
WPF4とかで、意味もわからずに落ちる(主にXAML内)時なんかは良いかもしれない。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

ロジックとデータアクセスの分離って大事だと思うよ?

クラスはできるだけシンプルが良い(Logic編)
クラスはシンプルが良い(DAO編)
の続き的な話になるだろうか?

業務ロジックとデータアクセスのコードは分離した方が良いと思う

まあ、こんな事、@ITなんかの記事に嫌ってほどあったりするので、今更なんだけど、仕事で他の人が書いたコードを読む機会があり、業務ロジッククラスの筈のメソッド内で、「業務ロジック→いきなりSQL発行!→データ取得!そのままロジック再開」に突き進むなんてコードがあり、今更そんなコードを追っかけて分析とかすることになったので、げんなりしてしまったので、書いてしまった。せめてDAOクラス作ってくれよ!と言いたい。いや、作っているところもあるんだけど、徹底されていないというか、途中から分けるのがめんどくさくなった感がすごくある。
でも、既にリリースされてしまっているコードなんで、安易に変更できない。これが個人的であったり、限定的な人数で使用しているコードならそんな事はないんだけど。一度コードを変更してしまうと、
・影響範囲はどこまでになるか?
・動作確認はしたか?
・リリースをいつにするか?
等々、それだけでは済まなくなる。なので、追っかけて、悶えるだけになってしまった。
基本、業務ロジックはロジックだけ。データアクセスはデータアクセスだけの役割を持たせるべきとは思う。
ロジック側が必要なDAOクラスのインスタンスを生成し、必要なメソッドを呼び、必要なデータを取得する。どのようにデータを取得するかは、DAOクラスに一任する。ロジック自身は知らない。こうすることで、データ取得先に変更があったり、データの持ち方(構造)に変更があったとしても、ロジッククラスには影響しないと思う。
仕事では、

・ロジッククラスがどのDBにアクセスするか?はロジッククラス自身の属性として持たせる。
・接続先の情報は、configファイルに持たせる。
・DBの接続、トランザクションの管理は、専用のクラスを実装する。
・設定ファイル情報をもとに、専用のクラスインスタンスを生成する。
・ロジッククラスは、その管理クラスインスタンスを保持し、DAOクラスインスタンス生成時にそのインスタンスを設定する。
・DBの接続開始と終了、トランザクションの開始とコミット(或いはロールバック)は、共通(基底)クラスのメソッドが行う。
・ロジッククラスは、接続先DBやテーブルの物理名を属性以外で意識することはない。
・DAOクラスは、どのDBMSに接続しているかをできるだけ意識しない。

というところまではやっている。
仕事では、どうしても「動いてなんぼ」が優先されるんだけども、後々の事も含めてトータル的に考えてコーディングしてくれるように、啓発していくしかないんだろうな。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

DataGrid用ViewModelクラスを実装した(四)。

DataGrid用ViewModelクラスを実装した(参)。で実装したコマンドを使って、カレントアイテム(太枠があるセル)の情報をプロパティとして持たせる。
なぜ、そんな実装になるか?というと、DataGrid自身に、CurrentItemというプロパティがあり、そいつとバインディングする事は可能だけども、このプロパティは、フォーカスがDataGridから外れた瞬間にnullになるという癖(仕様)があるため、カレントセルのアイテムに対して、何かをしようとボタンを押した瞬間にnullになってしまう。なので、直接バインディングせずに、カレントセルの移動イベントをコマンドで受け取る事で、カレントアイテムの情報を得ることにした。
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;

namespace TawamureDays.ViewModels {

using TawamureDays.Container;
using TawamureDays.Commands;

/// <summary>
/// データグリッド用ViewModelクラス
/// </summary>
public class DataGridVM<TItem> : UIElementVM {

#region プロパティ

/// <summary>
/// 現在のアイテム(カレントセルがある行のアイテム)を取得|設定します。
/// </summary>
public TItem CurrentItem {
get; protected set;
}

#endregion
}
}
これだけでは、どうにもならないなので、CellChangedイベント用のコマンドを実装する。

#region 内部メソッド

/// <summary>
/// カレントセル変更用コマンド実行メソッド
/// </summary>
/// <param name="parameter">コマンドパラメータ</param>
protected virtual void ExecuteCellChangedCommand(object parameter) {
var cellInfo = parameter as Tuple<int, int, object, string, bool>;

if (cellInfo != null && cellInfo.Item3 is TItem) {
this.CurrentItem = (TItem)cellInfo.Item3;
} else {
this.CurrentItem = default(TItem);
}

return;
}

#endregion
セルを移動させるたびにCurrentItemは更新されることになる。フォーカスが外れても、CellChangedは発生しないので、nullにはならない。
なお、CurrentItemをnullにせず、default(TItem)に初期化するのは、Genericの型に何の制限も設けていない(値型をジェネリックパラメータに設定することもできる)ので、上記実装では、nullに設定できない。以下のように参照型に限定するなら、nullに設定できる。

/// <summary>
/// データグリッド用ViewModelクラス
/// </summary>
public class DataGridVM<TItem> : UIElementVM where TItem : class {
}

DataGridのCurrentCellが、「フォーカスが外れる→nullになる」という仕様がなければ、実装する必要ないんだけど。WPF4.5ではどうなんんだろ?変わらなければ、そのまま使い続けるしかないなぁ。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

DataGrid用ViewModelクラスを実装した(参)。

DataGrid用ViewModelクラスを実装した(弐)。の続き。
DaraGridのイベントからセルの情報をVMで活用する。(壱)
や、
DaraGridのイベントからセルの情報をVMで活用する。(弐)
で作った添付プロパティと連携するためのプロパティをDataGridVMに持たせる。
定義、インタンス生成、後始末、実行用メソッドまで記述した時点で、↓のようになった。

using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;

namespace TawamureDays.ViewModels {

using TawamureDays.Container;
using TawamureDays.Commands;

/// <summary>
/// データグリッド用ViewModelクラス
/// </summary>
public class DataGridVM<TItem> : UIElementVM {

#region コンストラクタ

/// <summary>
/// コンストラクタ
/// </summary>
public DataGridVM() {
/* 省略 */

//各コマンド
this.cellClickedCommand_ = new RelayCommand(ExecuteCellClickedCommand);
this.cellChangedCommand_ = new RelayCommand(ExecuteCellChangedCommand);
this.selectionChangedCommand_ = new RelayCommand(ExecuteSelectionChangedCommand);
this.doubleClickedCommand_ = new RelayCommand(ExecuteDoubleClickedCommand);
return;
}

#endregion


#region プロパティ(コマンド)

/// <summary>セルクリック用コマンド</summary>
private RelayCommand cellClickedCommand_;

/// <summary>
/// セルクリック用コマンドを取得します。
/// </summary>
public ICommand CellClickedCommand {
get {return cellClickedCommand_;}
}

/// <summary>カレントセル変更用コマンド</summary>
private RelayCommand cellChangedCommand_;

/// <summary>
/// カレントセル変更用コマンドを取得します。
/// </summary>
public ICommand CellChangedCommand {
get {return cellChangedCommand_;}
}

/// <summary>セル選択変更用コマンド</summary>
private RelayCommand selectionChangedCommand_;

/// <summary>
/// セル選択変更用コマンドを取得します。
/// </summary>
public ICommand SelectionChangedCommand {
get {return selectionChangedCommand_;}
}

/// <summary>セルダブルクリック用コマンド</summary>
private RelayCommand doubleClickedCommand_;

/// <summary>
/// セルダブルクリック用コマンドを取得します。
/// </summary>
public ICommand CellDoubleClickedCommand {
get {return doubleClickedCommand_;}
}

#endregion

#region UIElementVMメンバ

/// <summary>
/// 内部リソースを破棄します。
/// </summary>
/// <param name="disposing">false:アンマネージドのみ破棄します。</param>
protected override void OnDispose(bool disposing) {
if (this.IsDisposed) {
return;
}

base.OnDispose(disposing);

if (disposing) {
//後始末
if (cellClickedCommand_ != null) {
cellClickedCommand_.Clear();
cellClickedCommand_ = null;
}

if (doubleClickedCommand_ != null) {
doubleClickedCommand_.Clear();
doubleClickedCommand_ = null;
}

if (cellChangedCommand_ != null) {
cellChangedCommand_.Clear();
cellChangedCommand_ = null;
}

if (selectionChangedCommand_ != null) {
selectionChangedCommand_.Clear();
selectionChangedCommand_ = null;
}
}
}

#endregion

#region コマンド実行用メソッド

/// <summary>
/// セルクリック用コマンド実行メソッド
/// </summary>
/// <param name="parameter">コマンドパラメータ</param>
private void ExecuteCellClickedCommand(object parameter) {
return;
}

/// <summary>
/// カレントセル変更用コマンド実行メソッド
/// </summary>
/// <param name="parameter">コマンドパラメータ</param>
private void ExecuteCellChangedCommand(object parameter) {
return;
}

/// <summary>
/// セル選択変更用コマンド実行メソッド
/// </summary>
/// <param name="parameter">コマンドパラメータ</param>
private void ExecuteSelectionChangedCommand(object parameter) {
return;
}

/// <summary>
/// セルダブルクリック用コマンド実行メソッド
/// </summary>
/// <param name="parameter">コマンドパラメータ</param>
private void ExecuteDoubleClickedCommand(object parameter) {
return;
}

#endregion
}
}
これをXAML側で添付プロパティと連携させる。
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tw="http://schemas.tawamuredays.jp/wpf/gui"
Title="MainWindow" Height="350" Width="525">
<tw:TawamureContents>
<DataGrid DataContext="{Binding BookmarkListVM}"
ItemsSource="{Binding ItemsSource}"
tw:DataGridExtender.CellChangedCommand="{Binding CellChangedCommand}"
tw:DataGridExtender.CellClickedCommand="{Binding CellClickedCommand}"
tw:DataGridExtender.SelectionChangedCommand="{Binding SelectionChangedCommand}"
tw:DataGridExtender.CellDoubleClickedCommand="{Binding CellDoubleClickedCommand}"
CanUserAddRows="True">
<DataGrid.Columns>
<--省略-->
</DataGrid.Columns>
</DataGrid>
</tw:TawamureContents>
</Window>
これでセルのイベント発生に対して、指定のコマンドが実行されるようになる。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

レアな例外で落ちた。

仕事上で初めて出会った例外。レアな感じので、メモっておこうと。
using System;
using System.Collections.Generic;

namespace TawamureDays {

/// <summary>
/// 基底クラス
/// </summary>
/// <typeparam name="T">アイテムの型宣言</typeparam>
public class BaseClass<T> {

/// <summary>
/// コンストラクタ
/// </summary>
public BaseClass() {
ItetaredList = new List<T>(10);
}

/// <summary>リスト</summary>
public List<T> ItetaredList {get; set;}

/// <summary>
/// リスト内のアイテムを取得するためのシーケンスを取得します。
/// </summary>
/// <returns>シーケンス</returns>
public virtual IEnumerable<T> GetItems() {
foreach (var elem in ItetaredList) {
yield return elem;
}
}

}

/// <summary>
/// 継承したサブクラス(落ちる方)
/// </summary>
public class SubClass1 : BaseClass<string> {

#region コンストラクタ

/// <summary>
/// コンストラクタ
/// </summary>
public SubClass1() : base() {
}

#endregion

#region BaseClass<string>メンバ

/// <summary>
/// リスト内のアイテムを取得するためのシーケンスを取得します。
/// </summary>
/// <returns>シーケンス</returns>
public override IEnumerable<string> GetItems() {
foreach (var elem in base.GetItems()) {
yield return elem;
}
}


#endregion
}
}
以下のコードは、確実に落ちる。

var subClass = new SubClass1();
subClass.ItetaredList.Add("1");
subClass.ItetaredList.Add("2");
subClass.ItetaredList.Add("3");
subClass.ItetaredList.Add("4");

foreach (var elem in subClass.GetItems()) {
Console.WriteLine(elem.ToString());
}
仕事で書いたコードは、もっと複雑なんだけど、落ちる箇所を抜粋すると、だいたいこんな感じ。
発生する例外は、BadImageFormatExceptionというやつ。BadImageFormat?画像系?とか思ったけど、違うっぽい。

この例外は、ダイナミック リンク ライブラリ (.DLL ファイル) または実行可能ファイル (.exe ファイル) のファイル形式が、共通言語ランタイムで予期されている形式に準拠していない場合にスローされます。 特に、この例外は次の条件に該当する場合にスローされます。

なんかこう、発生した例外が自分のイメージと違うなぁとか思ってしまった。
基底クラスはGeneric、継承クラスは非Genericなときに、baseメソッドを呼び出すのはまずいのかもしれない。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

カスタムMarkupExtensionを使った。使わずにはいられない。

System.Windows.Markup.MarkupExtensionクラスは、WPFでよく使われているクラスの1つだ。TemplateBindingやStaticResourceもその1つだったりする。
TemplateBindingExtension Class
StaticResourceExtension Class
日本語的には「マークアップ拡張」と呼ばれている。仕事では、多言語対応に使った。コードから指定言語のキャプション(文字列)を取得している。
参考にしたのは↓の記事。
Localization of a WPF application using a custom MarkupExtension
で、もう1つ、便利なマークアップ拡張があったので、取り入れてみた。
Binding to View Model properties in Data Templates. The RootBinding Markup Extension
このマークアップのキモは、Bindingのソースが必ずルートとなるコントロールのDataContext(ViewModel)となる事。これは、親となるコントロールのDataContext設定に影響されないという事だ。
これで思いついたのが、DataGridComboBoxColumnに使う事。
DataGridComboBoxColumnを使うとき、ItemsSourceの扱いについて困る事が多いんだけど、このマークアップを使う事で比較的簡単になる。使ってみるとその便利さがわかる。
続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

WeakEventManagerの使いドコロ

【WPF】変更通知をIWeakEventListenerでリッスン!
WeakEventManager Class
WeakEventManagerクラス:WeakEvent パターンで使用されるイベント マネージャの基本クラス
 あるイベントを発生させるソースオブジェクトに対し、IWeakEventListenerインターフェイスを実装するクラスオブジェクトをリスナーとして登録したり、削除したりするクラス。
○使いドコロ。
・イベントを発生するオブジェクトが、リスナのライフサイクルとは別な時。
 イベントを発生させるオブジェクトは基本的にアプリで1つ。画面に1つとかではない。
 要は、シングルトン的なオブジェクトであるとき。
□実装例1
 イベントソース:ユーザ情報を管理するクラスインスタンス。
 イベント:ユーザが切り替わった。あるいは再ログインした。
 リスナ:現在起動しているウィンドウ。起動すれば、リスナになり、終了時にリスナから削除される。
□実装例2
 イベントソース:言語情報を管理するクラスインスタンス。
 イベント:言語設定が切り替わった(例:日本語→英語)
 リスナ:言語切り替えによって、表示文字列を変更する必要のあるオブジェクトすべて。

後、考えられるのは…。
 イベント:テーマを切り替えたり、スキンを換えた。
 リスナ:テーマやスキンが適用されるVisual(あるいはUIElement)系オブジェクト。
とか。
 イベント:「ツール」→「設定」でアプリ設定が変更された
 リスナ:設定変更の影響を受けるオブジェクトすべて。
かなぁ。リスナになろうと思ったら、IWeakEventListenerを実装する必要があるのがめんどくさいところかな。それ用のクラスを作ってラップするというのも手かな↓。
A Generic IWeakEventListener Implementation
イベントハンドラ(メソッド)をリスナにしようという感じ。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

スタイル定義(リソース)って上から順に適用するんだね。

自分が拡張したコントロールに基底コントロールのスタイルをデフォルトで適用してくれないかと思った。
例えば、TextBoxクラスを拡張したとする。
using System.Windows;
using System.Windows.Controls;

namespace TawamureDays {

/// <summary>
/// 拡張TextBox
/// </summary>
public class CustomTextBox : TextBox {
}
}
これを使って配置しても、CustomTextBoxのTextBoxのスタイル適用がされていないように見える。
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TawamureDays"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBox">
<Setter Property="Background" Value="AliceBlue"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Black"/>
</Style>
</StackPanel.Resources>
<TextBox />
<local:CustomTextBox/>
</StackPanel>
</Window>

20121015_1
じゃあ、何も設定されていないのか?という事になると、どうも違うらしい。
カスタムコントロールでよくある実装をしてみた。
    /// <summary>
/// 拡張TextBox
/// </summary>
public class CustomTextBox : TextBox {

#region staticコンストラクタ

/// <summary>
/// staticコンストラクタ
/// </summary>
static CustomTextBox() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomTextBox),
new FrameworkPropertyMetadata(typeof(CustomTextBox)));

return;
}

#endregion
}
これをビルドして実行すると、何も表示されなくなる。
20121015_2
なので、TextBoxのスタイルを完全に無視しているわけではない感じ。
Styleをキー無しで定義すると、それがデフォルトとして扱われるんだけど、localでデフォルトのスタイルを定義しても、既に適用されているようで、適用した後にその定義を上書きしても反映されないようだ。
自分がlocalで定義したTextBoxのスタイルをCustomTextBoxに引き継がせたい時は↓のようにする。
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TawamureDays"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBox">
<Setter Property="Background" Value="AliceBlue"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Black"/>
</Style>
<Style TargetType="{x:Type local:CustomTextBox}"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="BorderBrush" Value="Red"/>
</Style>
</StackPanel.Resources>
<TextBox />
<local:CustomTextBox/>
</StackPanel>
</Window>

20121015_3
WPF Inspectorで見るとこんな感じ。
20121015_5

この定義の順番ってのは結構大事。定義の順序を変えてみる。
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TawamureDays"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type local:CustomTextBox}"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="BorderBrush" Value="Red"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="AliceBlue"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Black"/>
</Style>
</StackPanel.Resources>
<TextBox />
<local:CustomTextBox/>
</StackPanel>
</Window>

20121015_4
localのStyleではなく、標準のTextBoxスタイルを引き継ぐようだ。上から定義した順に読み込んで適用していくんだね。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

Commandの実装を変える必要が出た。

MVVMに必須なCommandクラスを作る。で、CanExecuteChangedイベント系のところを以下のように実装した。

/// <summary>実行可否変更イベント</summary>
public event System.EventHandler CanExecuteChanged {
add {CommandManager.RequerySuggested += value;}
remove {CommandManager.RequerySuggested -= value;}
}

/// <summary>
/// コマンドの実行可否状態が変更された事を知らせるイベントを発生させます。
/// </summary>
public void RaiseCanExecuteChanged() {
CommandManager.InvalidateRequerySuggested();
return;
}
この実装、WPF4.5では上手く動作しない現象が発生した。WPF4.5とWPF4で、コマンド系のCanExecute周辺の実装が変更されたらしい。
WPF: ICommand CanExecuteChanged behaviour change in .NET 4.5
どうも、CanExecuteChangedイベントが発生した時、ハンドラ側に渡す第1引数(sender)が、ICommand以外だったり、nullだったりすると、更新してくれなくなったらしい。
そして、上記実装がそれに当たる感じ。ただ、動作したりしなかったりするんだよな。要するに、.NET4.5上では、動作が不安定になってしまった。
↓調査、参考サイト
WPF – コマンドのCanExecute強制自動再評価について
CommandManager Class
↑から引用。

Provides command related utility methods that register CommandBinding and InputBinding objects for class owners and commands, add and remove command event handlers, and provides services for querying the status of a command.

説明分によれば、CommandBindingやInputBindingのためのクラスであり、ICommand実装のCommandの為とは書いていない。WPF4.5上で動作を考えるなら、上の実装を修正する必要がありそうだ…。
安易な実装の方に飛びつくべきではなかったなOrz。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4.5 | コメント: 0 | トラックバック: 0

作ったら実際に使ってみないと何とも言えない。

DataGrid用のViewModelを作ったので、早速使ってみる。仕事でそうだったんだけど、こういうのは実際に使ってみてナンボなんだ。それも自分以外の人に。他人に使ってもらって、バグとか問題点とかを挙げてもらう事で継続的なブラッシュアップができる。もちろん、そういう事(他人にデバッグしてもらう事)を前提に実装するという意味ではない。
○DataGridのアイテムとなるクラス
namespace TawamureDays {

using TawamureDays.ViewModels;

/// <summary>
/// ブックマークリストアイテム用ViewModelクラス
/// </summary>
public class BookmarkVM : BaseViewModel {

#region プロパティ

/// <summary>URL</summary>
private string url_;

/// <summary>
/// URLを取得|設定します。
/// </summary>
public string Url {
get {return url_;}
set {
if (url_ != value) {
url_ = value;
this.NotifyPropertyChanged("Url");
}
}
}

/// <summary>タイトル</summary>
private string title_;

/// <summary>
/// タイトルを取得|設定します。
/// </summary>
public string Title {
get { return title_; }
set {
if (title_ != value) {
title_ = value;
this.NotifyPropertyChanged("Title");
}
}
}

#endregion
}
}
名前の通り、ブックマーク系の情報を持たせる為のクラス。とりあえず、URLとタイトルだけ。チェック無し。
○ウィンドウ用ViewModel
using System;
using System.Windows;

namespace TawamureDays {

using TawamureDays.ViewModels;

/// <summary>
/// ウィンドウ用ViewModelクラス
/// </summary>
public class MainWindowVM : BaseWindowVM {
}
}
DataGrid用VM型のプロパティを追加して、コンストラクタでインスタンスを生成する。

#region プロパティ

/// <summary>
/// ブックマークリストを取得します。
/// </summary>
public DataGridVM<BookmarkVM> BookmarkListVM {
get; private set;
}

#endregion

#region コンストラクタ

/// <summary>
/// コンストラクタ
/// </summary>
public MainWindowVM() {
this.BookmarkListVM = new DataGridVM<BookmarkVM>();
//各アイテムのプロパティ値変更イベントにハンドラを登録します。
this.BookmarkListVM.ItemPropertyChanged +=
new System.ComponentModel.PropertyChangedEventHandler(
BookmarkListVM_ItemPropertyChanged);
return;
}

#endregion
各アイテムのプロパティ値変更イベント用ハンドラ

#region イベントハンドラ

/// <summary>
/// ブックマークリストアイテムプロパティ値変更イベントハンドラ
/// </summary>
/// <param name="sender">イベントハンドラ</param>
/// <param name="e">イベント引数</param>
private void BookmarkListVM_ItemPropertyChanged(object sender,
System.ComponentModel.PropertyChangedEventArgs e) {
var itemVM = sender as BookmarkVM;
var changedProperty = e.PropertyName;
return;
}

#endregion
単に発生元のアイテムと変更されたプロパティを変数に格納したいだけのコード。
○XAML
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tw="http://schemas.tawamuredays.jp/wpf/gui"
Title="MainWindow" Height="350" Width="525">
<tw:TawamureContents>
<DataGrid DataContext="{Binding BookmarkListVM}"
ItemsSource="{Binding ItemsSource}">
<DataGrid.Columns>
<DataGridTextColumn Header="URL"
tw:DataGridExtender.ColumnId="ColUrl"
Binding="{Binding Url}"
Width="4*"
/>
<DataGridTextColumn Header="TITLE"
tw:DataGridExtender.ColumnId="ColTitle"
Binding="{Binding Title}"
Width="6*"
/>
</DataGrid.Columns>
</DataGrid>
</tw:TawamureContents>
</Window>
DataGridに対する設定が少ない。単にApp.xaml側で設定しただけ。

<Style TargetType="DataGrid">
<Setter Property="AutoGenerateColumns" Value="False"/>
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Standard"/>
</Style>
とりあえず、列生成無しとUI仮想化設定のみ。
○コード側
public partial class MainWindow : Window {

public MainWindow() {
InitializeComponent();
this.DataContext = new MainWindowVM();
return;
}
}
DataContextに設定しないと意味が無い。
○実行
20121007_1
CanUserAddRows=true、CanUserDeleteRows=trueなので、ItemsSourceに何も設定しなくても、空の1行が表示されている。
画面から追加も削除もできる。
追加は、編集モード(F2キー)にして、Enterキー押すと、下に一行追加される。
削除は、行ヘッダをクリックして、Delキーを押す。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

DataGrid用ViewModelクラスを実装した(弐)。

DataGrid用ViewModelクラスを実装した(壱)。から更に進めていく。
アイテムソースをObservableCollectionEx<T>型にすることで、アイテムの増減を知ることができる。でも、各アイテムのプロパティ値の変更までは検知できない。これを可能にしようと思えば、各アイテムに変更通知機能があるとき(INotifyPropertyChangedインターフェイスを実装している)に限り、ProprtyuChangedイベントにハンドラを登録する必要がある。もちろん、リストから削除されるアイテムに対しては、登録したハンドラを削除する必要がある。削除しないと、当然の様にリークの因子となって残り続ける。

/// <summary>
/// コンストラクタ
/// </summary>
public DataGridVM() {
this.ItemsSource = new ObservableCollectionEx<TItem>();
this.ListViewSource = CollectionViewSource.
GetDefaultView(this.ItemsSource) as ListCollectionView;
//コレクションの変更イベント用ハンドラ
this.ItemsSource.CollectionChanged +=
new NotifyCollectionChangedEventHandler(
ItemsSource_CollectionChanged);
//コレクションクリアイベント用ハンドラ
this.ItemsSource.CleaningItems +=
new EventHandler<CleaningItemsEventArgs<TItem>>(
ItemsSource_CleaningItems);
//Itemの変更通知イベント用ハンドラ(自分用)
this.internalItemPropertyChangedHandler_ =
new PropertyChangedEventHandler(GridItem_PropertyChanged);

return;
}

#region privateメンバ

/// <summary>(内部用)Itemプロパティ変更イベント用ハンドラ</summary>
private PropertyChangedEventHandler internalItemPropertyChangedHandler_;

#endregion

コレクション変更イベントハンドラ

/// <summary>
/// アイテムソースコレクション変更イベントハンドラ
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private void ItemsSource_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e) {
//INotifyPropertyChangedインターフェイス実装型のオブジェクト
//が追加や削除された時のみ、プロパティ値変更イベントを登録したり、
//削除したりします。
if (e.OldItems != null && e.OldItems.Count > 0) {
foreach (var oldItem in e.OldItems.OfType<INotifyPropertyChanged>()) {
oldItem.PropertyChanged -= this.itemPropertyChangedHandler_;
}
}

if (e.NewItems != null && e.NewItems.Count > 0) {
foreach (var newItem in e.NewItems.OfType<INotifyPropertyChanged>()) {
newItem.PropertyChanged += this.itemPropertyChangedHandler_;
}
}

return;
}
追加されるアイテムの変更通知イベントにハンドラを登録し、削除されるアイテムの変更通知イベントからハンドラを削除する。クリア(Clear)でもこのイベントは発生するけど、e.OldItemsは空なので、クリア用イベントハンドラが必要になる。

/// <summary>
/// アイテムクリアイベントハンドラ
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private void ItemsSource_CleaningItems(object sender,
CleaningItemsEventArgs<TItem> e) {
//クリアされた時も、イベントハンドラの削除は怠れません。
if (e.ClearedList != null && e.ClearedList.Count > 0) {
foreach (var oldItem in e.ClearedList.OfType<INotifyPropertyChanged>()) {
oldItem.PropertyChanged -= this.itemPropertyChangedHandler_;
}
}
return;
}
こうすることで、リスト内のアイテムのプロパティ値が変更されたら、必ずGridItem_PropertyChangedメソッドが呼び出される事になる。

/// <summary>
/// DataGrid アイテム プロパティ変更イベントハンドラ(内部用)
/// </summary>
/// <param name="sender">変更発生元</param>
/// <param name="e">イベント引数</param>
private void GridItem_PropertyChanged(object sender,
PropertyChangedEventArgs e) {
if (this.IsDisposed) {
return;
}

//アイテムのプロパティ変更を外部(から登録さたリスナ)へ通知します。
if (this.itemPropertyChangedHandler_ != null) {
foreach (PropertyChangedEventHandler handler in
this.itemPropertyChangedHandler_.GetInvocationList()) {
handler(sender, e);
}
}
}

/// <summary>アイテムプロパティ変更イベント用ハンドラ</summary>
protected PropertyChangedEventHandler itemPropertyChangedHandler_;

/// <summary>
/// アイテムプロパティ変更イベント<br/>
/// DataGridのItemのプロパティ値変更を通知するイベントです。<br/>
/// </summary>
public event PropertyChangedEventHandler ItemPropertyChanged {
add {3itemPropertyChangedHandler_ += value;}
remove {itemPropertyChangedHandler_ -= value;}
}
当クラスを使い、アイテムのプロパティ値変更を検知したい時は、ItemPropertyChangedイベントにハンドラを登録するだけとなる。終了時の処理も忘れずに実装する。

/// <summary>
/// 内部リソースを破棄します。
/// </summary>
/// <param name="disposing">false:アンマネージドのみ破棄します。</param>
protected override void OnDispose(bool disposing) {
if (this.IsDisposed) {
return;
}

base.OnDispose(disposing);

if (disposing) {
if (this.ItemsSource != null) {
this.ItemsSource.CollectionChanged -=
new NotifyCollectionChangedEventHandler(
ItemsSource_CollectionChanged);
if (this.ItemsSource.Count > 0) {
this.ItemsSource.Clear();
}

this.ItemsSource.CleaningItems -=
new EventHandler<CleaningItemsEventArgs<TItem>>(
ItemsSource_CleaningItems);
this.ItemsSource = null;
}

if (this.itemPropertyChangedHandler_ != null) {
foreach (PropertyChangedEventHandler handler in
this.itemPropertyChangedHandler_.GetInvocationList()) {
this.itemPropertyChangedHandler_ -= handler;
}
}

this.ListViewSource = null;
this.internalItemPropertyChangedHandler_ = null;
}
return;
}
WPFにしても、Windows.Formsにしても、徹底的にやってはじめてメモリリークを抑制できると思う。
続く。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

DataGrid用ViewModelクラスを実装した(壱)。

DataGrid用のViewModelを作ったよ。(壱)と書いたけど、いきなり全部を実装(メモ)するのは無理なので、段階的に踏んでいく。
まず、「ガワ」となるクラスを作る。
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;

namespace TawamureDays.ViewModels {

using TawamureDays.Container;

/// <summary>
/// データグリッド用ViewModelクラス
/// </summary>
public class DataGridVM<TItem> : UIElementVM {
}
}

かならず必要になるのが、DataGridのItemsSourceの元となるアイテムリスト(コレクション)。これは、前に作ったObserbaleCollectionExを使うことにする。更に、このアイテムリストのビューとなるオブジェクト。この2つをまず用意する。

#region プロパティ

/// <summary>
/// アイテムソースを取得します。
/// </summary>
public ObservableCollectionEx<TItem> ItemsSource {
get; protected set;
}

/// <summary>
/// ビューをを取得します。
/// </summary>
public ListCollectionView ListViewSource {
get; protected set;
}

#endregion

setアクセサをprotectedにして、外部からはsetできなくした。インスタンスを勝手に入れ替えさせない為の処置だったりする。このコレクションを持ちたいが為に、クラス本体がジェネリック型になっている。次に、コンストラクタでこれらのプロパティのインスタンスを生成する。

#region コンストラクタ

/// <summary>
/// コンストラクタ
/// </summary>
public DataGridVM() {
this.ItemsSource = new ObservableCollectionEx<TItem>();
this.ListViewSource = CollectionViewSource.
GetDefaultView(this.ItemsSource) as ListCollectionView;
return;
}

#endregion

これだけでも、一々自分で用意する手間が省けるかな。なお、仕事ではデータ仮想化を組み込んでみているので、ListCollectionViewクラスを継承した仮想化用のビュークラスを使っている。
最後。作ったら、必ず破棄のコードを書く。

#region UIElementVMメンバ

/// <summary>
/// 内部リソースを破棄します。
/// </summary>
/// <param name="disposing">false:アンマネージドのみ破棄します。</param>
protected override void OnDispose(bool disposing) {
if (this.IsDisposed) {
return;
}

base.OnDispose(disposing);

if (disposing) {
if (this.ItemsSource != null) {
if (this.ItemsSource.Count > 0) {
this.ItemsSource.Clear();
}

this.ItemsSource = null;
}

this.ListViewSource = null;
}
return;
}

#endregion
これはもはや「お約束」だ。自分以外だれが開放するというのか、いやいない(反語)。
続く。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

仕事で今すぐは使えないので

VS2012、C#5.0、WPF4.5がリリースされた!と言って、はいそうですかとバージョンアップができないのが仕事。
そもそもユーザの環境にランタイム入れてもらわないといけないし。
C#4で困っているところなんて、特にないし。
ユーザのPC環境が、Windows8がメインになる頃を見据えておくしかできない。

WPF4.5の新機能
↑の英語版
What's New in WPF Version 4.5 RC
グルーピング使用時のパフォーマンス改善はいいなぁ。どこまで改善されているかが気になるところ。

.NET 4.5 Readme
Windows8には、.NET4.5のLanguage Packは入らないようだ…。代わりに、Windows8のLanguagePackを入れろと書いてある。

.NET 4.5 基本クラス ライブラリの新機能
非同期系のasync, awaitってのを使ってみたいな。バグなく使うのは難しいかもしれないけど。

アプリケーションの.NET Framework 4.5への移行: 廃止された型と新しい型

ついでのWCF
WCF 4.5 新機能

↑の英語版
What's New in Windows Communication Foundation 4.5
WCF通信で、バイナリ圧縮ができるようになったのか。前はIIS7の圧縮機能に頼るだけだったので、これ要望多かったのかも。WCFって、XMLかJSONなんだけど、プロパティ名やら名前空間やらでやたらサイズを食っている。メガ後半やギガクラスのネット環境であれば、特に問題ないけど、海外とかと通信しようと思うと、距離的な事や、そのお国の事情で、そうは行かないときがある。そういう時にはいいかもしれない。

もうそろそろ的なWindows8。なんちゃらストアに登録されるには、色々な要件があるようで。
Windows 8 アプリの認定の要件
Apple Storeの二番煎じ感満載だろうと、判定がNGではストアに登録してくれないよという事か。

パフォーマンス云々は、どんなにバージョンアップしても変わらない。
WPF アプリケーションのパフォーマンスの最適化

その他。
.NET Framework Blog
.NET Framework 4.5
C#5の新機能
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4.5 | コメント: 0 | トラックバック: 0

LINQのToDictionaryと似て非なるメソッドを作った。

LINQには、ToDictionaryというリスト型をディクショナリ型に変換する為のメソッドがある。
using System.Linq;

...
var numbers = new []{1, 2, 3, 4, 5, 6, 7};
var numberMap = numbers.ToDictionary(x => x);

任意の値をキーにして、該当する要素を取得したい時によく使う。ただし、このメソッドは、キーは重複できないというルールがあるようで、重複していると例外を発生させる。
 仕事では、この仕様が非常に不便だった。例えば、帳票等のデータを内部に持つとき、ヘッダ:明細=1:Nで設計することが多い。というより、これが普通なんだ。複数の帳票(ヘッダ)に関する明細を、DBのテーブルから一気に(1クエリで)検索し、ヘッダ毎にグルーピングしたい時に、このメソッドが使えなくて、悶えてしまった。
結局は、独自にメソッドを作ってそれで凌ぐ事になった。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

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

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

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

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

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