Diskuze: WPF - Global key bind
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Člen
Zobrazeno 19 zpráv z 19.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
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...
Hmmm máš pravdu vypadá to jednoduše Vyzkouším hnedka až k tomu zase sednu.
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?
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í...
Bezva to zní dobře, dám pak tedy vědět jak jsem dopadl až k tomu sednu
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.
Zkus toto...
private void Window_Loaded(object sender, RoutedEventArgs e)
{
IntPtr myHandle = new WindowInteropHelper(this).Handle;
}
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;
Jasné, mám to v main formu teď ovšem vůbec nemám tušení jak upravit tu metodu na zachycení klávesy
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.Windows.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...
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...
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
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ň.
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);
}
Hmm pokud vím udělal jsem prakticky to stejné jen bez switche a měl jsem tam 2x rozdílné if No mrknu na to zase až nebudu v práci, děkuji
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
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é.
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.
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...
Zobrazeno 19 zpráv z 19.