čtvrtek 26. ledna 2012

WPF DataGrid - scrollovaní na pozici

Ve WPF DataGridu jsem potřeboval zobrazit jednu položku která je kdekoliv v tabulce. Tabulka má hodně řádků a já chci do zobrazení dostat nějaký řádek, který není vidět.

Je to velice jednoduché:

datagrid.ScrollIntoView(next);

Položka se zviditelní, ale bohužel se může objevit kdekoliv v pohledu. Může se zobrazit na první zobrazené pozici nebo naopak na poslední pozici. To se děje podle toho, jestli scrollujete nahoru nebo dolu. Je to užitečná funkce, ale já jsem dostal v práci zadání, že zobrazená položka musí být na první zobrazené pozici. Jak na to? Dlouho jsem pátral po tom, jakým způsobem se mám napíchnout na scrollbar uvnitř gridu, a také jsem si myslil, že to ani není možné. Dnes jsem našel kód, který vyhledá srollbary přes vizuální strom. Já jsem hledal chybně scrollbary, ale měl jsem hledat tzv. ScrollViewer.

Příklad funkce, která vrátí ScrollViewer:
private static ScrollViewer GetScrollbar(DependencyObject dep)
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dep); i++)
    {
        var child = VisualTreeHelper.GetChild(dep, i);
        if (child != null && child is ScrollViewer)
            return child as ScrollViewer;
        else
        {
            ScrollViewer sub = GetScrollbar(child);
            if (sub != null)
                return sub;
        }
    }
    return null;
}

pátek 2. prosince 2011

WPF - Drag and Drop

Dnes jsem začal do WPF aplikace implementovat základní dovednost ve většině aplikací - Drag and Drop. Pojďme se podívat, jak Drag and Drop ve WPF funguje.

č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

pondělí 28. listopadu 2011

Složitost O(n)

Pokud softwarový designer navrhuje celý aplikační systém a vytváří vlastnosti tohoto systému jen z případu užití - Use cases, očekává od systému něco co navrhl. Dle mého názoru by si ale měl i designér, který není programátorem měl uvědomit, že jeho návrhy mohou významně ovlivnit většinu algoritmů v systému. Výsledkem čehož je pomalejší provádění všech algoritmů a větší čas potřebný ke zpracování. Vezměme si například geniální myšlenku využívání dynamických datových struktur namísto statických. A k tomu si přidejme navíc využívání multihodnot namísto jednohodnotových položek.

Poznámka:
Příkladem multihodnoty z běžného života je telefon. Mohu mít více telefonních čísel, proto se hodnota telefon v dynamické struktuře vyskytuje vícekrát.

Podívejme se na to, jak se změní v celém systému složitosti algorimu po zavedení dynamických datových struktur. Pro zjednodušení neuvažujeme že hodnotou u dynamické datové struktury může být jakýkoliv datový typ, vliv variantního typu a nutnosti přetypování je zanedbán.

A . Máme statickou datovou strukturu
LibraryElement
• elementID
• Artist[50]
• Title[50]

B. Máme dynamickou datovou strukturu
LibraryElement – je pole
Item[1] – elementID
Item[2] – Artist
Item[3] – Artist
.
.
.
Item[10] – Title
Zpracování jedno údaje ve struktuře má složitost O(1).
Zpracování jedné informace
Příklad 1. Abych zobrazil jednu položku např. Artist potřebuji složitost
M – počet atributů nebo chceteli počet položek ve struktůře
A. O(1)
B. O(2) – obecně O(M)

Zpracování pole informací – zpracování celé knihovny
Příklad 2. Zobrazení knihovny o N prvcích
A – počet zobrazovaných atributů
A. O(N*A)
B. O(N*A*M)
Příklad 3.Třídění informací – quicksort O(N log2 N)
A. O(N log2 N)
B. O((N*M) log2 (N *M))
Příklad 4. Přenos informací – složitost potřebná k serializaci a deserializaci

A. O(N) – u statické struktůry tedy mohu mluvit jednoduše o přenosu X KB/s
Vím, že chci úřenést (2b + 50+50) * N
B. O(N*M) - u statické struktury není správné mluvit o X KB/s … protože proces serializace a deserializace má mnohem větší složitost

WPF - Každé okno v aplikaci má vlastní vlákno - STA = Single threaded apartments, MTA = Multi-Threaded Apartments

Ve chvíli, kdy založíte nový projekt ve Visual Studio, vygeneruje se vám základní kostra aplikace. Která podle typu projektu WinForm nebo WPF vytvoří vstupní bod aplikace.

WinForm - funkce main
WPF - funkce main je skrytá, ale je možné ji přetížit a napsat si vlastní

Funkce main je vstupním bodem aplikace a je prováděna v hlavním vlákně aplikace (MainThread). Toto vlákno je nastaveno jako STA - Single thread apartments. Každé okno, které vytvoříme v aplikaci běží v rámci toho STA (kontejnéru na vlákna). Pokud budeme chtít vytvořit jiné vlákno, které bude provádět nějakou činnost, tak toto vlákno má nastaven apartments na MTA (multi-thread apartments). Data z tohoto vlákna můžeme synchronizovat do okna aplikace pomocí dispečeru.

myThread = new Thread(new ThreadStart(Execute));
myThread.Start();

V tomto vlákně máme nějaká omezení. Nemůžeme v něm vytvářet okna, ovládací prvky atd. Pokud chcete vytvořit nové okno v rámci jiného vlákna než je hlavní vlákno aplikace, vytvořte nové vlákno, ale nastavte mu apartments na STA.

myThread = new Thread(new ThreadStart(Execute));
myThread.Name = "Thread";
myThread.SetApartmentState(ApartmentState.STA);
myThread.IsBackground = true;
myThread.Start();

Nyní máme vytvořeno okno, ve kterém můžeme vytvořit nezávislé okno, ale aby okno fungovalo, musíme v rámci tohoto vlákna spustit dispečeram který se bude starat o případné synchronizace s jinými vlákny. Bez dispečera nebude okno fungovat.

oid Execute()
{
  MyWindow win = new MyWindow();
  win.Show();

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

Nezávislé okno máme hotovo.

Závěr
Hledal jsem příčinu, proč jsou takto pod .NETem vlákna řešena. Toto řešení se mi zdálo zbytečně komplikované. Ve většině článků bylo odůvodnění, že je to z důvodů zpětné kompatibility s COM objekty. Do hlubšího zkoumání jsem se už nepouštěl!

čtvrtek 24. listopadu 2011

1. Paralerní programování - Datový paralelismus

V .NET frameworku 4 můžeme využívat tzv. datový paralelismus. Pokud máme v aplikaci nějaký sekvenční algoritmus, který například prochází nějaké velké pole (vřádu desítek tisíc položek), tak může operace trvat dlouho, což může mít za následek výtuhnutí (zaneprázdnění) hlavního vlákna.

1. Příklad - Potřebujeme setřídit data, ale třídíme velké množství položek - sekvenčně pomocí LINQ

public void Sort()
{            
 _log.Debug("Begin Sort!"); 
 var elements = (from val in this select val).OrderBy(a => a, _comparer);
 List<LibraryElement> sort_list = elements.ToList();
 base.Clear();
 base.AddRange(sort_list); 
 _log.Debug("End Sort!"); 
}

Stejný příklad pomocí datového paralelizmu
2. Příklad - Potřebujeme setřídit data - paralelně pomocí PLINQ

public void Sort()
{            
 _log.Debug("Begin Sort!"); 
 var elements = (from val in this.AsParallel() select val).OrderBy(a => a, _comparer);
 List<LibraryElement> sort_list = elements.ToList();
 base.Clear();
 base.AddRange(sort_list); 
 _log.Debug("End Sort!"); 
}

Data jsme setřídili mnohem rychleji, protože TPL nabízí jednoduchý způsob, jak v aplikaci umožnit paralelní zpracování – tedy co nejefektivnější zpracování dat s využitím všech dostupných jader procesoru.

středa 23. listopadu 2011

Paralelní programování v .NET

Knihovna TPL je rozhraní API ze jmenných prostorů System.Threding, System.Threading.Tasks v .net frameworku 4. Knihovna umožňuje paralelně zpracovávat úlohy (tasks), která úkoly rozkládá dyamicky na všechny procesory. Použití TPL rozloží požadovaný úkol na části, které naplánuje do vláken a spouští s využitím fondu vláken ThreadPool (design pattern). Při provádění úlohy máme požnost sledovat stav provádění nebo úlohu přerušit.

Knihovna TPL se dělí:
Datový paralelizmus
Úkolový paralelizmus
PLINQ