Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

C# programiranje grafičkog korisničkog interfejsa

Практични примери

У овом поглављу ћемо се храбро упустити у прављње програма који су на неки начин још мало сложенији од претходних и још мало ближи стварној употреби. Примери могу да буду сложенији било зато што користе нове компоненте и њихова својства (налажење фајлова, штоперица, едитор текста) било нове функције .NET библиотеке (налажење фајлова) или нове технике програмирања (препрачунавање температуре).

Штоперица

У примеру који следи илустроваћемо употребу тајмера, а успут ћемо видети и како можемо да променимо боју и величину слова на контролама, боју контроле и слично.

О тајмеру

Тајмер (енгл. Timer) је компонента помоћу које можемо да задамо да се неки код (функција) извршава у правилним временским размацима. Ову компоненту можемо наћи у групи Components кутије за алат.

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/Timer_Toolbox.png

Тајмер се бира и поставља на формулар на исти начин као и видљиве компоненте - кликом на тајмер у кутији за алат а затим кликом на формулар. Разлика је само у томе што се тајмер неће појавити на формулару него на простору испод формулара, зато што тајмер није видљив на формулару у време извршавања програма и не спада у контроле.

Важна својства тајмера су Interval и Enabled. Својство Interval је целобројно и представља период у милисекундама који треба да протекне између два узастопна извршавања задате функције. Својство Enabled је логичко и оно укључује и искључује тајмер. Када својство Enabled има вредност false, тајмер је искључен, нема мерења нити извршавања задате функције.

Када својство Enabled има вредност true, тајмер је укључен и после сваких Interval милисекунди дешава се догађај Tick. Овом догађају можемо да придружимо функцију, која ће се онда периодично извршавати.

Креирати Windows Forms апликацију која омогућава мерење времена као штоперица.

Почнимо од тајмера. Додајемо тајмер у апликацију, као што је горе описано. Тајмер у овом примеру има улогу штоперице, а штоперица обично мери време до на стотинке. Пошто хоћемо да тајмер „тикне” сваке стотинке, односно на сваких 10 милисекунди, поставићемо интервал тајмера на 10 (јер се интервал задаје у милисекундама). Када се покрене апликација, штоперица не треба да почне да броји одмах, него када је корисник укључи. Зато постављамо почетниу вредност својства Enabled на false, то јест искључујемо тајмер.

Поред тајмера, биће нам потребна два дугмета: једно за покретање и заустављање штоперице, а друго за враћање времена на почетак, као што је најчешће случај и на механичким штоперицама. Зато постављамо на формулар и два дугмета, која ћемо назвати btnStartStop и btnReset. На дугме btnStartStop поставимо текст „Start”, а на дугме btnReset текст „Reset”. На крају, потребна нам је и контрола која ће приказивати време. У ову сврху можемо да искористимо лабелу, коју ћемо назвати lblVreme, а њено својство Text постављамо на „00:00.00” (почетно време).

Након што смо поставили све потребне компоненте, можемо да да обавимо и нека подешавања која су споредна са тачке гледишта функционисања апликације - штоперице, али утичу да апликација лепше изгледа. На пример, желимо да време буде исписано крупније и зато подешавамо својство Font лабеле, тако да се њен текст приказује појачано (bold) и да буде величине 48 (типографских) тачака. Слично можемо да променимо величину дугмета, боју позадине дугмета (својство BackColor) и боју текста на дугмету (својство ForeColor). Формулар после овог малог украшавања може, на пример, да изгледа овако:

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/Stoperica_Form.png

Прелазимо на прогрмирање. У овом примеру је згодно да користимо једну целобројну променљиву, која представља број стотинки протеклих од укључења тајмера. Назовимо ову променљиву Stotinke. Желимо да овој променљивој мењамо вредност из функције која се извршава кликом на дугме btnReset, а такође и на „тик” тајмера. Осим тога, хоћемо да променљива Stotinke „не заборавља” своју вредност између извршавања ових функција. Да би променљива била видљива из различитих функција формулара, потребно је да је декларишемо ван свих функција, а унутар формулара. За овако декларисане променљиве се каже да су чланице класе Form1 (у овом тренутку не морате детаљно да разумете шта то значи). Након декларације променљиве Stotinke програм изгледа овако:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Stoperica
{
    public partial class Form1 : Form
    {
        int Stotinke = 0;

        public Form1()
        {
            InitializeComponent();
        }
    }
}

Даље, можемо да напишемо функцију PrikaziVreme, која ће на основу вредности променљиве Stotinke приказати одговарајуће време у лабели. Приметимо да је променљива stotinke (са малим почетним словом) локална променљива и није исто што и променљива Stotinke (са великим почетним словом).

private void PrikaziVreme()
{
    int t = Stotinke;
    int stotinke = t % 100; t /= 100;
    int sekunde = t % 60; t /= 60;
    int minuti = t;
    lblVreme.Text = string.Format("{0:00}:{1:00}.{2:00}", minuti, sekunde, stotinke);
}

Функција PrikaziVreme је обична функција, која није директно повезана ни са једним догађајем, то јест не покреће се аутоматски када се неки догађај деси. Ова функција је „помоћна” и биће позивана из других функција.

Подесимо сада функцију која се извршава на тик тајмера. Кликнимо на тајмер, затим на сличицу муње у прозору Properties. Догађај Tick је једини у листи. Двокликом на њега (или двокликом директно на тајмер) добијамо празну функцију timer1_Tick, коју сада треба да попунимо. Ова функција треба само да повећа бројач стотинки и позове приказивање времена:

private void timer1_Tick(object sender, EventArgs e)
{
    Stotinke++;
    PrikaziVreme();
}

Функција која ће се извршавати кликом на дугме btnReset је такође врло једноставна. Она враћа бројач на нулу и позива приказивање времена. Осим тога, пошто нема смисла ресетовати време које је већ ресетовано, можемо да додамо и наредбу којом ће се онемогућити нови клик на дугме за ресетовање:

private void btnReset_Click(object sender, EventArgs e)
{
    Stotinke = 0;
    PrikaziVreme();
    btnReset.Enabled = false;
}

Као последње, испрограмирајмо и дугме btnStartStop, које има најсложенију логику. Прва ствар коју ово дугме треба да уради је да укључи тајмер када је он искључен и обрнуто. ово се постиже следећом наредбом:

timer1.Enabled =  !timer1.Enabled;

Пошто ово дугме током рада програма мења своју улогу (наизменично служи за покретање и заустављање штоперице), треба додати наредбе које ће тренутну функцију дугмета појаснити кориснику. То значи да када је тајмер укључен, на дугмету треба да пише „Stop”, а када је тајмер искључен, на дугмету треба да пише „Start”. Осим натписа на дугмету, можемо да подешавамо доступност дугмета за ресетовање (не допуштамо ресетовање док је мерење у току, него само када је тајмер искључен), као и боју позадине дугмета btnStartStop и текста на њему. Ево како може да изгледа цела функција:

private void btnStartStop_Click(object sender, EventArgs e)
{
    timer1.Enabled =  !timer1.Enabled;
    if (timer1.Enabled)
    {
        btnStartStop.ForeColor = Color.Black;
        btnStartStop.BackColor = Color.Red;
        btnStartStop.Text = "Stop";
        btnReset.Enabled = false;
    }
    else
    {
        btnStartStop.ForeColor = Color.White;
        btnStartStop.BackColor = Color.Green;
        btnStartStop.Text = "Start";
        btnReset.Enabled = true;
    }
}

Програмирањем овог дугмета штоперица је довршена и спремна за употребу. Испробајте како програм ради и потрудите се да детаљно разумете како је то постигнуто.

Прерачунавање температуре

До сада смо више пута поменули да поједини догађаји настају при промени стања одговарајуће контроле. Замислимо сада да функција која обрађује један такав догађај мења стање исте или друге контроле. Ово би могло да изазове ланчану реакцију, у којој функције за обраду догађаја стално покрећу једна другу.

Следећи пример показује један начин да спречимо овај „зачарани круг”.

Креирати Windows Forms апликацију за упоредо и аутоматско прерачунавање температуре у степене Целзијуса, Фаренхајта и Келвина.

Овога пута дизајн формулара је врло једноставан: само три текстуална поља и лабеле које их појашњавају. Текстулана поља смо назвали tbCelzijusi, tbFarenhajti и tbKelvini.

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/KonverzijaTemperature_Form.png

За свако од ових текстуланих поља ћемо да испрограмирамо догађај TextChanged. Да бисмо избегли да функције за обраду ова три догађаја неограничено позивају једна другу, увешћемо логичку променљиву MenjanjeTekstaIzProgramaJeUToku, која ће бити видљива у свим функцијама формулара, ито као променљива Stotinke у примеру „Штоперица”.

using System;
using System.Windows.Forms;

namespace KonverzijaTemperature
{
    public partial class Form1 : Form
    {
        bool MenjanjeTekstaIzProgramaJeUToku = false;

        public Form1()
        {
            InitializeComponent();
        }
    }
}

Сада на почетку сваке од функција tbCelzijusi_TextChanged, tbFarenhajti_TextChanged и tbKelvini_TextChanged прово проверавамо вредност ове логичке променљиве. У рачунање улазимо само ако је вредност променљиве MenjanjeTekstaIzProgramaJeUToku једнака false. Када је вредност true, то значи да нека друга функција мења вредност овог текстуалног поља да би га ускладила са уносом корисника у текстуално поље чији догађај функција обрађује. У том случају нови (секундарни) догађај треба игнорисати.

Само прерачунавање температура врши се по формулама познатим и физике. Претварање (конверзија) стрингова у бројеве и обрнуто би требало да је већ познато.

private void tbCelzijusi_TextChanged(object sender, EventArgs e)
{
    double c, f, k;
    if (!MenjanjeTekstaIzProgramaJeUToku)
    {
        MenjanjeTekstaIzProgramaJeUToku = true;
        if (double.TryParse(tbCelzijusi.Text, out c))
        {
            k = 273.15 + c;
            f = c * 1.8 + 32;
            tbKelvini.Text = k.ToString();
            tbFarenhajti.Text = f.ToString();
        }
        else
        {
            tbKelvini.Text = "";
            tbFarenhajti.Text = "";
        }
        MenjanjeTekstaIzProgramaJeUToku = false;
    }
}

private void tbFarenhajti_TextChanged(object sender, EventArgs e)
{
    double c, f, k;
    if (!MenjanjeTekstaIzProgramaJeUToku)
    {
        MenjanjeTekstaIzProgramaJeUToku = true;
        if (double.TryParse(tbFarenhajti.Text, out f))
        {
            c = (f - 32) / 1.8;
            k = 273.15 + c;
            tbCelzijusi.Text = c.ToString();
            tbKelvini.Text = k.ToString();
        }
        else
        {
            tbCelzijusi.Text = "";
            tbKelvini.Text = "";
        }
        MenjanjeTekstaIzProgramaJeUToku = false;
    }
}

private void tbKelvini_TextChanged(object sender, EventArgs e)
{
    double c, f, k;
    if (!MenjanjeTekstaIzProgramaJeUToku)
    {
        MenjanjeTekstaIzProgramaJeUToku = true;
        if (double.TryParse(tbKelvini.Text, out k))
        {
            c = k - 273.15;
            f = c * 1.8 + 32;
            tbCelzijusi.Text = c.ToString();
            tbFarenhajti.Text = f.ToString();
        }
        else
        {
            tbCelzijusi.Text = "";
            tbFarenhajti.Text = "";
        }
        MenjanjeTekstaIzProgramaJeUToku = false;
    }

Налажење фајлова

У овом примеру се по први пут појављује контрола Поље са листом (енгл. ListBox). Пример илуструје употребу својства Dock ове контроле и с тим у вези догађај формулара Resize.

Употребљено и је и неколико функција из билиотеке .NET за рад са фајловима и директоријумима.

Креирати Windows Forms апликацију за тражење фајлова. Корисник треба обавезно да зада маску за име фајла (на пример *.txt) и директоријум у коме тражи. да тражи фајлове. Даље, ако жели, корисник може да прецизира:

  • текст који треба да се налази у фајлу (са или без разликовања малих и великих слова)

  • да ли тражи фајлове само у наведеном директоријуму или и у поддиректоријумима

  • да ли га интересују само фајлови, само директоријуми, или и једно и друго

  • да ли га интересују само скривени, само нескривени или сви фајлови

Пошто и овај пример има нешто богатији интерфејс, почећемо од слике дизајнираног формулара:

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/NalazenjeFajlova_Form.png

За задавање маске имена и полазног директоријума постављамо текстуалма поља tbImeFajla и tbDir.

Текстуално поље tbTekst и поље за потврду cbCaseSensitive служе да кориник (ако жели) зада текст који треба да се налази у траженим фајловима. Ове контроле иницијално постављамо као недоступне (својство Enabled постављамо на false), а корисник ће моћи да им приступи ако пре тога кликне на поље за потврду cbSaTekstom, чиме саопштава да жели да зада текст. Додајмо зато одмах контроли cbSaTekstom догађај CheckedChanged и испрограмирајмо га на следећи начин:

private void cbSaTekstom_CheckedChanged(object sender, EventArgs e)
{
    tbTekst.Enabled = cbSaTekstom.Checked;
    cbCaseSensitive.Enabled = cbSaTekstom.Checked;
}

Поље за потврду cbRekurzivno (са текстом „тражи у дубину”) служи да корисник каже да ли жели претрагу и у поддиректоријумима.

Поље за потврду cbDir презизира да ли се траже фајлови, директоријуми или и једно и друго. Поље за потврду cbSkriven презизира да ли тражимо скривене фајлове. За оба ова поља за потврду сва три стања имају смисла, па им ћемо својство ThreeState поставити на true, а својство CheckState на Indeterminate. Ова два поља су такође иницијално недоступна, а постају доступна кликом на поље за потврду cbAtributi, чиме корисник саопштава да жели да зада атрибуте. Као и малопре, контроли cbAtributi додајемо догађај CheckedChanged и убацујемо наредбе које постављају доступност подређених контрола cbDir и cbSkriven:

private void cbAtributi_CheckedChanged(object sender, EventArgs e)
{
    cbDir.Enabled = cbAtributi.Checked;
    cbSkriven.Enabled = cbAtributi.Checked;
}

Требаће нам и дугме btnTrazi (са текстом „Тражи”), које покреће акцију тражења.

На крају додајемо контролу у којој ће се појавити списак пронађених фајлова. За ову намену је погодна контрола Поље са листом, која се такође налази међу уобичајеним контролама кутије за алат. Назваћемо наше поље са листом lbSpisak. Најприродније је да ова листа потпуно попуни доњи део формулара. Постоји начин да се ивице ове контроле закаче за ивице формулара. То постижемо помоћу својства Dock (енгл. пристаниште, односно пристајање брода уз док). Када кликнемо на ово својсто, отвара се графички приказ могућих положаја контроле.

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/NalazenjeFajlova_Dock.png

Бирамо доњи део (вредност Bottom), пошто желимо да приљубимо контолу на доњи део формулара.

Списак пронађених фајлова може да буде дугачак, имена фајлова са пуним путањама такође. Зато у овом примеру нећемо спречавати корисника да повећа прозор апликације. Међутим, када корисник повећа главни прозор, треба да се повећа и физичка величина листе, а то се неће десити само од себе. Потребно је да контролишемо догађај Resize формулара, додајући ову функцију:

private void Form1_Resize(object sender, EventArgs e)
{
    lbSpisak.Height = ClientRectangle.Height - 140;
}

Тиме листа својом висином прати формулар. Да формулар не би постао премали, поставићемо својство формулара MinimumSize на „360, 400”.

Потребно је још испорограмирати догађај Click дугмета „Тражи”. Одговарајућа функција треба да тражи фајлове према задатим критеријумима, а имена оних који испуњавају све постављене критеријуме да убаци у списак.

Поново се ослањамо на .NET библиотеку и њене многобројне функције. На пример, за добијање списка користимо једну од функција Directory.EnumerateFileSystemEntries, Directory.EnumerateDirectories, Directory.EnumerateFiles, а за смештање комплетног садржаја фајла у стринг користимо функцију File.ReadAllText.

Функција која следи није написана одједном нити „из главе” - поједини делови су додавани или мењани након мало тестирања, а називи појединих функција које користимо су пронађени на интернету. Зато немојте бринути ако тренутно не разумете све детаље ове функције, али свакако је пажљиво погледајте. Овај пример је ту пре свега да вам помогне да стекнете представу како изгледа испрограмирати један овакав задатак. То вам сада можда изгледа веома тешко, али уз стрпљење и посвећеност (уз приступ интернету који очигледно имате), можете да савладате и много комплексније програме.

private void btnTrazi_Click(object sender, EventArgs e)
{
    string putanja = tbDir.Text;
    if (!Directory.Exists(putanja))
    {
        MessageBox.Show("Ne postoji folder " + putanja);
        return;
    }
    string maska = tbImeFajla.Text;
    SearchOption opt = cbRekurzivno.Checked ?
        SearchOption.AllDirectories :
        SearchOption.TopDirectoryOnly;

    IEnumerable<string> spisak = new List<string>();
    switch (cbDir.CheckState)
    {
        case CheckState.Indeterminate:
            spisak = Directory.EnumerateFileSystemEntries(putanja, maska, opt);
            break;
        case CheckState.Checked:
            spisak = Directory.EnumerateDirectories(putanja, maska, opt);
            break;
        case CheckState.Unchecked:
            spisak = Directory.EnumerateFiles(putanja, maska, opt);
            break;
    }

    string trazeniTekst = tbTekst.Text;
    if (cbSaTekstom.Checked && !cbCaseSensitive.Checked)
        trazeniTekst = trazeniTekst.ToLower();

    lbSpisak.Items.Clear();
    foreach (string imeFajla in spisak)
    {
        bool dodajUListu = true;

        // Ako je fajl skriven a trazeni su samo neskriveni (ili obrnuto), ne dodajemo ga u listu
        if ((File.GetAttributes(imeFajla) & FileAttributes.Hidden) == FileAttributes.Hidden)
        {
            if (cbSkriven.CheckState == CheckState.Unchecked)
                dodajUListu = false;
        }
        else
        {
            if (cbSkriven.CheckState == CheckState.Checked)
                dodajUListu = false;
        }

        // Ako nije vec odbacen, i jeste fajl (tj. nije direktorijum) i treba da sadrzi tekst ...
        // proverimo da li sadrzi tekst
        if (dodajUListu && File.Exists(imeFajla) && cbSaTekstom.Checked)
        {
            string sadrzajFajla = File.ReadAllText(imeFajla);
            if (!cbCaseSensitive.Checked)
                sadrzajFajla = sadrzajFajla.ToLower();

            if (!sadrzajFajla.Contains(trazeniTekst))
                dodajUListu = false;
        }

        // Ako ispunjava sve uslove, dodajemo ga u listu
        if (dodajUListu)
            lbSpisak.Items.Add(imeFajla);
    }
}

Едитор текста

У овом примеру показаћемо употребу нове контроле (Поље са обогаћеним текстом, енгл RichTextBox), менија и неколико врста дијалога. Поље са обогаћеним текстом се налази у групи уобичајених контрола, мени у групи Менији и траке алатки (Menus & Toolbars), а дијалози у групи Дијалози (Dialogs), све у прозору ToolBox.

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/EditorTeksta_Meniji_Dijalozi.png

Креирати Windows Forms апликацију за уређивање текста.

Поставимо на формулар потребне компоненте.

Из групе уобичајених контрола бирамо контролу Поље са обогаћеним текстом (RichTextBox) и дајемо јој име rtbDokument. Ова контрола треба да прекрива (скоро) цео формулар па ћемо њено својство Dock поставити на вредност Fill тако што у графичком приказу могућих положаја овог поља бирамо средишњи део.

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/EditorTeksta_Dock.png

Из групе Менији и траке алатки (Menus & Toolbars) узећемо компоненте Трака менија (MenuStrip) и Статусна трака (StatusStrip). Ове две траке ће се појавити на формулару, као што показују стрелице на следећој слици (горња стрелица показује на траку менија, а доња на статусну траку).

Из групе Дијалози (Dialogs), бирамо следеће дилајоге:

  • дијалог за отварање фајла (енгл. OpenFileDialog)

  • дијалог за чување фајла (енгл. SaveFileDialog)

  • дијалог за избор боје (енгл. ColorDialog)

  • дијалог за подешавање фонта (енгл. FontDialog)

компоненте које представљају дијалоге и траке виде се у простору испод формулара (на следећој слици је тај простор уоквирен црвено):

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/EditorTeksta_Izabrani_Meniji_Dijalozi.png

Када селектујемо статусну траку, у њеном левом углу се појављује мала стрелица. Кликом на ту стрелицу добијамо падајући мени. Да бисмо могли да исписујемо поруке у статусној линији, изаберимо из тог падајућег менија ставку StatusLabel.

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/EditorTeksta_ToolStripStatusLabel.png

Ставке менија се уносе врло једноставно, довољно је кликнути на место где треба да се појави прва ставка менија и уписати је. Порстор за нове ставке се аутоматски појављује доле и десно, као што показује следећа слика.

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/EditorTeksta_PopunjavanjeMenija.png

Ми смо ставке менија поунили овако:

  • Ставке менија Фајл: Нови, Отвори, Сачувај, Изађи

  • Ставке менија Едит: Исеци, Копирај, Залепи

  • Ставке менија Изглед: Боја текста, Боја позадине, Фонт, Прелом, Увећај, Смањи

Тиме је дизајн формулара завршен. У делу програмирања прво треба свакој ставки менија испрограмирати догађај Click. С обзиром на број ставки, у овом делу има доста посла, али је тај посао сасвим једноставан. Функције за обраду догађаја често имају само једну линију кода коју није тешко разумети (комплетан код је нешто ниже). Конторла Поље са обогаћеним текстом (RichTextBox) има много уграђених готових функционалности, које само треба покренути у функцијама за обраду кликова на одговарајуће ставке менија.

Дијалози се сви понашају међусобно слично. Користимо их тако што позивамо методу ShowDialog. Ако метода врати вредност DialogResult.OK, то значи да је корисник начинио неки избор (није одустао) и у том случају ћемо очитати вредност неког својства из дијалога и пренети га главној контроли у програму (поље са текстом). У дијалозима које смо користили, та својства су:

  • код дијалога за отварање фајла својство FileName, које садржи име фајла изабраног за отварање ;

  • код дијалога за чување фајла такође својство FileName, које овај пут садржи име фајла у коме ће се сачувати текст;

  • код дијалога за избор боје својство Color, (боју различито употребљавамо зависно од начина покретања дијалога);

  • код дијалога за подешавање фонта својство Font које садржи комплетан опис изабраног фонта;

Осим кликова на ставке менија, обрађују се и два догађаја поља са текстом.

Догађај TextChanged поља са текстом се дешава при свакој измени текста, и обрађујемо га тако што у лабели статусне линије исписујемо број знакова од којих се текст састоји. Функција за обраду је једноставна, као и већина оних које обрађују кликове на ставке менија.

private void rtbDokument_TextChanged(object sender, EventArgs e)
{
    lblBrojZnakova.Text = "Broj znakova: " + rtbDokument.TextLength.ToString();
}

Догађај KeyDown поља са текстом се дешава код сваког притиска на неки тастер тастатуре, када је поље са текстом у фокусу (прима улаз са тастатуре). Други параметар функције за обраду, који је типа KeyEventArgs садржи додатне информације о притиснутом тастеру. У коду тај параметар аутоматски добија име e, а може се по жељи именовати и другачије (ми то нисмо чинили). Својство e.KeyCode садржи код притиснутог тастера, а својство e.Control је логичког типа и показује да ли је тастер Ctrl био притиснут заједно са тим тастером.

За сваки тастер постоји именована константа једнака коду тог тастера. Захваљујући томе, довољно је вредност e.KeyCode поредити са именима кодова оних тастера за које смо заинтересовани. На пример, константа Keys.Add представља код тастера + са нумеричког дела тастатуре (на већини тастатура велики тастер сасвим десно), па у случају да је корисник притиснуо овај тастер заједно са тастером Ctrl, биће позвана функција Uvecaj. Слично томе, притисак на Ctrl и - (важи само за минус са нумеричког дела тастатуре) покреће функцију Smanji.

private void rtbDokument_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Control && e.KeyCode == Keys.Add)
        Uvecaj();

    if (e.Control && e.KeyCode == Keys.Subtract)
        Smanji();
}

Више примера програмирања догађаја притиска на тастере може се наћи у делу који се бави графиком (цртање и анимација) и интеракцијом корисника са таквим програмима.

Функције Uvecaj и Smanji су (иако врло кратке) издвојене као посебне функције, јер се могу позвати и из менија. Њихова улога је да текст у главној контроли прикажу повећано, односно смањено.

private void Uvecaj()
{
    if (rtbDokument.ZoomFactor < 60)
        rtbDokument.ZoomFactor += 1;
}

private void Smanji()
{
    if (rtbDokument.ZoomFactor > 1)
        rtbDokument.ZoomFactor -= 1;
}

Следи комплетан код са свим методама овог формулара. Поново напомињемо да не треба да бринете ако вам све ово изгледа тешко (то је очекивано). На овај и сличне примере треба гледати више као на идеју шта све можете да направите и упутство за истраживање, него на нешто што већ сада треба да умете од почетка до краја.

using System;
using System.Windows.Forms;

namespace EditorTeksta
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void fajlNoviMenuItem_Click(object sender, EventArgs e)
        {
            rtbDokument.Clear();
        }

        private void fajlOtvoriMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    rtbDokument.LoadFile(openFileDialog1.FileName, RichTextBoxStreamType.PlainText);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void fajlSacuvajMenuItem_Click(object sender, EventArgs e)
        {
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    rtbDokument.SaveFile(saveFileDialog1.FileName,
                   RichTextBoxStreamType.PlainText);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }

        }

        private void fajlIzadjiToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void editIseciMenuItem_Click(object sender, EventArgs e)
        {
            rtbDokument.Cut();
        }

        private void editKopirajMenuItem_Click(object sender, EventArgs e)
        {
            rtbDokument.Copy();
        }

        private void editZalepiMenuItem_Click(object sender, EventArgs e)
        {
            rtbDokument.Paste();
        }

        private void izgledBojaTekstaMenuItem_Click(object sender, EventArgs e)
        {
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                rtbDokument.ForeColor = colorDialog1.Color;
            }
        }

        private void izgledBojaPozMenuItem_Click(object sender, EventArgs e)
        {
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                rtbDokument.BackColor = colorDialog1.Color;
            }
        }

        private void izgledFontMenuItem_Click(object sender, EventArgs e)
        {
            if (fontDialog1.ShowDialog() == DialogResult.OK)
            {
                rtbDokument.Font = fontDialog1.Font;
            }
        }

        private void izgledPrelomMenuItem_Click(object sender, EventArgs e)
        {
            izgledPrelomMenuItem.Checked = !izgledPrelomMenuItem.Checked;
            rtbDokument.WordWrap = izgledPrelomMenuItem.Checked;
        }

        private void izgledUvecajMenuItem_Click(object sender, EventArgs e)
        {
            Uvecaj();
        }

        private void izgledSmanjiMenuItem_Click(object sender, EventArgs e)
        {
            Smanji();
        }

        private void rtbDokument_TextChanged(object sender, EventArgs e)
        {
            lblBrojZnakova.Text = "Broj znakova: " + rtbDokument.TextLength.ToString();
        }

        private void rtbDokument_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && e.KeyCode == Keys.Add)
                Uvecaj();

            if (e.Control && e.KeyCode == Keys.Subtract)
                Smanji();
        }

        private void Uvecaj()
        {
            if (rtbDokument.ZoomFactor < 60)
                rtbDokument.ZoomFactor += 1;
        }

        private void Smanji()
        {
            if (rtbDokument.ZoomFactor > 1)
                rtbDokument.ZoomFactor -= 1;
        }
    }
}