čtvrtek 1. prosince 2011

Každe okno aplikace ve vlastním vlákně - pokračování

Jak jsem zmínil nedávno v příspěvku, potřebuji do své poslední aplikace, aby každý modul a hlavně také jeho okno běžel v samostatném vlákně.

Požadavky:
- Když v jednom okně provádím náročnější operaci, nesmí vytuhnout ostatní okna.
- Když dojede k pádu v jednom modulu tak potřebuji, aby ostatní moduly fungovali dál.

Jak jsem psal v předešlém článku, požadovaného výsledku dosáhneme vytvořením STA vlákna pro každé okno.

Když pak spustíme aplikaci, zjistíme, že každé okno funguje opravdu v nezávislém vlákně. Brzo si všimnete velmi nepříjemné vlastnosti takové aplikace - okna jsou opravdu na sobě nezávislá a fungují jako samostatné aplikace uvnitř vaší aplikace. Důsledkem toho vám program přestane správně fungovat při minimalizaci a opětovné maximalizaci. Po opětovné maximalizaci se vám zobrazí pouze okno hlavního vlákna. Ale kde jsou ty ostatní okna?

První myšlenka, která mě napadla bylo, nastavení vlastnosti Window.Owner. Tuto vlastnost bohužel z jiného vlákna nenastavíte, protože tímto vyvoláte kód v hlavním vlákně.

_window.Owner = Application.Current.MainWindow;

Dojde k výjímce:
The calling thread cannot access this object because a different thread owns it.

Dlouho jsem procházel nejrůznější fóra, až jsem objevil konstrukci, která tuto WPF chybku obejde.

string strFriendlyName = AppDomain.CurrentDomain.FriendlyName;
Process[] pro = Process.GetProcessesByName(strFriendlyName.Substring(0, strFriendlyName.LastIndexOf('.')));            
typeof(System.Windows.Window).InvokeMember("_ownerHandle",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetField,
null, _window, new object[] { pro[0].MainWindowHandle });
_window.Closed += new EventHandler(WindowClosed);
_window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
_window.ShowDialog();           

//System.Windows.Threading.Dispatcher.Run();

A můžeme si oddechnout, opravdu to funguje.

Odkaz na článek

1 komentář:

Mišák řekl(a)...

Poznámka: Nemusím procházet procesy. Stačí:

Process pr = Process.GetCurrentProcess();
typeof(System.Windows.Window).InvokeMember("_ownerHandle",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetField,
null, _window, new object[] { pr.MainWindowHandle });