수색…
비고
컨트롤은 다른 클래스와 동일한 방식으로 파생됩니다. 주의해야 할 것은 이벤트를 재정의하는 것입니다. 일반적으로 자체 이벤트 핸들러를 호출 한 다음 기본 이벤트 처리기를 호출하는 것이 좋습니다. 내 자신의 경험 법칙 : 의심스러운 경우 기본 이벤트를 호출하십시오.
응용 프로그램 전체 설정
대부분의 개발자 사이트를 빠르게 읽으면 WinForms가 WPF보다 열등하다고 여겨집니다. 가장 자주 인용되는 이유 중 하나는 전체 응용 프로그램의 "모양과 느낌"에 응용 프로그램을 광범위하게 변경하는 데 어려움이 있다고 가정합니다.
실제로 표준 컨트롤의 사용을 피하고 단순히 표준 컨트롤을 사용하지 않으면 WinForms에서 디자인 타임과 런타임 모두에서 쉽게 구성 할 수있는 응용 프로그램을 만드는 것이 놀랍도록 쉽습니다.
TextBox를 예로 들어 보겠습니다. TextBox의 사용을 요구하지 않는 Windows 응용 프로그램을 상상하기 란 어렵습니다. 따라서 자신의 TextBox를 갖는 것이 항상 의미가 있습니다. 다음 예제를 참조하십시오.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace StackOverflowDocumentation
{
public class SOTextBox : TextBox
{
public SOTextBox() : base()
{
base.BackColor = SOUserPreferences.BackColor;
base.ForeColor = SOUserPreferences.ForeColor;
}
protected override void OnEnter(EventArgs e)
{
base.BackColor = SOUserPreferences.FocusColor;
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
base.BackColor = SOUserPreferences.BackColor;
base.OnLeave(e);
}
}
}
사용자가 많은 입력 상자가있는 데이터 입력 양식에서 가장 유용하다고 생각하는 것 중 하나는 포커스가 변경된 상자의 배경색을 유지하는 것입니다. 표준 깜박이는 수직 커서보다 쉽게 볼 수 있습니다. 위 코드는 정확히 수행하는 TextBox를 제공합니다.
이 과정에서 정적 클래스의 정적 속성을 사용합니다. 나는 나의 것에서 추출물을 아래에 준다 :
using System;
using System.Threading;
using Microsoft.Win32;
using System.Globalization;
using System.Data;
using System.Drawing;
namespace StackOverflowDocumentation
{
public class SOUserPreferences
{
private static string language;
private static string logPath;
private static int formBackCol;
private static int formForeCol;
private static int backCol;
private static int foreCol;
private static int focusCol;
static SOUserPreferences()
{
try
{
RegistryKey HKCU = Registry.CurrentUser;
RegistryKey kSOPrefs = HKCU.OpenSubKey("SOPrefs");
if (kSOPrefs != null)
{
language = kSOPrefs.GetValue("Language", "EN").ToString();
logPath = kSOPrefs.GetValue("LogPath", "c:\\windows\\logs\\").ToString();
formForeCol = int.Parse(kSOPrefs.GetValue("FormForeColor", "-2147483630").ToString());
formBackCol = int.Parse(kSOPrefs.GetValue("FormBackColor", "-2147483633").ToString());
foreCol = int.Parse(kSOPrefs.GetValue("ForeColor", "-2147483640").ToString());
backCol = int.Parse(kSOPrefs.GetValue("BackColor", "-2147483643").ToString());
focusCol = int.Parse(kSOPrefs.GetValue("FocusColor", "-2147483643").ToString());
}
else
{
language = "EN";
logPath = "c:\\windows\\logs\\";
formForeCol = -2147483630;
formBackCol = -2147483633;
foreCol = -2147483640;
backCol = -2147483643;
focusCol = -2147483643;
}
}
catch (Exception ex)
{
//handle exception here;
}
}
public static string Language
{
get
{
return language;
}
set
{
language = value;
}
}
public static string LogPath
{
get
{
return logPath;
}
set
{
logPath = value;
}
}
public static Color FormBackColor
{
get
{
return ColorTranslator.FromOle(formBackCol);
}
set
{
formBackCol = ColorTranslator.ToOle(value);
}
}
public static Color FormForeColor
{
get
{
return ColorTranslator.FromOle(formForeCol);
}
set
{
formForeCol = ColorTranslator.ToOle(value);
}
}
public static Color BackColor
{
get
{
return ColorTranslator.FromOle(backCol);
}
set
{
backCol = ColorTranslator.ToOle(value);
}
}
public static Color ForeColor
{
get
{
return ColorTranslator.FromOle(foreCol);
}
set
{
foreCol = ColorTranslator.ToOle(value);
}
}
public static Color FocusColor
{
get
{
return ColorTranslator.FromOle(focusCol);
}
set
{
focusCol = ColorTranslator.ToOle(value);
}
}
}
}
이 클래스는 Windows 레지스트리를 사용하여 속성을 유지하지만 원하는 경우 데이터베이스 또는 설정 파일을 사용할 수 있습니다. 이런 식으로 정적 클래스를 사용하면 응용 프로그램 전반에 걸친 변경이 디자인 타임뿐만 아니라 런타임에 사용자가 변경할 수 있다는 이점이 있습니다. 나는 항상 사용자가 선호하는 값을 변경할 수 있도록 응용 프로그램에 양식을 포함합니다. save 함수는 레지스트리 (또는 데이터베이스 등)에 저장하는 것뿐만 아니라 실행시 정적 클래스의 프로퍼티도 변경합니다. 정적 클래스의 정적 속성은 일정하지 않습니다. 이러한 의미에서 응용 프로그램 전체 변수로 간주 될 수 있습니다. 즉, 저장중인 변경 사항 이후에 열린 모든 양식은 저장된 변경 사항의 영향을 즉시받습니다.
같은 방식으로 구성 할 수있는 다른 응용 프로그램의 광범위한 속성을 쉽게 생각할 수 있습니다. 글꼴은 또 다른 좋은 예입니다.
NumberBox
종종 숫자 만 사용하는 입력 상자가 필요할 것입니다. 다시 표준 제어에서 파생 됨으로써 쉽게 달성 할 수 있습니다. 예를 들면 다음과 같습니다.
using System;
using System.Windows.Forms;
using System.Globalization;
namespace StackOverflowDocumentation
{
public class SONumberBox : SOTextBox
{
private int decPlaces;
private int extraDecPlaces;
private bool perCent;
private bool useThouSep = true;
private string decSep = ".";
private string thouSep = ",";
private double numVal;
public SONumberBox() : base()
{
}
public bool PerCent
{
get
{
return perCent;
}
set
{
perCent = value;
}
}
public double Value
{
get
{
return numVal;
}
set
{
numVal = value;
if (perCent)
{
double test = numVal * 100.0;
this.Text = FormatNumber(test) + "%";
}
else
{
this.Text = FormatNumber(value);
}
}
}
public bool UseThousandSeparator
{
get
{
return useThouSep;
}
set
{
useThouSep = value;
}
}
public int DecimalPlaces
{
get
{
return decPlaces;
}
set
{
decPlaces = value;
}
}
public int ExtraDecimalPlaces
{
get
{
return extraDecPlaces;
}
set
{
extraDecPlaces = value;
}
}
protected override void OnTextChanged(EventArgs e)
{
string newVal = this.Text;
int len = newVal.Length;
if (len == 0)
{
return;
}
bool neg = false;
if (len > 1)
{
if (newVal.Substring(0, 1) == "-")
{
newVal = newVal.Substring(1, len - 1);
len = newVal.Length;
neg = true;
}
}
double val = 1.0;
string endChar = newVal.Substring(newVal.Length - 1);
switch (endChar)
{
case "M":
case "m":
if (len > 1)
{
val = double.Parse(newVal.Substring(0, len - 1)) * 1000000.0;
}
else
{
val *= 1000000.0;
}
if (neg)
{
val = -val;
}
this.Text = FormatNumber(val);
break;
case "T":
case "t":
if (len > 1)
{
val = double.Parse(newVal.Substring(0, len - 1)) * 1000.0;
}
else
{
val *= 1000.0;
}
if (neg)
{
val = -val;
}
this.Text = FormatNumber(val);
break;
}
base.OnTextChanged(e);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
bool handled = false;
switch (e.KeyChar)
{
case '-':
if (this.Text.Length == 0)
{
break;
}
else if (this.SelectionStart == 0)
{
//negative being inserted first
break;
}
else
{
handled = true;
break;
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
case (char)Keys.Back:
break;
case 'M':
case 'm':
case 'T':
case 't':
case '%':
//check last pos
int l = this.Text.Length;
int sT = this.SelectionStart;
int sL = this.SelectionLength;
if ((sT + sL) != l)
{
handled = true;
}
break;
default:
string thisChar = e.KeyChar.ToString();
if (thisChar == decSep)
{
char[] txt = this.Text.ToCharArray();
for (int i = 0; i < txt.Length; i++)
{
if (txt[i].ToString() == decSep)
{
handled = true;
break;
}
}
break;
}
else if (thisChar != thouSep)
{
handled = true;
}
break;
}
if (!handled)
{
base.OnKeyPress(e);
}
else
{
e.Handled = true;
}
}
protected override void OnLeave(EventArgs e)
{
string tmp = this.Text;
if (tmp == "")
{
tmp = "0";
numVal = NumberLostFocus(ref tmp);
this.Text = tmp;
}
if (tmp.Substring(tmp.Length - 1) == "%")
{
tmp = tmp.Substring(0, tmp.Length - 1);
numVal = 0.0;
numVal = NumberLostFocus(ref tmp) / 100.0;
double test = numVal * 100.0;
this.Text = FormatNumber(test) + "%";
}
else if (perCent)
{
numVal = NumberLostFocus(ref tmp);
double test = numVal * 100.0;
this.Text = FormatNumber(test) + "%";
}
else
{
numVal = NumberLostFocus(ref tmp);
this.Text = tmp;
}
base.OnLeave(e);
}
private string FormatNumber(double amount)
{
NumberFormatInfo nF = new NumberFormatInfo();
nF.NumberDecimalSeparator = decSep;
nF.NumberGroupSeparator = thouSep;
string decFormat;
if (useThouSep)
{
decFormat = "#,##0";
}
else
{
decFormat = "#0";
}
if (decPlaces > 0)
{
decFormat += ".";
for (int i = 0; i < decPlaces; i++)
{
decFormat += "0";
}
if (extraDecPlaces > 0)
{
for (int i = 0; i < extraDecPlaces; i++)
{
decFormat += "#";
}
}
}
else if (extraDecPlaces > 0)
{
decFormat += ".";
for (int i = 0; i < extraDecPlaces; i++)
{
decFormat += "#";
}
}
return (amount.ToString(decFormat, nF));
}
private double NumberLostFocus(ref string amountBox)
{
if (amountBox.Substring(0, 1) == decSep)
amountBox = "0" + amountBox;
NumberFormatInfo nF = new NumberFormatInfo();
nF.NumberDecimalSeparator = decSep;
nF.NumberGroupSeparator = thouSep;
try
{
double d = 0.0;
int l = amountBox.Length;
if (l > 0)
{
char[] c = amountBox.ToCharArray();
char endChar = c[l - 1];
switch (endChar)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
stripNonNumerics(ref amountBox);
d = Double.Parse(amountBox, nF);
break;
}
case 'm':
case 'M':
{
if (amountBox.Length == 1)
d = 1000000.0;
else
{
string s = amountBox.Substring(0, l - 1);
stripNonNumerics(ref s);
d = Double.Parse(s, nF) * 1000000.0;
}
break;
}
case 't':
case 'T':
{
if (amountBox.Length == 1)
d = 1000.0;
else
{
string s = amountBox.Substring(0, l - 1);
stripNonNumerics(ref s);
d = Double.Parse(s, nF) * 1000.0;
}
break;
}
default:
{
//remove offending char
string s = amountBox.Substring(0, l - 1);
if (s.Length > 0)
{
stripNonNumerics(ref s);
d = Double.Parse(s, nF);
}
else
d = 0.0;
break;
}
}
}
amountBox = FormatNumber(d);
return (d);
}
catch (Exception e)
{
//handle exception here;
return 0.0;
}
}
private void stripNonNumerics(ref string amountBox)
{
bool dSFound = false;
char[] tmp = decSep.ToCharArray();
char dS = tmp[0];
string cleanNum = "";
int l = amountBox.Length;
if (l > 0)
{
char[] c = amountBox.ToCharArray();
for (int i = 0; i < l; i++)
{
char b = c[i];
switch (b)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
cleanNum += b;
break;
case '-':
if (i == 0)
cleanNum += b;
break;
default:
if ((b == dS) && (!dSFound))
{
dSFound = true;
cleanNum += b;
}
break;
}
}
}
amountBox = cleanNum;
}
}
입력을 숫자로 제한하는 것뿐만 아니라이 클래스에는 몇 가지 특별한 기능이 있습니다. 숫자의 이중 값을 나타내는 속성 값을 노출하고, 선택적으로 천 단위 구분 기호를 사용하여 텍스트의 서식을 지정하며, 큰 숫자의 짧은 입력을 제공합니다. 10M이 10,000,000.00으로 남을 때 확장됩니다 (소수점 이하 자릿수가 속성 임). ). 간결함을 위해 십진수와 천 단위 구분 기호는 하드 코딩되어 있습니다. 프로덕션 시스템에서 이는 사용자 환경 설정입니다.