Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

Vlko napísal ...

.. mostly harmless ...
WPF OnScreenKeybord coding story [update 1]

Dnes nás čaká dosť rozsiahly príspevok (asi som sa nechal uniesť). Preto ak štýl akým píšem, alebo rozsah textu Vás odrádza, odporúčam skok na koniec článku, kde je odkaz na zdrojové kódy ako aj binárku. Nech vašej pozornosti neujde aj varovanie, ktoré je hneď za linkami na stiahnutie, určite schladí vaše nadšenie.

[update 1] linka na zdrojové kódy bola rovnaká ako na binárku, teraz by už malo byť všetko v poriadku

Intermezzo

Občas sa človeku pritrafí, taký ten výnimočný deň, keď sa mu splnia také malé detské sny a po mesiacoch práce mu v balíčku príde vysnívaný počítač typu Tablet PC (z antireklamných dôvodov pre uvediem len link). Dôvodov, prečo som si vybral na vývoj akurát 12.1" riešenie je viacero, ale hlavným asi je to, že až si raz kliknete prstom na obrazovku a kurzor sa tam dokonca aj pohne (s kruhovou toleranicou od 1 do 10 px podľa kvality digitalizera), tak človek niekde vo vnútri cíti: "toto je budúcnosť".

A teraz sa už dostávame k praktickému používaniu. Používanie prsta na ovládanie je proste veľmi návyková záležitosť až tak, že sa časom zabúdate a ťukáte aj na externý monitor a teda handričku musíte mať poruke. Tú samozrejme musíte mať aj na tablet, aj keď momentálne zisťujem, že je lepšie je ho nechať riadne ucapať, to sa už nejaká tá mastnosť stratí, ale to je už hold daň za technológiu.

A pomaly sa dostávame k jadru problému a tým sú znaky, občas, vlastne dosť často potrebujete zadať nejakú tu sekvenciu znakov. Pretože Vista v sebe už obsahuje Tablet PC extensions, voľba operačného systému bola jednoznačná. Predstavy sa už ale od reality líšili, pôvodná prestava bola, že vista obsahuje takúto UMPC klávesnicu:

Bohužiaľ dostanete niečo takéto:

A čo je horšie táto klávesnica sa zobrazuje prejdením kurzora cez cca 5px široký panel schovaný úplne na okraji obrazovky a následným kliknutím na panel, ktorý následne vyskočí:

Pretože vätšina ľudí ma bruška prsta široké od 1 cm do 3 cm, a teda stred od 0.5 cm do 1.5 cm, k tomu treba prirátať zvýšený okraj a teda sa tento úkon dá zaradiť do kategórie nemožné. Samozrejme vätšina tablet pc umožnuje aj detailny posun kurzora pomocou nejakej pomocnej ikony, ale to už nie je single klik ale klik na max dosažiteľnú pozíciu, klik na pomocnú ikonu, presun a klik.

Zobrazená klávesnica nie je samozrejme jediným možnosťou vstupu, ďalšou je písaný text, ale na ten už musite držať v ruke pero, ale ja potrebujem pre pracu v tablet režime niečo šikovnejšie. Ale prečo chodiť okolo horúcej kaše, potrebujem niečo takéto:

Tak toto je to čo chceme dosiahnuť a asi aj to prečo ste začali čítať tento článok. Tak po suchom úvode poďme do práce.

KROK č.1 Vzhľad

Pretože mám rád nové technológie a všetko v živote treba raz skúšať, voľba padla na WPF, vektory sú pekné, dajú sa ľubovoľne resizovať a jednoducho nakresliť.

Takže stačí spustiť Inkscape urobiť mriežku, vyplniť mriežku, vymazať mriežku a výplňam nastaviť border a po cca 4 hodinách máte grafický základ vo formáte .svg. Na konverziu do xaml stačí už len použiť šikovný programček ViewerSvg.

Hmm. Hovoríte si, veď to je jednoduché. To áno, ale to nie je všetko, čaká Vás ta horšia časť a to umiestniť nad každé tlačidlo (po svg importe element Path) a tieto dva elementy groupnuť do elementu Canvas z dôvodu aby sme nad tlačidlom mohli urobiť hover efekt a bohužial Path nemôže obsahovať child elementy narozdiel od Canvasu. Rutinná práca ale výsledok stojí za to:

V tejto fáze som bol nadšený, mam panel pre pravú ruku s pekným hover efektom, je časť zavrieť Microsoft Expression Blend 2 a nastáva coding fáza.

KROK č.2 Stlačme kláves po prvé

Tak a teraz trošku teórie. V podstate sú dve možnosti ako urobiť on screen klávesnicu

  1. Pri kliku na klávesu zachytiť aktuálny focus, stlačiť klávesu vrátiť focus a poslať stisk klávesy (viď Onscreen Keyboard). Ale povedzme si úprimne: toto riešenie je hrozné, pretože ak nenastavíme carret position na koniec, tak nám zmaže input (v prípade štandardného textboxu, ten štandardne pri focuse označí celý text) a k tomu to preblikávanie focusu.
  2. Vo WndProc zachytiť správu WM_MOUSEACTIVATE a vrátiť MA_NOACTIVATE okrem toho ešte pridať v CreateParams funkcii do StyleEx WS_EX_NOACTIVATE flag.

Toľko teórie, čerpať zaujímavé informácie môžte z článku On-screen Keyboards a pre nás je dôležité, že voľba padla na možnosť č.2.

Teória robí majstra, bohužiaľ WPF okno nemá ani WndProc ani CreateParams. Niet sa čomu čudovať, je to nová technológia, ktorá sa snaží vyhýbať balastu starých API funkcií a okrem toho všetko by už malo byť prístupné cez vlastnosti objektu, teda žiadné skryté vlastnosti cez nejaký nepublikovanú flag hodnotu. Teda máme tu atribút Focusable="false" bohužiaľ to funguje len na text input elementy.

Musíme isť hlbšie a porozmýšľať. Veď nech sa na to pozriem z akejkoľvek strany, wpf okno je stale postavené na starom jadre a teda musí existovať spôsob ako získať jeho handle  a k čomu máme handle, tomu vieme zameniť WndProc aj nastaviť cez API funkcie StyleEx. Pár dobre mierených dotazov do google nám vráti WindowInteropHelper triedu. A my sa môžme pustiť konečne do kódu:

        private void Window_Loaded(object sender, RoutedEventArgs e)

        {

            WindowInteropHelper windowHandle = new WindowInteropHelper(this);

 

            // hWnd is the window you want to subclass..., create a new

            // delegate for the new wndproc

            newWndProc = new WindowAPI.Win32WndProc(OverridedWndProc);

            // subclass

            oldWndProc = WindowAPI.SetWindowLong(

                windowHandle.Handle,

                WindowAPI.GWL_WNDPROC,

                newWndProc);

 

            //get current window style of child form

            int style = WindowAPI.GetWindowLong(windowHandle.Handle, WindowAPI.GWL_STYLE);

 

            //set new style

            WindowAPI.SetWindowLong(windowHandle.Handle, WindowAPI.GWL_STYLE, (style | WindowAPI.WS_EX_NOACTIVATE));

        }

 Vo WindowAPI triede sa nachádzaju namapované funkcie (definície možné nájsť na pinvoke.net). Overridnut WndProc už potom vyzerá ibá takto:

        private int OverridedWndProc(IntPtr hWnd, int Msg, int wParam, int lParam)

        {

            switch (Msg)

            {

                case WindowAPI.WM_MOUSEACTIVATE:

                    //System.Windows.Forms.MessageBox.Show("Clicked");

                    return WindowAPI.MA_NOACTIVATE;

                default:

                    break;

            }

            return WindowAPI.CallWindowProc(oldWndProc, hWnd, Msg, wParam, lParam);

        }

Nasleduje kompilácia a samozrejme veľké sklamanie, pretože WPF window pri WS_EX_NOACTIVATE flagu urobí z okna pekný statický obrázok, ktorý nereaguje na žiadne vonkajšie podnety. Niekde na msdn som našiel príspevok, že možno niekedy v budúcnosti bude wpf také niečo podporovať. Tudy cesta nevedie. Tak to skúsme inak.

KROK č.3 Stlačme kláves po druhý krát

Pretože máme spravený vzhľad vo WPF a vieme, že onscreen klávesnicu je možné spraviť iba na štandardnom win okne, je potrebné tieto dve technolófie spojiť. Na to už človek nepotrebuje ani google, aby našiel ElementHost control. Potom už stačí iba použiť našu non activate window techniku:

        private const int WS_EX_NOACTIVATE = 0x08000000;

 

        protected override CreateParams CreateParams

        {

            get

            {

                CreateParams createParams = base.CreateParams;

                createParams.ExStyle = createParams.ExStyle | WS_EX_NOACTIVATE;

                return createParams;

            }

        }

 

        private const int WM_MOUSEACTIVATE = 0x0021;

        private const int MA_NOACTIVATE = 0x0003;

 

        protected override void WndProc(ref Message m)

        {

            //If we're being activated because the mouse clicked on us...

            if (m.Msg == WM_MOUSEACTIVATE)

            {

                //Then refuse to be activated, but allow the click event to pass through (don't use MA_NOACTIVATEEAT)

                m.Result = (IntPtr)MA_NOACTIVATE;

            }

            else

                base.WndProc(ref m);

        }

A na prekvapenie, takto vytvorené okno s WPF user controlom v sebe už pracuje ako má. Teda reaguje na všetky myšie eventy a nekradne focus aktívnej aplikácii.

Po dlhej dobe konečne dobrá správa už nám iba chýba ľavá časť klávesnice.

KROK č.4 Flip it horizontal

Dostávam sa do pracovnej horúčky otáčam wpf control pomocou transformácie <ScaleTransform ScaleX="-1" ScaleY="1"/>. Ostáva už len tak isto transformovať všetky textové elementy podobným spôsobom, aby text nebol zobrazený zrkadlovo. A už veselo kompilujeme..

Išlo to príliš rýchlo, že? Ale tak to v našom živote nechodí a ja narážam na známy WPF bug [BUG 3.5 BETA 2] BitmapEffect + RenderTransform = Incorrect clipping. Našťastie existuje workarround s nastavením Background="Transparent".

Aj toto sa Vám zdalo moc rýchle (v pozadí je cca 4 hodiny zmien kódu a hľadania správneho keywordu do googlu)? Tak nasleduje ďalší kopanec a to, že canvas elementy, v ktorých su path elementy sú štvorcové a transparent farba zachytáva mouse efekty a keďže sa rôzne prekrývaju. Výsledom je, že celá hover funkčnosť je v háji a my sa môžeme vrátiť k Inkscape otočiť obrázok a manuálne opäť urobiť opäť bod 1.

KROK č.5 Make it transparent

Tak nejak začínam šípiť, prečo sú programátori tak dobre platený, pretože nastaviť transparentnosť okna určite nestačí aby hostovaný WPF control bol priesvitný. A tak musíme použiť region aby okno aspoň z diaľky vyzeralo ako wpf okno z bodu 1.

Stačí nastaviť správnu farbu pozadia. Urobiť screenshot okna, trošku screenshot upraviť aby sa niekde vo vnútri neobjavovali rovnaké farby ako tie podľa ktorých okno budeme orezávať a správny kod na vytvorenie Custom Shape Form nájdete tu.

KROK č.6 A nezabudnúť na užívateľskú príťažlivosť

Aj keď mame virtuálnu klávesnícu peknú oblú, priesvitnú a vlastne nech by bola akokoľvek krásna, ak bude stále otravovať na obrazovke určite to užívateľov nepoteší. Musíme teda správiť aktívne zóny, pri kliknutí na ktoré sa naša virtuálna klávesnica zobrazí.

Na aktívne zóny potrebujeme mouse global hook. A samozrejme až taký v C# napíšete (tak ako ja), zistíte, že .net global hook-y v managed kóde nepodporuje. Zostáva nám teda C++ a unmanaged kód. Opäť si pomôžeme opensource zdrojmi a použijeme jeden napr. z Global System Hooks in .NET.

Aby sme opäť neboli len copy'n'paste programátormi, musíme ešte upraviť C++ kód, aby po kliknutí na aktívnu zónu nam bolo umožnené zahodiť všetky nechcené mouse eventy. Pri použití prsta na daný plochu klikneme a ak sa na danom mieste nachádza nejaká aplikácia, dochádza k nechcenému prekliku.

KROK č.7 Finalizujeme

Ostávaju už len drobnosti ako:

  • konfigurácia kláves pomocou XML súboru
  • konfigurácia aplikácie
  • troška kódu aby sa klávesnica po určitej nečinnosti sama schovala
  • štipku medzerníka
  • systray ikonku spolu s menu, aby sme aplikáciu vedeli aj zavrieť
  • zverejniť zdrojový kód

A najťažšia časť:

  • napísať tento blog príspevok aby sa už nikto nikdy nedopustil podobných omylov

Výsledok práce

Binárka: http://vlko.zilina.net/dwn/onscreenkeyboard.zip

Zdrojové kódy: http://vlko.zilina.net/dwn/onscreenkeyboard-src.zip

Na zdrojové kódy, ani obrázky si neuplatňujem žiadne autorské práva. Ak ma niekto chuť môže aplikáciu umiestniť na codeplex, alebo podobné miesto, aplikácii to určite pomôže.

VAROVANIE

Pre použitie WPF hostingu sa aplikácia stala žrútom pamäti. 85 MB mi príde na množstvo kódu a to čo má aplikácia robiť fakt prehnané. Tu je každá rada drahá, nenájde sa nejaká po ruke?

Posted: Sunday, June 01, 2008 6:01 PM by vlko
Vedeno pod: ,

Komentář

hermik napsal:

Skvělý článek, zrovna se chystám na něco podobnému, určitě mi to ušetří dost času.

# June 2, 2008 4:54 PM

Vojtěch Vít napsal:

Člověk si až říká, nakolik je WPF skutečně použitelné, když i při vývoji tak malé aplikace (byť uznávám, že nestandardní) člověk narazí na takovou řadu nedostatků a chyb... :-(

# June 2, 2008 10:31 PM

vlko napsal:

Clanok som napisal hlavne preto, aby clovek videl, aky je dolezity vyber vhodnej technologie. V tomto pripade je jasne vidno, ze WPF sa na moju ulohu nehodila.

To ale neznamena, ze WPF je nepouzitelna. Navrh UI som zvladol v takom rekordnom case a s takou funkcionalitou, aku by som z winforms takto rychlo nevytiahol. WPF ma velmi zaujalo, asi preto, ze som hlavne web vyvojar a XAML sa najviac blizi sposobu vyvoja HTML stranok.

Teda z neschopnosti je mozne obvinit len mna, pretoze som podcenil navrhovu cast, ale na druhu stranu to bolo velmi zaujimavych par dni, ktore urcite nelutujem:)

# June 3, 2008 9:00 AM

Vojtěch Vít napsal:

No já jsem právě také spíše webový vývojář, a tak mne WPF rovněž celkem zaujala.

Mohu se proto zeptat, jakou jinou technologii byste po zhodnocení WPF řešení spíše volil?:-)

# June 3, 2008 10:57 AM

vlko napsal:

Ak myslite konkretne tento projekt, tak tu by sa najviac hodilo ciste C++ (MFC). Potom asi na dalsom stupienku je delphi, pripadne nieco podobne. Na tretom su normalne winformsy a az potom WPF.

Okrem WPF, by som ale musel vela programovat, pretoze moja onscreen klavesnica pouziva nestvorcove ovladacie prvky, takze by som musel urobit nieco ako ImageMap a pracne zadefinovat regiony pre kazdu klavesu, to by uz skor clovek asi urobil nejaky tool, ktory by to robil v userfriendly rozhrani.

# June 3, 2008 1:28 PM

Viktor napsal:

Paradny clanok, vsetka cest. Skusal som nieco podobne nakodit v c# ale nevedel som prist na tu fintu s createparams. Good job :)

# October 15, 2008 5:17 PM

Violeta napsal:

Witam. Odwiedzam ten blog codziennie. Gdy nie mogę się nzpatraeć   kilka razy dziennie. Dziękuję za wiele inspiracji, a nawet wzruszeń. Sama założyłam blog 2 dni temu. Wreszcie. Jest tyle piękna do pokazania, do podzielenia się nim, nieprawdaż? Pozdrawiam najserdeczniej autorkę i wszystkich obserwatorf3w. AOB

# March 26, 2012 11:48 PM

ofeoug napsal:

# March 27, 2012 8:18 PM

cczhqhfpv napsal:

19pBOy , [url=http://bpqtvjgiajhg.com/]bpqtvjgiajhg[/url], [link=http://dwsoespetvjo.com/]dwsoespetvjo[/link], http://hkokhtlyyeoo.com/

# March 28, 2012 1:04 AM

fouceo napsal:

# March 28, 2012 6:44 AM

rzsniwza napsal:

AqZbVV , [url=http://noqidojjoboc.com/]noqidojjoboc[/url], [link=http://wkbcujoilblw.com/]wkbcujoilblw[/link], http://tmgaccotfphd.com/

# March 29, 2012 3:38 AM
Vytvoření nového komentáře

(povinný) 

(povinný) 

(nepovinný)

(povinný) 

Opiš čísla, která vidíš na obrázku:

Upozornění na nové komentáře

Pokud chčeš dostávat upozornění emailem na změny u toho příspěvku,tak se zaregistruj zde.zde

Odebírat komentáře k tomuto příspěvku pomocí RSS