Tvorba vlastního formulářového prvku (WPF)

C# .NET WPF Tvorba vlastního formulářového prvku (WPF)

V tomto tutoriálu si zkusíme naprogramovat MaskedTextBox pro wpf. Uvidíte jak prvek zařadit do aplikace pomocí kódu i pomocí XAMLu.

Tvorba formulářového prvku

Náš MaskedTextBox bude fungovat podobně, jako funguje ve Windows Forms. Tedy bude mít vlastnost mask, do které se bude vkládat ověřovaný formát. Naše verze bude osekaná, pro ukázku bude podporovat pouze čísla, písmena a desetinnou čárku (tečku). Budeme je kontrolovat podle jejich ASCII kódu, ten najdete v ASCII tabulce a tu najdete třeba zde. Vypsal jsem kódy našich znaků, které budeme potřebovat:

Pro potřeby tohoto článku si vytvořte novou wpf aplikaci. V okně Solution explorer pravým tlačítkem myši klikněte na projekt aplikace, z kontextové nabídky vyberte Add > Add New Item. Přesuňte se do skupiny WPF a vyberte User Control. Pojmenujte ho MaskedTextBox.

Znak ASCII
a-z 97-122
A-Z 65-90
. (tečka) 46
, (čárka) 44

Nyní se vám vytvoří ovládací prvek, ten bude zařazen do toolboxu. Všimněte si, že prvek má příponu xaml a jeho kód cs (vb). Otevřete designer formulářového prvku a přidejte do něj obyčejný TextBox. Tento TextBox pojmenujte generalTextBox. Hlavnímu gridu ještě nastavte výšku na 20 a šířku na 200. Nyní již máte hotové UI našeho vlastního formulářového prvku.

Přesuňte se do editoru kódu našeho ovládacího prvku, přidejte tam následující vlastnosti:

private BadValueHandler _valueHandler;
public BadValueHandler valueHandler {
        get {
                return _valueHandler;
        }
        set {
                _valueHandler = value;
        }
}
private string _mask;
public string mask {
        get {
                return _mask;
        }
        set {
                System.Collections.Generic.List<string> poleZnaku = new         System.Collections.Generic.List<string>();
                for (int i = 0; i < value.Length; i++)
                {
                        poleZnaku.Add(value.Substring(i, 1));
                }
                foreach (var znak in poleZnaku)
                {
                        switch (znak)
                        {
                                case "0":
                                        break;
                                case "a":
                                        break;
                                case ".":
                                        break;
                                default:
                                        throw new FormatException("Neplatný znak v masce");
                        }
                }
                _mask = value;
        }
}
public string Text {
        get
        {
                return generalTextBox.Text;
        }
        set
        {
                generalTextBox.Text = value;
        }
}

Máme tam vlastnost mask, kde validujeme zadanou hodnotu. Dále tam máme vlastnost valueHandler typu BadValueHandler, ten přidáme za chvíli.

Do kódu přidejte novou enumeraci BadValueHandler:

public enum BadValueHandler
{
        RedBox,
        MessageBox
}

Toto tam není jen tak. Hodnota bude určovat, jak se program zachová, když uživatel zadá špatný text, tedy text který není ve formátu masky. Chováni u jednotlivých hodnot, je v tabulce níže:

Hodnota Chování
RedBox Textové pole začervení.
MessageBox Uživateli se zobrazí dialogové okno se zprávou.

V kódu XAML u prvku generalTextBox vytvořte obsluhu události TextChanged. Přesuňte se do editoru kódu. Vytvořte v něm soukromou funkci Validate(), která bude vracet hodnotu typu bool (boolean). Do této funkce vložte následující kód:

string writedText = generalTextBox.Text;
string maska = this.mask;
if (writedText.Length != maska.Length)
{
        return false;
}
for (int i = 0; i < maska.Length; i++)
{
        string znakMasky = maska.Substring(i, 1);
        string znakVRetezci = writedText.Substring(i, 1);
        switch (znakMasky)
        {
                case "0":
                        try
                        {
                                Int testValue = int.Parse(znakVRetezci) / 2;
                        }
                        catch (Exception)
                        {
                                return false;
                        }
                        break;
                case "a":
                        int asc = (int)char.Parse(znakVRetezci);
                        if (!((asc >= 65 && asc <= 90) || (asc >= 97 && asc <= 122)))
                        {
                                return false;
                        }
                        break;
                case ".":
                        int dotAsc = (int)char.Parse(znakVRetezci);
                        if (!(dotAsc == 46 || dotAsc == 44))
                        {
                                return false;
                        }
                        break;
                default:
                        throw new FormatException("Naplatný formát masky");
        }
}
return true;

Tato funkce projde podle masky všechny znaky z masky. Hned na úvod kontroluje, jestli má zadaný text stejnou délku jako maska. Pokud ne, tak logicky text nemůže mít správný formát, protože má buď znaky navíc, nebo naopak některé chybí. Pak se projdou jednotlivé znaky v masce, podle nich se ověřuje, jestli je to číslo s zkouší se dělit. Pokud to nejde, vyhodí to výjimku a zadaný text je nevalidní. Když se jedná o písmeno, ověřuje se ASCII, stejně tak desetinná tečka a čárka. Pokud je v masce jiný než podporovaný znak (v tomto okamžiku by již být neměl, ale jistota je jistota) vyhodí se výjimka FormatException a jako zpráva se ji předá text "Neplatný formát masky". Nyní zpět k obsluze události TextChanged, vložte do ní následující kód:

if (validate())
{
        generalTextBox.Background = Brushes.White;
}
else
{
        switch (valueHandler)
        {
                case BadValueHandler.RedBox:
                        generalTextBox.Background = Brushes.OrangeRed;
                        break;
                case BadValueHandler.MessageBox:
                        if (mask.Length == generalTextBox.Text.Length)
                        {
                                MessageBox.Show("Zadali jste neplatnou hodnotu, zadejte hodnotu ve formátu: " + mask, "Neplatná hodnota", MessageBoxButton.OK, MessageBoxImage.Warning);
                        }
                        break;
        }
}

Jak vidíte, zde ověřujeme, zda je validní. Pokud ano tak nastavíme barvu pozadí na bílou, je to z toho důvodu, že když uživatel zadá špatnou hodnotu (červená) a teprve pak správnou, tak aby se barva vrátila na bílou. Pokud zadaný text není validní, ověří se hodnota ve ValueHandler (typ této hodnoty je enumerace BadValueHandler). Pokud hodnota je RedBox tak se pole začervení (červená je moc výrazná, tak jsem zvolil oranžovočervenou). Pokud je hodnota MessageBox, tak se zobrazí dialogové okno s varováním, to se ale zobrazí až v okamžiku, kdy je stejný počet znaků v masce i v poli. Je to z toho důvodu, že okno se nemá zobrazovat v průběhu zadávání, ale až po jeho skončení. Takhle by se dialogové okno zobrazovalo po zadání každého znaku.

Implementace formulářového prvku

V předchozí části jsme prvek vytvořili, ale jak ho implementovat do formulářové aplikace?

Výhodou vlastních formulářových prvků je taky to, že můžeme ovlivnit, jak se zobrazí. Ovládací prvky můžeme do formuláře přidat dvěma (mě známými) variantami. Pokud znáte další, napište mi to do diskuze pod článkem.

Přidání pomocí XAML

Nejprve klikněte na BUILD > Build Solution, tím zkompilujete řešení. Občas jsou s přidáváním nezkompilovaných prvků problémy. Otevřete si designer okna aplikace. Zobrazte si toolbox, uvidíte tam novou skupinu prvků "<název projektu> Control". V té je i ovládací prvek MaskedTextBox (<název projektu>). Tento prvek přetáhněte na formulář. Umístěte si jej kam chcete a nastavte mu nějakou rozumnou velikost.

Dále mu nastavte vlastnosti, které bude potřebovat pro vlastní účely (tj. vlastnost mask a valueHandler). Vlastnost Mask nastavte například na: "0000.aaaa". Vlastnost valueHandler na RedBox. Aplikaci zkompilujte a spusťte.

Zkuste do textového pole zadávat různé hodnoty. Uvidíte, že dokud nezadáte hodnotu ve správném formátu, bude textové pole červené.

Přidání v kódu

Po načtení formuláře, přidáme druhý ovládací prvek, vyzkoušíme ho z ošetřování hodnoty pomocí dialogového okna se zprávou.

Nastavte vlastnost Name jediného prvku grid na: "hlavniMriz", do tohoto prvku budeme přidávat MaskedTextBoxi. Vytvořte formuláři obsluhu události Load. Do té zadejte následující kód, případně si přidejte vlastnosti pro velikost a umístění:

textb.mask = "aaaa.0000";
textb.Width = 150;
textb.valueHandler = MaskedTextBox.BadValueHandler.MessageBox;
hlavniMriz.Children.Add(textb);

Nyní spusťte aplikaci, zkuste si zadávání hodnot, jakmile zadáte správný počet znaků, ověří se, zdali jsou správně. Pokud ano, nic se nestane. Pokud ne tak se zobrazí varovné dialogové okno.


 

Stáhnout

Staženo 381x (86.64 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (1)

Článek pro vás napsal Michal Žůrek (misaz)
Avatar
Autor se věnuje tvorbě aplikací pro počítače, mobilní telefony, mikroprocesory a tvorbě webových stránek a webových aplikací. Nejraději programuje ve Visual Basicu a TypeScript. Ovládá HTML, CSS, JavaScript, TypeScript, C# a Visual Basic.

Jak se ti líbí článek?
Celkem (3 hlasů) :
2.666672.666672.66667 2.666672.66667


 


Miniatura
Předchozí článek
Měnová kalkulačka
Miniatura
Všechny články v sekci
Okenní aplikace v C# .NET WPF
Miniatura
Následující článek
0. Díl WPF - Rychlost vykreslování

 

 

Komentáře

Avatar
Jirka
Člen
Avatar
Jirka:

Ahoj, pěkný tutoriál, ale nějak nevidím smysl těchto příkazů:

System.Collec­tions.Generic­.List<string> poleZadanychZnaku = new List<string>();
System.Collec­tions.Generic­.List<string> poleZnakuMasky = new List<string>();

Nedostaly se ti tam nějak omylem ? 8|

Editováno 13.9.2013 20:58
 
Odpovědět 13.9.2013 20:57
Avatar
Odpovídá na Jirka
Michal Žůrek (misaz):

jo jsou tam zbytečné, díky za upozornění.

Odpovědět 13.9.2013 21:24
Nesnáším {}, proto se jim vyhýbám.
Avatar
Petr Vocel
Redaktor
Avatar
Petr Vocel:

Používám Express 2015 for Windows Desktop a Win 10.
Když tam podle uvedeného postupu přidám usercontrol, tak soubory vidím v Sol. exploreru, ale prvek není v toolboxu. Když si stáhnu a otevřu příklad, tak tam je. Pokusím li se zde přidat další, soubory se přidají ale prvek do toolsu ne. What is wrong ? Ď

 
Odpovědět 31. července 18:33
Avatar
Odpovídá na Petr Vocel
Michal Žůrek (misaz):

Zkus dát v nabídce Build > Rebuild Solution.

Odpovědět 31. července 19:16
Nesnáším {}, proto se jim vyhýbám.
Avatar
Petr Vocel
Redaktor
Avatar
Petr Vocel:

Díky, to bylo ono

 
Odpovědět 31. července 22:31
Avatar
pracansky
Člen
Avatar
pracansky:

Ahoj, jako céčkaře jsi mě docela překvapil tou prácí se stringem :-O :-D

wpf sice moc neumím ale za odměnu bych tě rád inspiroval tímhle:

foreach (char znak in value)
            {
                switch (znak)
                {
                    case '0': break;
                    case 'a': break;
                    case '.': break;
                    default:
                        throw new FormatException("Neplatný znak v masce");
                }
            }

místo tohohle

System.Collections.Generic.List<string> poleZnaku = new         System.Collections.Generic.List<string>();
               for (int i = 0; i < value.Length; i++)
               {
                       poleZnaku.Add(value.Substring(i, 1));
               }
               foreach (var znak in poleZnaku)
               {
                       switch (znak)
                       {
                               case "0":
                                       break;
                               case "a":
                                       break;
                               case ".":
                                       break;
                               default:
                                       throw new FormatException("Neplatný znak v masce");
                       }
               }

a tímhle

char ch = writedText[i];
if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))) { /*...*/ }

místo tohodle

string znakVRetezci = writedText.Substring(i, 1);
int asc = (int)char.Parse(znakVRetezci);
if (!((asc >= 65 && asc <= 90) || (asc >= 97 && asc <= 122)))  { /*...*/ }

v jednoduchosti je krása ;-)

Editováno 21. listopadu 20:39
 
Odpovědět 21. listopadu 20:38
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.