Avatar
Elisse
Člen
Avatar
Elisse:

Zdravím, potřeboval bych prosím poradit, jak napsat key bind (aplikace bude reagovat například na zmáčknutí F2 i když budu úplně v jiné aplikace (příklad. Push to talk u TeamSpeaku), hádám, že budu muset zabrousit do WinAPI? Nebo to jde nějak jednodušeji? Díky moc :)

 
Odpovědět 29. července 19:06
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Elisse
David Oczka:

Ahoj. Není to zase tak těžké. :-)

Je to sice postup pro WinForms, ale nevidím důvod, proč by to nemělo fungovat i ve WPF...

Krok 1 - Do těla třídy je třeba přidat:

[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr windowHandle, int idOfShortCut, int keyModifiers, int keyCode);
[DllImport("user32.dll")]
public static extern bool UnregisterHotKey(IntPtr windowHandle, int idOfShortCut);

// Identifikační číslo klávesové zkratky
const int HOTKEY_ID_FOR_F2 = 1;

Krok 2 - Registrace zkratky při inicializaci:
V tomto kódu je pod instancí this ukrytá instance mého hlavního formuláře. Kód může být umístěn v konstruktoru formuláře nebo třeba v události Load.

// Modifikační hodnoty kláves: Alt = 1, Ctrl = 2, Shift = 4, Win = 8
// Kombinace je možná pomocí sčítání (Jde v podstatě o maskování)
// ALT+CTRL = 1 + 2 = 3 , CTRL+SHIFT = 2 + 4 = 6...
RegisterHotKey(this.Handle, HOTKEY_ID_FOR_F2, 0, (int)Keys.F2);

Krok 3 - Odchycení klávesy:

protected override void WndProc(ref Message m)
{
    if (m.Msg == 0x0312 && m.WParam.ToInt32() == HOTKEY_ID_FOR_F2)
    {
        // Udělej, co potřebuješ...
    }

    base.WndProc(ref m);
}

Krok 4 - Uvolnění zkratky:
Je vhodné tento kód zavolat v události FormClosing.

UnregisterHotKey(this.Handle, HOTKEY_ID_FOR_F2);

Snad jsem na nic nezapomněl... Dej pak vědět, jestli Ti to funguje... ;-)

 
Nahoru Odpovědět 31. července 1:27
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Hmmm máš pravdu vypadá to jednoduše :) Vyzkouším hnedka až k tomu zase sednu.

 
Nahoru Odpovědět 1. srpna 13:16
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Ještě takový dotázek jen, jak todle funguje v případě zavolání něčeho co už něco používá, bez problému to funguje a zkrátka se to zavolá v obou aplikacích? Nebo s tím může být nějaký problém?

 
Nahoru Odpovědět 1. srpna 13:33
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Elisse
David Oczka:

No, vzhledem k tomuto,

base.WndProc(ref m);

by to problém být neměl.

Tím je vlastně řečeno, že jakmile provedeš tu vlastní implemetaci, zavolá se původní metoda, kterou přepisuješ. Pokud to tedy chápu správně, tak by v případě shody té zkratky (v případě, že by tu metodu přepisovalo více aplikací), bylo nějaké pořadí, ve kterém si ty aplikace splní tu vlastní implemetaci až se to vrátí až na tu původní nepřepsanou metodu.

Takhle to chápu já, ale jestli se pletu nebo má někdo lepší vyvětlení, prosím o doplnění... :)

 
Nahoru Odpovědět 1. srpna 15:00
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Bezva to zní dobře, dám pak tedy vědět jak jsem dopadl až k tomu sednu :)

 
Nahoru Odpovědět 1. srpna 15:23
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Zdravím, přidal jsem řádek

RegisterHotKey(this.Handle, HOTKEY_ID_FOR_F2, 0, (int)Keys.F2);

k eventu Form_Loaded

Nicméně mi prej chybí reference pro Handle . Knihovny jsem samozřejmě importoval.

 
Nahoru Odpovědět 2. srpna 13:03
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Elisse
David Oczka:

Zkus toto...

private void Window_Loaded(object sender, RoutedEventArgs e)
{
        IntPtr myHandle = new WindowInteropHelper(this).Handle;
}
 
Nahoru Odpovědět 2. srpna 13:11
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na David Oczka
David Oczka:

Ještě jsem zapomněl uvést, že v případě, že by šlo o jiné než hlavní okno, tak místo this uvedeš instanci toho daného okna... Třeba:

IntPtr myHandle = new WindowInteropHelper(myWindow).Handle;
 
Nahoru Odpovědět 2. srpna 13:19
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Jasné, mám to v main formu :) teď ovšem vůbec nemám tušení jak upravit tu metodu na zachycení klávesy 8-|

Editováno 2. srpna 13:36
 
Nahoru Odpovědět 2. srpna 13:35
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Elisse
David Oczka:

No, ono to řešení pro WPF úplně fungovat nebude, tak se omlouvám za špatný návod... Ale je to alespoň nová zkušenost pro mě... ;)

Tady máš řešení:

Do referencí je ale třeba z Assemblies přidat System.Window­s.Forms a pak přidat stejnojmenný using.

// Importy
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, UInt32 fsModifiers, UInt32 vlc);

[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

// Identifikační číslo klávesové zkratky
const int HOTKEY_ID_FOR_F2 = 1;

// Handle pro naše okno
IntPtr myHandle;

// Událost Loaded (Handle pro okno zde již 100% existuje)
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    myHandle = new WindowInteropHelper(this).Handle;
    var source = PresentationSource.FromVisual(this) as HwndSource;
    source?.AddHook(WndProc);

    // Pozor! Toto nebude fungovat, protože enum Key z WPF má jiné hodnoty kláves
    // než enum Keys z WinFormů. Proto máme ten using System.Windows.Forms
    // RegisterHotKey(myHandle, HOTKEY_ID_FOR_F2, 0, (int)Key.F2);

    RegisterHotKey(myHandle, HOTKEY_ID_FOR_F2, 0, (int)Keys.F2);
}

// Odchycení klávesové zkratky
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == 0x0312 && wParam.ToInt32() == HOTKEY_ID_FOR_F2)
    {
        this.Title = "Funguje to!";
    }
    return IntPtr.Zero;
}

// Událost Closing
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    UnregisterHotKey(myHandle, HOTKEY_ID_FOR_F2);
}

Docela dlouho jsem se zasekal na tom, než mě napadlo zkontrolovat hodnoty těch kláves ve WPF a WF... %P

Teď už by to snad mělo být v pohodě... Mě to ve WPF aplikaci funguje, tak pak dej vědět, co u Tebe... :-`

Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět 2. srpna 16:12
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Paráda, vše funguje jak má! :) Díky todle bych já nikdy nevymyslel, si musím někde uložit ať se mi to neztratí :) Moc děkuji

 
Nahoru Odpovědět 2. srpna 20:18
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Zdravím, mohl bych poprosit ještě o pomoc s tím když budu chtít nastavit těch bindů více? Zkoušel jsem různé varianty a ve finále se mi dařilo aby fungoval ten bind jen jeden zároveň.

 
Nahoru Odpovědět 3. srpna 23:45
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Elisse
David Oczka:

Jsem celkem zvědavý, co se Ti povedlo stvořit... Nicméně, tady:

// Importy
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, UInt32 fsModifiers, UInt32 vlc);

[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

// Identifikační číslo klávesové zkratky
const int HOTKEY_ID_FOR_F2 = 1;
const int HOTKEY_ID_FOR_F3 = 2;

// Handle pro okno
IntPtr myHandle;

// Loaded událost
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    myHandle = new WindowInteropHelper(this).Handle;
    var source = PresentationSource.FromVisual(this) as HwndSource;
    source?.AddHook(WndProc);

    RegisterHotKey(myHandle, HOTKEY_ID_FOR_F2, 0, (int)Keys.F2);
    RegisterHotKey(myHandle, HOTKEY_ID_FOR_F3, 0, (int)Keys.F3);
}

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == 0x0312)
    {
        switch(wParam.ToInt32())
        {
            case HOTKEY_ID_FOR_F2:
                this.Title = "Funguje!";
                break;

            case HOTKEY_ID_FOR_F3:
                this.Title = "Pořád funguje!";
                break;

            default:
                break;
        }

    }

    return IntPtr.Zero;
}

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    UnregisterHotKey(myHandle, HOTKEY_ID_FOR_F2);
    UnregisterHotKey(myHandle, HOTKEY_ID_FOR_F3);
}
 
Nahoru Odpovědět 4. srpna 9:49
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Hmm pokud vím udělal jsem prakticky to stejné jen bez switche a měl jsem tam 2x rozdílné if :-O No mrknu na to zase až nebudu v práci, děkuji :)

 
Nahoru Odpovědět 4. srpna 10:36
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Elisse
David Oczka:

A použil jsi určitě enum Keys?

 
Nahoru Odpovědět 4. srpna 10:47
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Určitě ano, u té registrace jsem používal ten stejný řádek že jo...

RegisterHotKey(myHandle, HOTKEY_ID_FOR_??, 0, (int)Keys.??);
RegisterHotKey(myHandle, HOTKEY_ID_FOR_??, 0, (int)Keys.??);

Jen jsem měnil otazníčky :)

Až k tomu doma sednu můžu poslat přesně to co mi nešlo :)

Editováno 4. srpna 12:31
 
Nahoru Odpovědět 4. srpna 12:30
Avatar
Elisse
Člen
Avatar
Odpovídá na David Oczka
Elisse:

Ahh najít chybu mi trvalo asi 30 sekund po otevření kódu, return z WndProc na špatném místě :)

Jen menší point vzhledem k absenci původního base.WndProc(ref m); již nelze otevřít program aby fungovaly bindy ve všech nově otevřených instancích programu :)

PS: nepotřebuju to jen tak pro upozornění, které je ti ale asi jasné.

 
Nahoru Odpovědět 4. srpna 21:59
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Elisse
David Oczka:

Nedalo mi to, tak jsem si ověřil testem pravdivost svého tvrzení a musím se bohužel opravit. Jednu klávesou zkratku může v jednu chvíli využívat pouze jedna aplikace a to ta, která si ji zaregistruje jako první. Otestoval jsem to pro WinFormové a WPF aplikace i vzájemně mezi nimi. Nevím, jestli to jde v jiných jazycích, nějakým jiným přístupem, ale v C# to tímto způsobem nelze.

Takže se omlouvám a ruším své tvrzení o tom, že se někde vytváří fronta pro klávesové zkratky, které jsou zavoláním base.WndProc(ref m); provedeny. O:-)

Možná nás mělo trknout, že metoda RegisterHotKey je typu bool a navrací false v případě obsazené zkratky, také v případě, že je volána z konzolové aplikace (To se mimochodem řeší skrytým formulářem), anebo pokud byla zavolána z jiného vlákna, než ve kterém bylo vytvořeno okno, kterému chceme zkratku přiřadit... :-)

Teď je již doufám toto vlákno plně vyřešeno... 8-)

 
Nahoru Odpovědět  +3 6. srpna 21:11
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 19 zpráv z 19.