数値入力用のDataGridColumnも欲しくなる。

数値だけのTextBoxだってやれるはずだ(壱)
数値だけのTextBoxだってやれるはずだ(弐)
数値だけのTextBoxだってやれるはずだ(参)
数値だけのTextBoxだってやれるはずだ(四)
で、TextBoxに数値専用の機能(添付プロパティ)を実装した。でも、開発する画面によっては、DataGrid内の列に対して直接編集をするような事もある。当たり前のように、金額や数量の様に、数値のみ入力を許可したい要件というものが出てくる。どんなご時世だろうと、ユーザさんの中にはパソコンに対するアレルギーを持っている人もいるので、できるだけ入力ミスをさせない(できない)ように作ることもそれなりに大切なんだ。
数値入力専用のDataGridColumnなので、

○表示や編集時、右詰めで表示する。
○アルファベットやカナ等、数値と記号(マイナス(-)やドット(.))以外の入力をさせない。

という感じで作る事にする。
右詰め表示については、↓の記事で書いていたので、これと同じような方法で実装してみる。
DataGridTextColumnで右詰めにしたい時が多々ある。

・DataGridColumnを継承してクラスを作る。
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace TawamureDays.Controls {

using TawamureDays.Behaviors;//**Behavior系のクラスをここに
using TawamureDays.Converters;//コンバータ系のクラス

/// <summary>
/// TawamureDays 数値入力専用DataGridColumn
/// </summary>
public saeld class DataGridNumericColumn : DataGridTextColumn {

#region コンストラクタ

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

#endregion

#region 依存関係プロパティ

/// <summary>
/// 垂直方向の位置を取得|設定します。
/// </summary>
public VerticalAlignment VerticalAlignment {
get {return (VerticalAlignment)GetValue(VerticalAlignmentProperty);}
set {SetValue(VerticalAlignmentProperty, value);}
}

/// <summary>垂直方向の位置</summary>
public static readonly DependencyProperty VerticalAlignmentProperty =
DependencyProperty.Register("VerticalAlignment", typeof(VerticalAlignment),
typeof(DataGridNumericColumn),
new UIPropertyMetadata(VerticalAlignment.Center));

#endregion

#region プロパティ

/// <summary>
/// 整数部桁数を取得|設定します。
/// </summary>
public int IntegerPartDigit {
get; set;
}

/// <summary>
/// 小数部桁数を取得|設定します。
/// </summary>
public int DecimalPartDigit {
get; set;
}

/// <summary>
/// 最大値を取得|設定します。
/// </summary>
public decimal? MaxValue {
get; set;
}

/// <summary>
/// 最小を取得|設定します。
/// </summary>
public decimal? MinValue {
get; set;
}

#endregion

#region DataGridTextColumnメンバ

/// <summary>
/// 列の Binding プロパティ値にバインドされた読み取り専用の TextBlock コントロールを取得します。<br/>
/// 表示用<br/>
/// </summary>
/// <remarks>
/// 値が入る前なので、ここで値の編集とかはできない。<br/>
/// </remarks>
/// <param name="cell">生成された要素を格納するセル。</param>
/// <param name="dataItem">目的のセルを格納している行によって表されるデータ項目。</param>
/// <returns>列の Binding プロパティ値にバインドされた新しい読み取り専用のテキスト ブロック コントロール</returns>
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) {
var txtBlock = (TextBlock)base.GenerateElement(cell, dataItem);
txtBlock.HorizontalAlignment = HorizontalAlignment.Right;
txtBlock.VerticalAlignment = this.VerticalAlignment;
return txtBlock;
}

/// <summary>
/// 列の Binding プロパティ値にバインドされた TextBox コントロールを取得します。
/// </summary>
/// <remarks>
/// 値が入る前なので、ここで値の編集とかはできない。<br/>
/// </remarks>
/// <param name="cell">生成された要素を格納するセル。</param>
/// <param name="dataItem">目的のセルを格納している行によって表されるデータ項目。</param>
/// <returns>列の Binding プロパティ値にバインドされた新しい TextBox コントロール</returns>
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) {
var textBox = (TextBox)base.GenerateEditingElement(cell, dataItem);

//文字寄せを指定します。
textBox.TextAlignment = TextAlignment.Right;//強制的に右寄せ
textBox.VerticalAlignment = this.VerticalAlignment;//垂直方向はユーザ設定に
//数値のみ許可
TextBoxBehavior.SetAllowOnlyNumeric(textBox, true);
//整数部桁数
TextBoxBehavior.SetIntegerPartDigit(textBox, this.IntegerPartDigit);
//小数部桁数
TextBoxBehavior.SetDecimalPartDigit(textBox, this.DecimalPartDigit);
//最小値
TextBoxBehavior.SetMinValue(textBox, this.MinValue);
//最大値
TextBoxBehavior.SetMaxValue(textBox, this.MaxValue);
return textBox;
}

#endregion
}
}
依存関係プロパティにしていないのは、動的に変えることはないだろうと踏んでの事なので、条件によってかえたり、Trigger等に使いたいときは、依存関係プロパティに変更する必要がある。
実際に使ってみる。
<Window x:Class="TawamureDays.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tw="http://schemas.tawamureworks.jp/gui"
Title="Tawamure.WPFTest" Height="300" Width="300"
tw:UIElementBehavior.OnLoadedCommand="{Binding OnLoadedCommand}">
<DockPanel>
<tw:TwContents>
<StackPanel>
<DataGrid CanUserAddRows="True"
AutoGenerateColumns="False"
ItemsSource="{Binding DataSource}">
<DataGrid.Columns>
<tw:DataGridNumericColumn Binding="{Binding Data, Mode=TwoWay}"
Header="数値"
IsReadOnly="False"
IntegerPartDigit="9"
DecimalPartDigit="3"
Width="*"
/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</tw:TwContents>
</DockPanel>
</Window>

コードビハインド
namespace TawamureDays {

using TawamureDays.Commands;
using TawamureDays.ViewModels;

/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window {

#region コンストラクタ

/// <summary>
/// コンストラクタ
/// </summary>
public Window1() {
InitializeComponent();
this.DataSource = new List<DataObject>(0);
this.DataContext = this;
return;
}

#endregion

#region コマンド

/// <summary>
/// ItemsSourceたるリストを取得|設定します。
/// </summary>
public IList<DataObject> DataSource {
get; set;
}

#endregion
}

/// <summary>
/// データ用クラス
/// </summary>
public saaled class DataObject : TwBaseViewModel {

public decimal? data_;

/// <summary>
/// 数値データ
/// </summary>
public decimal? Data {
get {return data_;}
set {
if (data_ != value) {
data_ = value;
this.RaisePropertyChanged(() => Data);
}
}
}
}
}
20130316_1
画像をアップしたところで、その実際がわかるわけではないのがつらいところ…。
一応、これで数値専用の列ってやつができたんだけど、利用しているWorkerクラスがこのDataGridColumnでの利用に対応していなくて、きっちりメモリリークしそうな感じだった。基本、DataGridColumn内の編集時のコントロール(今回はTextBox)は、Load→Unloadedとなって、WindowsのClosedイベント等は発生しない。しかも、使いまわされる可能性もあるので、同じセルの編集時に同じTextBoxのインスタンスが使われる保証もない。
現在のWorkerクラスは、Closedの時にDisposeする仕組みとなっているので、非常に都合が悪い。編集モードでTextBoxを出せば出すほどメモリを食べ続ける悪い子になってしまう。なので、Workerクラスの基底としているAttachedPropertyWorkerクラスを改修する必要が出た。
続く。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


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

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

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

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