TextBoxを拡張してユーザーコントロールとして作成しました
以下のプログラムは、数値や日付を入力する TextBox を拡張したユーザーコントロールのサンプルプログラムです。
Enterキーが押された直後に編集後の形式にするようにプログラムを変更しました。
この処理がないと、xamlで定義したCommand="{x:Static local:MyWindow.NumericIn_1}"等の実行部分(.csファイル内)で
((TextBox)e.Source).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
などとして、一度フォーカスを外すして編集後の形式にしなければならないなどの不便な場合があり、PreviewKeyDownの処理を追加しました
.Net Framework 4.8 用と .NET 8 用のコードを用意しました
新しいプロジェクトとして作成するとき、テンプレートは WPF ユーザー コントロール ライブラリ を選択して作成します。
プロジェクト名が UserControl1 のとき xaml ファイルは
<UserControl x:Class="WpfControlLibrary1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfControlLibrary1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
</Grid>
</UserControl>
の様になっているので、最初の UserControl を継承するクラス TextBox に変更し mc:Ignorable="d" の部分より後を削除します。数値入力用のTextBoxを作っているなら、入力時にIMEをOFFにするため以下の様に変更します。
<TextBox x:Class="WpfControlLibrary1.UserControl1"
︙
︙
mc:Ignorable="d"
InputMethod.IsInputMethodEnabled="False" />
実際に使用する場合は、WpfControlLibrary1 2か所を使用環境に合わせて変更します数値入力
using System;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace UserContril // namespaceは使用環境に合わせて変更してください
{
/// <summary>
/// NumericTextBox.xaml の相互作用ロジック
/// </summary>
public partial class NumericTextBox : TextBox
{
public NumericTextBox()
{
InitializeComponent();
this.TextAlignment = TextAlignment.Right;
this.Loaded += OnLoaded;
this.PreviewTextInput += TextBox_OnPreviewTextInput;
this.PreviewKeyDown += TextBox_OnPreviewKeyDown;
this.GotFocus += TextBox_GotFocus;
this.PreviewLostKeyboardFocus += TextBox_PreviewLostKeyboardFocus;
this.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.Text = "";
}
public int IntValue()
{
string txt = this.Text.Replace(",", "");
int int_value;
double double_value;
if (double.TryParse(txt, out double_value))
{
int_value = (int)double_value;
}
else
{
int_value = 0;
}
return int_value;
}
public double DoubleValue()
{
string txt = this.Text.Replace(",", "");
double double_value;
if (!double.TryParse(txt, out double_value))
{
double_value = 0;
}
return double_value;
}
public Decimal DecimalValue()
{
string txt = this.Text.Replace(",", "");
decimal decimal_value;
if (!decimal.TryParse(txt, out decimal_value))
{
decimal_value = 0;
}
return decimal_value;
}
public void SetValue(int? value)
{
String format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "D";
this.Text = ((int)value).ToString(format);
}
else
{
this.Text = "";
}
}
public void SetValue(long? value)
{
String format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "D";
this.Text = ((long)value).ToString(format);
}
else
{
this.Text = "";
}
}
public void SetValue(float? value)
{
String format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "F2";
this.Text = ((float)value).ToString(format);
}
else
{
this.Text = "";
}
}
public void SetValue(double? value)
{
String format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "F2";
this.Text = ((double)value).ToString(format);
}
else
{
this.Text = "";
}
}
public void SetValue(decimal? value)
{
String format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "F2";
this.Text = ((decimal)value).ToString(format);
}
else
{
this.Text = "";
}
}
public void SetValue(string value)
{
String format = Format;
if (String.IsNullOrEmpty(value) == false)
{
try
{
double.TryParse(value, out double dval);
if (String.IsNullOrEmpty(format)) format = "F2";
this.Text = (dval).ToString(format);
}
catch (FormatException)
{
this.Text = "";
}
}
else
{
this.Text = "";
}
}
public String Format { set; get; }
public bool IsNotDisplayZero { set; get; }
private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = new Regex("[^0-9.-]+").IsMatch(e.Text);
}
private void TextBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
// Enterキーが押されたとき、PreviewLostKeyboardFocusを呼んで編集後の形式を表示する
// フォーカスが移動したときまたPreviewLostKeyboardFocusが実行されるが、とりあえずは気にしない
TextBox_PreviewLostKeyboardFocus(sender, e);
}
}
private void TextBox_PreviewLostKeyboardFocus(object sender, RoutedEventArgs e)
{
var s = sender as TextBox;
string txt = s.Text.Replace(",", "");
int int_value;
double double_value;
if (Text.Length > 0)
{
if (double.TryParse(txt, out double_value))
{
int_value = (int)double_value;
}
else
{
e.Handled = true;
return;
}
}
else
{
double_value = 0;
int_value = 0;
if (IsNotDisplayZero == true) return;
}
String sbuf2;
String format = (string)this.Format;
if (format == null) format = "D";
if (Regex.IsMatch(format, "[FN][1-9]*") || format.IndexOf(".") > 0)
{
sbuf2 = double_value.ToString(format);
}
else
{
sbuf2 = int_value.ToString(format);
}
this.Text = sbuf2;
}
private void TextBox_GotFocus(object sender, EventArgs e)
{
var s = sender as TextBox;
s.Text = s.Text.Replace(",", "");
s.SelectAll();
}
private void OnMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// マウスクリックされた場合、他の項目からフォーカスが移動してきたときは全選択
// すでにフォーカスされているときはカーソルの移動
if (this.IsFocused)
{
return;
}
this.Focus();
e.Handled = true;
}
}
}
using System;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace UserControl // namespaceは使用環境に合わせて変更してください
{
/// <summary>
/// NumericTextBox.xaml の相互作用ロジック
/// </summary>
public partial class NumericTextBox : TextBox
{
public String? Format { set; get; } = null;
public bool IsNotDisplayZero { set; get; } = false;
[GeneratedRegex("[^0-9.-]+")]
private static partial Regex DigitRegex();
[GeneratedRegex("[FfNn][1-9]*")]
private static partial Regex FormatRegex();
public NumericTextBox()
{
InitializeComponent();
this.TextAlignment = TextAlignment.Right;
this.Loaded += OnLoaded;
this.PreviewTextInput += TextBox_OnPreviewTextInput;
this.PreviewKeyDown += TextBox_OnPreviewKeyDown;
this.GotFocus += TextBox_GotFocus;
this.PreviewLostKeyboardFocus += TextBox_PreviewLostKeyboardFocus;
this.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.Text = "";
}
public int IntValue()
{
string txt = this.Text.Replace(",", "");
int int_value;
double double_value;
if (double.TryParse(txt, out double_value))
{
int_value = (int)double_value;
}
else
{
int_value = 0;
}
return int_value;
}
public double DoubleValue()
{
string txt = this.Text.Replace(",", "");
double double_value;
if (!double.TryParse(txt, out double_value))
{
double_value = 0;
}
return double_value;
}
public Decimal DecimalValue()
{
string txt = this.Text.Replace(",", "");
decimal decimal_value;
if (!decimal.TryParse(txt, out decimal_value))
{
decimal_value = 0;
}
return decimal_value;
}
public void SetValue(int? value)
{
String? format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "D";
this.Text = ((int)value).ToString(format);
}
else
{
this.Text = String.Empty;
}
}
public void SetValue(long? value)
{
String? format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "D";
this.Text = ((long)value).ToString(format);
}
else
{
this.Text = String.Empty;
}
}
public void SetValue(float? value)
{
String? format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "F2";
this.Text = ((float)value).ToString(format);
}
else
{
this.Text = String.Empty;
}
}
public void SetValue(double? value)
{
String? format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "F2";
this.Text = ((double)value).ToString(format);
}
else
{
this.Text = String.Empty;
}
}
public void SetValue(decimal? value)
{
String? format = Format;
if (value != null)
{
if (String.IsNullOrEmpty(format)) format = "F2";
this.Text = ((decimal)value).ToString(format);
}
else
{
this.Text = String.Empty;
}
}
public void SetValue(string? value)
{
String? format = Format;
if (String.IsNullOrEmpty(value) == false)
{
try
{
double.TryParse(value, out double dval);
if (String.IsNullOrEmpty(format)) format = "F2";
this.Text = (dval).ToString(format);
}
catch (FormatException)
{
this.Text = String.Empty;
}
}
else
{
this.Text = String.Empty;
}
}
private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = DigitRegex().IsMatch(e.Text);
}
private void TextBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TextBox_PreviewLostKeyboardFocus(sender, e);
}
}
private void TextBox_PreviewLostKeyboardFocus(object sender, RoutedEventArgs e)
{
string txt = "";
if (sender is TextBox s)
{
txt = s.Text.Replace(",", "");
}
int int_value;
double double_value;
if (Text.Length > 0)
{
if (double.TryParse(txt, out double_value))
{
int_value = (int)double_value;
}
else
{
e.Handled = true;
return;
}
}
else
{
double_value = 0;
int_value = 0;
if (IsNotDisplayZero == true) return;
}
String sbuf2;
String? format = Format;
if (String.IsNullOrEmpty(format))
{
format = "D";
}
if (FormatRegex().IsMatch(format) || format.IndexOf('.') > 0)
{
sbuf2 = double_value.ToString(format);
}
else
{
sbuf2 = int_value.ToString(format);
}
this.Text = sbuf2;
}
private void TextBox_GotFocus(object sender, EventArgs e)
{
if (sender is TextBox s)
{
s.Text = s.Text.Replace(",", "");
s.SelectAll();
}
}
private void OnMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (this.IsFocused)
{
return;
}
this.Focus();
e.Handled = true;
}
}
}
追加プロパティ
Format | 数値編集書式を指定する(ToStringで使用できる形式) |
IsNotDisplayZero | 入力した値が0の時、何も表示しない(trueまたはfalse ) |
追加メソッド
IntValue() | 内容をintで取得します |
DoubleValue() | 内容をdoubleで取得します |
DecimalValue() | 内容をdecimalで取得します |
SetValue(int) | int型の数値を表示します |
SetValue(long) | long型の数値を表示します |
SetValue(float) | float型の数値を表示します |
SetValue(double) | double型の数値を表示します |
SetValue(decimal) | decimal型の数値を表示します |
SetValue(string) | 数値に変換可能ならば表示します |
日付入力
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace UserControl // namespaceは使用環境に合わせて変更してください
{
/// <summary>
/// DateTextBox.xaml の相互作用ロジック
/// </summary>
public partial class DateTextBox : TextBox
{
public DateTextBox()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.PreviewTextInput += TextBox_OnPreviewTextInput;
this.PreviewKeyDown += TextBox_OnPreviewKeyDown;
this.GotFocus += TextBox_GotFocus;
this.PreviewLostKeyboardFocus += TextBox_PreviewLostKeyboardFocus;
this.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.Text = "";
}
public DateTime? Date()
{
string txt = this.Text;
if (txt.Length > 0)
{
DateTime inDay;
DateTime.TryParse(txt, out inDay);
return inDay;
}
else
{
return null;
}
}
public void SetDate(object? date)
{
if (date != null)
{
if (date is DateTime)
{
if (Format == null)
{
this.Text = ((DateTime)date).ToString("yyyy年MM月dd日");
}
else
{
this.Text = ((DateTime)date).ToString(Format);
}
}
else
{
this.Text = "";
}
}
else
{
this.Text = "";
}
}
public String Format { set; get; }
public bool Default { set; get; }
private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = new Regex("[^0-9.-/]+").IsMatch(e.Text);
}
private void TextBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
// Enterキーが押されたとき、PreviewLostKeyboardFocusを呼んで編集後の形式を表示する
// フォーカスが移動したときまたPreviewLostKeyboardFocusが実行されるが、とりあえずは気にしない
TextBox_PreviewLostKeyboardFocus(sender, e);
}
}
private void TextBox_PreviewLostKeyboardFocus(object sender, RoutedEventArgs e)
{
var s = sender as TextBox;
string txt = s.Text.Replace(".", "/");
DateTime today = DateTime.Today;
String format = "yyyy年MM月dd日";
if(Format != null) { format = Format; };
DateTime inDay;
DateTime testDay;
String dt;
if (txt.Length == 0)
{
if (Default == false)
{
return;
}
this.Text = today.ToString(format);
}
// 日付のみ(1~2桁)の入力において正しい日付の形式にはなっていないので、年と月を補完する
// MM/ddやMM.ddはTryParseで年が補完されるが、同じように処理をする
if (txt.Where(c => c == '/').Count() == 0 && txt.Length <= 2)
{
dt = today.Year + "/" + today.Month + "/" + txt;
if (DateTime.TryParse(dt, out inDay))
{
this.Text = inDay.ToString(format);
}
}
if (txt.Where(c => c == '/').Count() == 1 && txt.Length <= 5)
{
dt = today.Year + "/" + txt;
if (DateTime.TryParse(dt, out inDay))
{
this.Text = inDay.ToString(format);
}
}
if (txt.Where(c => c == '/').Count() == 2 && txt.Length <= 10)
{
if (DateTime.TryParse(txt, out inDay))
{
this.Text = inDay.ToString(format);
}
}
if (DateTime.TryParse(this.Text, out testDay)) return;
e.Handled = true;
}
private void TextBox_GotFocus(object sender, EventArgs e)
{
var s = sender as TextBox;
s.SelectAll();
}
private void OnMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// マウスクリックされた場合、他の項目からフォーカスが移動してきたときは全選択
// すでにフォーカスされているときはカーソルの移動
if (this.IsFocused)
{
return;
}
this.Focus();
e.Handled = true;
}
}
}
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace UserControl // namespaceは使用環境に合わせて変更してください
{
/// <summary>
/// DateTextBox.xaml の相互作用ロジック
/// </summary>
public partial class DateTextBox : TextBox
{
public String? Format { set; get; } = null;
public bool Default { set; get; } = false;
[GeneratedRegex("[^0-9.-/]+")]
private static partial Regex DigitRegex();
public DateTextBox()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.PreviewTextInput += TextBox_OnPreviewTextInput;
this.PreviewKeyDown += TextBox_OnPreviewKeyDown;
this.GotFocus += TextBox_GotFocus;
this.PreviewLostKeyboardFocus += TextBox_PreviewLostKeyboardFocus;
this.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.Text = "";
}
public DateTime? Date()
{
string txt = this.Text;
if (txt.Length > 0)
{
DateTime inDay;
DateTime.TryParse(txt, out inDay);
return inDay;
}
else
{
return null;
}
}
public void SetDate(object? date)
{
if (date != null)
{
if (date is DateTime hizuke)
{
if (Format == null)
{
this.Text = hizuke.ToString("yyyy年MM月dd日");
}
else
{
this.Text = hizuke.ToString(Format);
}
}
else
{
this.Text = "";
}
}
else
{
this.Text = "";
}
}
private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = DigitRegex().IsMatch(e.Text);
}
private void TextBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TextBox_PreviewLostKeyboardFocus(sender, e);
}
}
private void TextBox_PreviewLostKeyboardFocus(object sender, RoutedEventArgs e)
{
string txt = "";
if (sender is TextBox s)
{
txt = s.Text.Replace(".", "/");
}
DateTime today = DateTime.Today;
String format = "yyyy年MM月dd日";
if (Format != null) { format = Format; };
DateTime inDay;
DateTime testDay;
String dt;
if (txt.Length == 0)
{
if (Default == false)
{
return;
}
this.Text = today.ToString(format);
}
if (txt.Where(c => c == '/').Count() == 0 && txt.Length <= 2)
{
dt = today.Year + "/" + today.Month + "/" + txt;
if (DateTime.TryParse(dt, out inDay))
{
this.Text = inDay.ToString(format);
}
}
if (txt.Where(c => c == '/').Count() == 1 && txt.Length <= 5)
{
dt = today.Year + "/" + txt;
if (DateTime.TryParse(dt, out inDay))
{
this.Text = inDay.ToString(format);
}
}
if (txt.Where(c => c == '/').Count() == 2 && txt.Length <= 10)
{
if (DateTime.TryParse(txt, out inDay))
{
this.Text = inDay.ToString(format);
}
}
if (DateTime.TryParse(this.Text, out testDay)) return;
e.Handled = true;
}
private void TextBox_GotFocus(object sender, EventArgs e)
{
if (sender is TextBox s)
{
s.SelectAll();
}
}
private void OnMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (this.IsFocused)
{
return;
}
this.Focus();
e.Handled = true;
}
}
}
追加プロパティ
Format |
日付編集フォーマットを指定する 指定しない場合は”yyyy年MM月dd日”になる |
Default | 何も入力しない場合に、システム日付を表示するか指定する(trueまたはfalse ) 日付を”null”でも扱いたい場合以外は、Default="True"にしておくと面倒がない |
追加メソッド
Date() |
内容をdateで取得します。 何も入力されていない時は”null”が帰ります。 |
SetDate(date) | date型の日付を表示します |
xaml内で記述する内容
<local:NumericTextBox x:Name="numeric_1" HorizontalAlignment="Left" Height="23" Margin="96,34,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="100" Format="#,0">
<local:NumericTextBox.InputBindings>
<KeyBinding Key="Enter" Command="{x:Static local:MyWindow.NumericIn_1}" />
</local:NumericTextBox.InputBindings>
</local:NumericTextBox>
<local:DateTextBox x:Name="date_1" HorizontalAlignment="Left" Height="23" Margin="96,65,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" Default="True">
<local:DateTextBox.InputBindings>
<KeyBinding Key="Enter" Command="{x:Static local:MyWindow.DateIn_1}" />
</local:DateTextBox.InputBindings>
</local:DateTextBox>
windowのC#で使用する
public MyWindow()
{
InitializeComponent();
// ウィンドウにコマンドを追加します
CommandBindings.Add(new CommandBinding(NumericIn_1, NumericIn_1Exec));
CommandBindings.Add(new CommandBinding(DateIn_1, DateIn_1Exec));
}
// コマンドを定義してインスタンス化する
public static RoutedCommand NumericIn_1 = new RoutedCommand();
public static RoutedCommand DateIn_1 = new RoutedCommand();
// numeric_1でEnterキーが押されると、実行される
private void NumericIn_1Exec(object sender, ExecutedRoutedEventArgs e)
{
/* ここに入力後の処理を書く */
}
// date_1でEnterキーが押されると、実行される
private void DateIn_1Exec(object sender, ExecutedRoutedEventArgs e)
{
/* ここに入力後の処理を書く */
}