まえおき
追加調査した結果タイトルを見直しました。
旧タイトル:
C# - NumericUpDownコントロールはHexadecimal=trueで使用すると0x80000000が負になる(せっかくValueがDecimal型なのに入力値がInt32でConvertされる)
概要
NumericUpDown
コントロールは下記のように、数値の入力をUp/Down操作するのに使いやすいコントロールです。
経緯
マイコンのシミュレータをフルスクラッチでつくろうというクレイジーなことをやり始めたときにはまった話。。
NumericUpDown
コントロールはHexadecimal
をtrue
にすると16進法で数値を表示できるのですが、32bit以上を扱おうとすると厄介な問題が発生しました。
Maximum
を0xFFFFFFFF
とかにし、Minimum
を0にして、スピン(?)(コントロールの右端の上下の▲のボタン)をいじってるときはよかったんですが、、
コントロールに直接FFFFFFFF
とかを入力すると、0
になるんですよ。。。 えっ???
ってなりましたが、さすがにもうこの手の現象には慣れてきて、どうせ内部でInt32に変換しているんだろうなと思ったら案の定でした。。。
内部コード
ILSpyで確認してみた。
/// <summary>スピン ボックス (アップダウン コントロール) に表示するテキストを数値に変換して評価します。</summary>
protected void ParseEditText()
{
try
{
if (!string.IsNullOrEmpty(Text) && (Text.Length != 1 || !(Text == "-")))
{
if (Hexadecimal)
{
Value = Constrain(Convert.ToDecimal(Convert.ToInt32(Text, 16)));
}
else
{
Value = Constrain(decimal.Parse(Text, CultureInfo.CurrentCulture));
}
}
}
catch
{
}
finally
{
base.UserEdit = false;
}
}
ヤッパリネ。。なんでInt32やねん・・・。けちくさい・・・。
追調査
コメントを頂いて、Hexadecimal
をtrue
に設定して使用した際の振る舞いとか、
今回やりたいこと(0
~0xFFFFFFFF
1(あえて型は問わず、16進数8桁を入力・設定できるユーザインタフェースをForm
に置きたい。)ができないか追加で調べてみた。
そもそもドキュメントに制約が記載されている
しっかり制約事項が書かれていました。。。※日本語サイトのほうは訳が壊れていたので英語のほうを引用しています。
https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.numericupdown.hexadecimal?view=netcore-3.1#remarks
When the Hexadecimal property is set, the UpdateEditText method is called to update the spin box's display to the new format.
When the Hexadecimal property is set to true, the Maximum property should be set to Int32.MaxValue and the Minimum property should be set to Int32.MinValue.
ドキュメントの通り使ってみたら・・・
using System;
using System.Drawing;
using System.Windows.Forms;
class NudHexTest : Form
{
NumericUpDown nud;
NudHexTest()
{
Controls.Add(nud = new NumericUpDown(){
Width = 180,
Hexadecimal = true,
Maximum = Int32.MaxValue, // 0x7FFFFFFF
Minimum = Int32.MinValue, // -0x80000000
Value = 0
});
ClientSize = new Size(180, 100);
}
[STAThread]
static void Main(string[] args)
{
Application.Run(new NudHexTest());
}
}
▼を押したらFFFFFFFF
が表示される(今回の使い方が一応実現できる)と思ったら、16桁になった。。
内部コード
public decimal Value
{
get
{
if (base.UserEdit)
{
ValidateEditText();
}
return currentValue;
}
set
{
if (value != currentValue)
{
if (!initializing && (value < minimum || value > maximum))
{
throw new ArgumentOutOfRangeException("Value", SR.GetString("InvalidBoundArgument", "Value", value.ToString(CultureInfo.CurrentCulture), "'Minimum'", "'Maximum'"));
}
currentValue = value;
OnValueChanged(EventArgs.Empty);
currentValueChanged = true;
UpdateEditText();
}
}
}
↓
/// <summary>スピン ボックス (アップダウン コントロール) の現在の値を適切な形式で表示します。</summary>
protected override void UpdateEditText()
{
if (!initializing)
{
if (base.UserEdit)
{
ParseEditText();
}
if (currentValueChanged || (!string.IsNullOrEmpty(Text) && (Text.Length != 1 || !(Text == "-"))))
{
currentValueChanged = false;
base.ChangingText = true;
Text = GetNumberText(currentValue);
}
}
}
↓
private string GetNumberText(decimal num)
{
if (Hexadecimal)
{
return ((long)num).ToString("X", CultureInfo.InvariantCulture);
}
return num.ToString((ThousandsSeparator ? "N" : "F") + DecimalPlaces.ToString(CultureInfo.CurrentCulture), CultureInfo.CurrentCulture);
}
なぜかこっちはlong
(Int64
)。。
参考
-
もしくは
0x80000000
( $= -2^{31} = $Int32.MinValue
)~0x7FFFFFFF
($= 2^{31}-1=$Int32.MaxValue
) ↩