Szukaj…


Uwagi

Kontrolki są wyprowadzane dokładnie w taki sam sposób jak inne klasy. Jedyną rzeczą, na którą należy uważać, jest nadpisywanie zdarzeń: zazwyczaj zaleca się, aby wywołać podstawową procedurę obsługi zdarzeń po swoim. Moja własna zasada: w razie wątpliwości wywołaj zdarzenie podstawowe.

Ustawienia dla całej aplikacji

Szybki przegląd większości witryn programistów ujawni, że WinForms jest uważany za gorszy od WPF. Jednym z najczęściej cytowanych powodów jest domniemana trudność w dokonywaniu szerokich zmian aplikacji w „wyglądzie” całej aplikacji.

W rzeczywistości zaskakująco łatwo jest stworzyć aplikację w WinForm, którą można łatwo skonfigurować zarówno w czasie projektowania, jak i w czasie wykonywania, jeśli po prostu unikasz korzystania ze standardowych elementów sterujących i czerpiesz z nich własne.

Weźmy TextBox jako przykład. Trudno wyobrazić sobie aplikację Windows, która nie wymaga użycia TextBox na jakimś etapie. Dlatego posiadanie własnego TextBoxa zawsze będzie miało sens. Weź następujący przykład:

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);
        }
    }
}

Jedną z rzeczy, które użytkownicy uważają za najbardziej pomocne w formularzu wprowadzania danych, z wieloma polami wprowadzania danych, jest zmiana koloru tła pola ze zmianą fokusa. Widocznie jest łatwiejszy do zauważenia niż standardowy migający pionowy kursor. Powyższy kod zapewnia TextBox, który właśnie to robi.

W tym procesie wykorzystuje właściwości statyczne klasy statycznej. Poniżej podaję wyciąg z mojego:

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);
            }
        }
    }
}

Ta klasa używa rejestru systemu Windows do zachowania właściwości, ale możesz użyć bazy danych lub pliku ustawień, jeśli wolisz. Zaletą używania klasy statycznej w ten sposób jest to, że zmiany w całej aplikacji mogą być dokonywane nie tylko w czasie projektowania, ale także przez użytkownika w czasie wykonywania. Zawsze dołączam formularz do moich aplikacji, umożliwiając użytkownikowi zmianę preferowanych wartości. Funkcja zapisu nie tylko zapisuje do rejestru (lub bazy danych itp.), Ale także w czasie wykonywania zmienia właściwości w klasie statycznej. Zauważ, że właściwości statyczne klasy statycznej nie są stałe; w tym sensie można je traktować jako zmienne dla całej aplikacji. Oznacza to, że na wszystkie formularze otwarte po zapisaniu zmian będą natychmiast miały wpływ wszystkie zapisane zmiany.

Z łatwością będziesz mógł pomyśleć o innych właściwościach aplikacji, które chcesz skonfigurować w ten sam sposób. Czcionki to kolejny bardzo dobry przykład.

NumberBox

Często chcesz mieć pole wprowadzania, które przyjmuje tylko liczby. Ponownie, korzystając ze standardowych kontroli, można to łatwo osiągnąć, na przykład:

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;
    }
}

Oprócz ograniczenia wprowadzania liczb, ta klasa ma kilka specjalnych cech. Wyświetla wartość właściwości, która reprezentuje podwójną wartość liczby, formatuje tekst, opcjonalnie z tysiącami separatorów, i zapewnia krótkie wprowadzanie dużych liczb: 10M rozwija się na urlop do 10 000 000,00 (liczba miejsc dziesiętnych jest właściwością ). Ze względu na zwięzłość, separatory dziesiętne i tysiące zostały zakodowane na stałe. W systemie produkcyjnym są to również preferencje użytkownika.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow