Jarda Jirava

Vývojář a architekt řešení postavených na technologii .net framework. Zabývám se jak vývojem webových aplikací za pomoci asp.net, tak také desktopových aplikací winform. Při návrhu řešení a samotném vývoji pak využívám dlouholetých zkušeností se zpracováním obchodní logiky a pravidel aplikací získaných z vývoje komerčních aplikací pro finanční a bankovní instituce.

Microsoft MVP

Microsoft MVP - Client App dev

Silverlight minesweeper

Get Microsoft Silverlight

Navrhi COOL design a vyhraj zkušební let po Evropě!

Poslední příspěvky

code snippet

20
XI

PagerControl - fotbalově konferenční

Asi se divíte proč je nadpis takový, jaký jsem zvolil. Je to tak, ten podnět přišel opět z konference VS-NET. Jenže teď měl trošku jinou příchuť, příspěvek jsem četl, ale nepřišlo mi, že bych musel reagovat. Souběh událostí ovšem tomu chtěl a tak jsem se dostal do příjemné restaurace na fotbal - jak jinak než fandit našim proti Norům. Nešel jsem sám, ale s několika kolegy z práce. A tak se stalo, že jsem se potkal s Richardem, bývalým to kolegou mého současného spolupracovníka.

Tak nějak v rámci oslav jsme se v rámci hovoru dostali až k programování a jeho problému. Přestože jsem tvrdil, že to co požaduje jde udělat jednoduše a bez velké námahy, příliš mi nevěřil. A tak jsem se rozhodl, že jej přesvědčím. Jen doufám, že jsem jeho záměr pochopil správně - přeci jen nálada po vítězství nebyla pro analýzu problému vhodná. Raději přejdu k popisu samotného prvku ....

Uživatelský prvek PagerControl

Účelem tohoto prvku je vykreslit na stránku dle zadaných vlastností řadu LinkButtonů, které představují počet datových stránek a na základě kliknutí na některý z nich, předají zpracovávající stránce o tomto informaci. Veškeré důležité informace jsou uchovány ve ViewState tohoto prvku.

Rozhraní tohoto prvku tedy tvoří:

    Vlastnosti:
  • TotalPages: počet datových stránek, které se mají zobrazit
  • CurrentPage: aktuální datová stránka
  • PagingType: možnost výběru stánkování, na výběr je mezi seznamem všech datových stránek, nebo možnost procházet vpřed a vzad
    Událost:
  • PageSelected: vyvolaná v okamžiku, kdy dojde ke změně datové stránky kliknutím na LinkButton

Jak je vidět z popisu rozhraní, jedná se o poměrně jednoduchý prvek, který je možné dále rozšířit o případné další vlastnosti. Co mě napadá je přidání vlastností pro hezčí zobrazení prvku na stránce. To ovšem považuji za poměrně jednoduchý krok a důležitější bylo ukázat, jak zajistit funkcionalitu takového prvku.

Snad Ti tedy, Richarde, přinese prvek PagerControl vyřešení problému a udělá radost stejně velkou, jakou udělali čeští fotbalisté radost fotbalovému národu [no dobrá, tak velká asi nebude, ale přeci jen ...].

Publikováno pod: code snippet
8
V

Enter jako tabulátor

Podnět k napsání tohoto příspěvku přišel z dotazu do konference o .NET VSNET-L. A to konkrétně WinForms: poradi controlu a jak se stiskem Enter presunout na další textbox :-). A to hlavně první část otázky.

Když jsem si přečetl odpovědi, obzvláště tu, ve které se radí podědit použité prvky vzpoměl jsem si na své začátky v OOP. Ano, takhle se to dá také řešit. Teda pokud chcete pro každou novou věc dědit ze současného stavu prvků a psát kód několikrát. Brrr. Dovedu si představit takový chytrý prvek, který bude úžasný a dokáže splnit všechno co si programátor dokáže jen přát. Co s ním však v další aplikaci? Co s jeho údržbou a dalším rozvojem? Co když se mi bude líbit nějaký jiný povedený prvek a já k němu budu chtít doplnit všechny mé úžasné vychytávky? Skutečně je budu muset přepisovat a vytvářet další úroveň ve třídní hierarchii?

Použijeme IExtenderProvider

Dost už černých myšlenek. Na takovéhle vychytávky je tu přeci IExtenderProvider. Jestliže chceme rozšířit funkčnost prvků, můžeme použít toto jednoduché rozhraní. Asi nemá význam se teď rozepisovat o tom, jak jej implementovat, je to skutečně jednoduché a spoustu příkladů je možné nalézt třeba na CodeProject.

EnterAsTabProvider

K vyřešení tazatelovi otázky je tak možné přistoupit ještě jinak. A proto jsem napsal velice jednoduchý provider, který zajistí posun na následující prvek v okamžiku stisku klávesy Enter. Nazval jsem jej jednoduše EnterAsTabProvider. Na co je ovšem důležité myslet! Na obrazovce jsou umístěny takové prvky, kterým by funkčnost stisku klávesy Enter neměla být upřena. Dokonce je vyžadováno, aby tuto funkci podporovali a uživatel z nich po stisku klávesy neodskočil. Narychlo mě napadá třeba stisk tlačítka. A tak jsem zpřístupnil vlastnost pro možnost aktivace/deaktivace chování (posunu) po stisku klávesy Enter.

Vlastní kód

	[ProvideProperty("Enabled", typeof(Control))]
	public class EnterAsTabProvider: Component, IExtenderProvider  {

		/// 
		/// Store properties for extendee control
		/// 
		private Hashtable properties;

		#region Public contructor
		public EnterAsTabProvider() {
			properties = new Hashtable();
		}

		public EnterAsTabProvider(IContainer container): this() {
			container.Add(this);
		}
		#endregion 

		#region IExtenderProvider Members

		/// 
		/// Can extend Controls w/o Form and ToolBar
		/// 
		/// 
		/// Gets info about can extend object
		public bool CanExtend(object extendee) {
			if ((extendee is Control) && !(extendee is Form)) {
				return !(extendee is ToolBar);
			}
			return false;
		}

		#endregion

		/// 
		/// Is move enabled for this Control
		/// 
		/// attached Control
		/// Gets info if provider is enabled or disabled
		public bool GetEnabled(Control control) {
			return GetControlProperties(control).Enabled;
		}

		/// 
		/// Sets info if provider will be enabled or disabled
		/// 
		/// Control
		/// set enabled or disabled provider
		public void SetEnabled(Control control, bool enabled) {
			GetControlProperties(control).Enabled = enabled;
		}


		/// 
		/// Provide properties for Control
		/// 
		/// Control
		/// gets EnterAsTabExtendedProperties
		private EnterAsTabExtendedProperties GetControlProperties(Control control) {
			if (properties.Contains(control)) {
				return properties[control] as EnterAsTabExtendedProperties;
			} else {
				EnterAsTabExtendedProperties prop = new EnterAsTabExtendedProperties();
				properties.Add(control, prop);
				control.KeyDown += new KeyEventHandler(KeyDownHandler);
				return prop;
			}
		}

		/// 
		/// This is the main method for this provider. It provides functionality for changing enter to tab.
		/// 
		/// sender, Control on which was the key pressed
		/// KeyEventArgs information
		private void KeyDownHandler(object sender, KeyEventArgs e) {
			Control control = sender as Control;
			if ((e.KeyCode == Keys.Enter) && (control != null)) {
				if (GetControlProperties(control).Enabled)
					if (control.FindForm().SelectNextControl(control, true, false, true, true))
						e.Handled = true;
			}
		}

		/// 
		/// private class to store information 
		/// 
		private class EnterAsTabExtendedProperties {
			public bool Enabled = true; 
		}

Tento kód stačí umístit do samostatné assembly a zpřístupnit si tento prvek na Toolbaru. Přetažením tohoto prvku na formulář se nám ukáže u každého prvku, poděděného od Control (mimo Form a ToolBar) další vlastnost, nejspíše pojmenovaná jako Enabled on ....

Meze dalšímu rozvoji se nekladou a budu jen rád, pokud mi tady dáte vědět, jak jste jej rozšířili vy.

Věřím, že tento způsob rozšiřování funkčnosti se vám bude zamlouvat více, než na začátku zmiňované dědění. Příjemnou zábavu a hezký den.

Publikováno pod: code snippet
22
I

Static file Handler - servírujeme návštěvníkům soubory

Konečně je na čase, abych tady zmínil také něco o .NETu, o kterém by tento log měl být především. Mám v hlavě pár témat, která tu chci uveřejnit, jedním z nich je něco o tom, co může pomoci v každodenní rutině. A také o tom, jak vlastně vznikal tento systém, který publikuje moje výplody.

Static file HttpHandler

Vesměs v každé aplikaci, kterou píši je potřeba poslat na klienta nějaký statický soubor. Za příklad se dá vzít obrázek. Jenže tenhle obrázek se musí použít na více stránkách a navíc ještě v různých "zanořeních" v adresářové struktuře webu. To byl můj problém i tady, nabídnout soubory kdekoli ve struktuře webu a to pomocí jednoduchého odkazu, i když zrovna nevím odkud se ten soubor bude volat.

A právě k tomu jsem využil možností, které nabízí .NET.

Dobrá, teď někdo může namítnout, že o HttpHandlerech toho bylo napsáno i v českých končínách dost. Jsou to hlavně články Reného Steina [Vytvoření nového http handleru v ASP.NET] nebo Michala Altair Valáška [ Pohled do hlubin webserverovy duše (aneb jak fungují HTTP moduly a handlery)] a další se určitě ještě najdou.

Mě jde ale o to, předvést jak jsem implementoval posílání souborů, obrázků a dalších statických souborů na klienta. A to právě pomocí implementace rozhraní IHttpHandler.

Jak to celé funguje

Nejdříve bylo nutné napsat v handleru obsluhu metody ProcessRequest a namapovat tento handler ve web.configu. Což sice byla až konečná úprava, ale uvedu ji na prvním místě.

Registrace je provedena na soubor resfile.aspx (nic ale nebrání tomu, abyste si zvolili vlastní název souboru) v sekci httpHandlers takto: . Tento řádek zabezpečí, že veškeré odkazy směřující na soubor resfile.aspx budou zpracovány právě tímto handlerem.

Teď nastává druhá část řešení, napsání metody, která bude poskytovat soubory. Z důvodu větší univerzálnosti řešení jsem zvolil následující podmínky. Soubory, které budu takto publikovat se můžou nacházet kdekoli v adresářích na webu. Druhou podmínkou je, že některé soubory se budou stahovat přímo do prohlížeče, některé by se měli nabídnout návštěvníkovi ke stažení pod daným názvem souboru. Pro uchování potřebných informací jsem zvolil xml soubor, který bude obsahovat následující informace:

  • Klíč, který bude vytažen z QueryStringu a je jednoznačný pro každý poskytovaný soubor
  • Umístění souboru v adresářové struktuře od rootu webové aplikace
  • Informaci, zda do hlavičky bude zaslána informace o názvu souboru, pod kterým si uživatel může soubor uložit.
Takto jsem získal tabulku s informacemi. Byla to tabulka, takže pro načtení souboru jsem použil DataSet a ten jsem vložil do aplikační Cache pro rychlejší přístup. S tím, že je vázán na změnu daného xml souboru. Ještě je potřeba se zmínit o daném xml souboru. Protože pokud náhodou někdo odhadne název zdrojového souboru, získal by přístup k informacím v něm uloženým. Což ne vždy musí být chtěné. Takže přípona tohoto souboru je .config, což je opět mapovaná přípona na HttpHandler, konkrétně na System.Web.HttpForbiddenHandler.

Mám připravenu tabulku, ze které budu čerpat data. Teď už jen zbývá načíst soubor a odeslat jej s potřebnými informacemi na klientský počítač.

Pravda, ještě jedna nutnost tu je, zjistit content type odesílaného souboru. Ten si neukládám do tabulky, ale zjišťuji přímo pomocí koncovky souboru, při neexistenci (neznámé koncovce) pak odesílám content type application/octet-stream.

To bylo vše. Mám vše potřebné, takže zkompilovat a šup s tím do /bin adresáře na webu. Použití uvedeného handleru můžete vidět přímo na této stránce, kdy si soubor můžete pomocí uvedeného handleru stáhnout.

Snad vám představený StaticFileHandler pomůže stejně, jako pomáhá mě.

Publikováno pod: code snippet
10
XII

Reflection vs. Array

Podtitulek tohoto logu se zmiňuje o tom, že bych se zde rád věnoval střípkům z konferencí o .NETu. Dlouho jsem váhal, zda téma, které mě trklo, bude zajímavé. Ale poté, co jsem si vše vyzkoušel a napsal test, neváhal jsem.

Informace o tom, že použití reflection je pomalejší, je obecně známý fakt, se kterým jsem pracoval. Ovšem výsledky měření, které jsem provedl, mě celkem překvapily. Ale vrátím se na samý začátek, ať nevytvářím závěry dřív, než napíšu podstatu.
Jeden z dotazů do konference zněl, zda se dá ze zadaného jména proměnné, k této přistoupit a změnit její hodnotu.

On ten dotaz zněl ještě přesněji. Což mě vedlo k odpovědi, proč raději nepoužít pole. Tomas Rampas však neváhal a odpověděl, přesně podle přání dotazujícího. V ten okamžik mě napadlo, že ve svých programech používám reflection, a uživatelsky definované atributy. Zatím jsem však neprováděl měření, kterým bych zjistil k jak velkému zpomalení, díky tomuto přístupu, vše vede. Takže jsem neváhal a zkusil napsat pár tříd, na kterých jsem si chtěl ověřit onen obecně známý fakt.

Definice testování

Ač jsem absolvoval nějaké pokusy (jistě je znáte i vy ze školních škamen), jedna z mála věcí, která mi utkvěla v paměti je, že se má testovat na stejném vzorku dat. Takže jsem definoval množinu, vlastně třídu, která bude mít možnost uchovat sto hodnot typu int. K těmto hodnotám se půjde dostat pomocí metod int GetValue(int index) a void SetValue(int index, int value). Pro lepší porovnání jsem pak přistoupil ještě k možnosti, že třídy, které používají reflection, si mohou informativní údaje uschovat do HashTable. Co mě napadlo až při pohledu na kód zaslaný do příspěvku, že vytvořím ještě třídu, která bude obsahovat property, místo fieldů.

Jak jsem měřil

Mám připravené čtyři třídy, které jsem pojmenoval TestArray, TestField, TestProperty a TestPropertyHash. Každá obsahovala zmíněné dvě metody. Přistup k hodnotám pole byl realizován s otestováním na přetečení a přistupu nad/pod mez pole. Ostatní tři třídy přistupovaly ke svým property/fields pomocí reflection. Veřejné proměnné byly pojmenovány Cislo0 .. Cislo99. Z předaného čísla indexu byl sestaven název proměnné a pomocí reflection získáno PropertyInfo/FieldInfo. Rozdíl byl u testování TestPropertyHash, kdy se nejdříve zjišťovala přítomnost informací v tabulce a až poté jsem případně zjišťoval potřebné informace.
Po několika měřeních jsem zjistil, že simulovat aspoň trochu reálné prostředí zřejmě nepůjde, neboť naměřené hodnoty jsou příliš malé pro posouzení náročnosti. Takže jsem nastavil počet průchodů přes jednotlivé operace na poněkud vyšší hodnoty, než které se mohou (snad) reálně vyskytnout v aplikaci. I když ani takové chování se nemůže nikdy vyloučit. Takže nakonec jsem přistoupil k následujícím počtům.

  • Start počítání času
  • Vytvoření objektu celkem 20x
  • Počet zapsání a opětovné vyzvednutí hodnoty každé proměnné v jednom vytvořeném objektu 50x
  • Ukončení počítání času
Toto celé pak bylo zopakováno pro každou testovanou třídu 15x. Vše po dvou hodinách běhu počítače na kterém jsem v té době neprováděl žádné operace.

Malé doplnění k měření

Jelikož jsem byl při znalosti naměřených hodnot zvědavý, co s metodami GetProperty a GetField udělají zapsané BindingFlags, pustil jsem měření ještě jednou s uvedením těchto Flagů BindingFlags.Public | BindingFlags.Instance. V tabulce jsou tato měření označena jako "O".

Výsledky měření

Jak už jsem se zmínil v úvodu, měřeními jsem byl celkem překvapen, i když musím říci, že se jedná o trošku přehnané hodnoty průchodů, a ať jsem vzpomínal jak chtěl, nenarazil jsem na případ, kdy bych něco takového ve svých aplikacích prováděl. Každopádně je to zajímavé a pokud někdo uvažuje o co nejrychlejším kódu, určitě bych doporučoval se reflection vyhnout. Měření ukázalo, že nejpomalejší přístup je pomocí reflection, která získává informace o veřejných Fieldech. Za ní se v těsném závěsu drží přístup k Property. O něco lepší je přístup, kdy se použije k uchování informací Hashtable, která pak udržovala celkem konstantní čas přístupu a je to asi nejrozumější volbou, pokud je potřeba použít reflection. Samozřejmě nejrychlejší cestou je pak přímý přístup k poli, který měl v prvních chvílích až nezanedbatelné měření. Ostatně kvůli tomu, jsem zvedal "laťku" výše, abych se ujistil, že takové měření nemá spíše statistickou chybu. Jak měření dopadla, se můžete přesvědčit v následující tabulce.

Ještě je potřeba zmínit, že pro měření jsem nakonec využil kódu, který uveřejnil Petr Lazecký.

Tabulka naměřených hodnot

 Array OField OProperty OPropertyHash OArrayFieldPropertyPropertyHash
1.0,01285945614,522786568,1192849933,7901046080,0125365113,9827528,309939953,31640418
2.0,01120617314,449837628,5542866483,4394731730,01118270614,108848478,1857736113,274529863
3.0,01098575413,983554068,1735044543,1657311190,01102961414,149120078,37456663,262086382
4.0,01152017913,812216368,3883211413,2296241560,01097569713,936636088,4009163183,455275461
5.0,01096759513,61635047,9409357893,1473469390,01098100513,94958278,0877499793,135475598
6.0,01114555113,831922497,904646543,1787288610,01098184314,604324018,1616238943,274396606
7.0,01141150613,588490727,9426650593,1673120470,01163946814,413230457,9907312244,447815854
8.0,01121287814,400577737,9378163983,5124327250,012171114,400959348,5929954783,496401638
9.0,01125450313,615833027,9286213753,1681931640,01103352514,459147749,1208290443,327205832
10.0,01095949313,482833717,8598243633,1347146080,01284939813,826378218,2736738893,491689586
11.0,01133635713,823402977,9036634543,1478735430,01095474415,382351398,6365803484,733472271
12.0,01123187413,565827787,9379099863,1491915870,01152017913,967557898,3010807243,216261005
13.0,01178976713,57695887,9303143283,1488714350,01097010913,903576018,084788433,143627752
14.0,01156348113,601983217,9203647393,1561530860,01109442713,547039368,4452571494,925186022
15.0,01114275713,570303217,9052418673,1685784090,01230603313,666581378,0252752793,137395675
Avg.0,01137248813,829525248,0231600763,2469552970,01148175714,153205678,3327854613,575814915

Poohlédnutí se za výsledkem

Myslím, že k tomuto není třeba dodávat jakýchkoli slov. Čísla jsou jasná. Naměřenými hodnotami jsem byl překvapen víc, a očekával jsem o hodně menší rozdíly. Obzvláště mě pak překvapil rozdíl v přístupu k Property a Field. Můj osobní předpoklad byl spíše opačný na druhou stranu je to jen dobře, neustále zjišťuji, že se mám co učit a co zkoušet.

Budu rád, za jakékoli Vaše připomínky, které mohou pomoci nejen mě, ale i ostatním.

Publikováno pod: .net technology , code snippet , konference
22
X

DropDownList s atributy u ListItem

Včerejší den byl ve znamení menší mystifikace. Do konference o programování v .NET přišel jednoduchý dotaz, asi následujícího znění.
Při plnění ListBoxu potřebuji k jednotlivým položkám do tagu option přigenerovat ještě svoje pomocné atributy.
Neváhal jsem a na jednoduchou otázku poslal celkem jednoduchou odpověď s odkazem, že třída ListItem má vlastnost Attributes, které je možné použít pro přidávání vlastních atributů.

Myšlenka to byla dobrá, až do chvíle, kdy během několika okamžiků přišla zpět odpověď, která mě zarazila. .. fyzicky se mi do HTML k tagu option ty atributy nevypíšou .... V tu chvíli mi všechno došlo, DropDownList a ListBox mají napsané metody RenderContents trochu jinak, a výpis jednotlivých položek zajišťují sami. (Třída ListItem nemá vlastní metodu Render.)

Není však třeba zoufat, i toto se dá napravit a vytvořit si vlastní webcontrol, který bude dědit z třídy DropDownList případně ListBox a přepsat mu metodu RenderContents. Pro usnadnění práce a přemýšlení jsem se o něco takového pokusil a zde je výsledek.

 [ToolboxData("<{0}:DropDownListOptionAttribute runat=server>")] 
public class DropDownListOptionAttribute: DropDownList { 
public DropDownListOptionAttribute(): base() {} 
protected override void RenderContents(HtmlTextWriter writer) { 
bool isSelected; 
ListItemCollection lic; 
ListItem li; 
isSelected = false; 
lic = base.Items; 
if (lic.Count > 0) { 
for (int i=0; i
li = lic[i]; 
writer.WriteBeginTag("option"); 
li.Attributes.Render(writer); 
if (li.Selected) { 
if (isSelected) 
throw new HttpException("Nelze vybrat více než jednu položku v DropDownListu."); 
isSelected = true; 
writer.WriteAttribute("selected", "selected"); 
} 
writer.WriteAttribute("value", li.Value, true); 
writer.Write(">"); 
HttpUtility.HtmlEncode(li.Text, writer); 
writer.WriteEndTag("option"); 
writer.WriteLine(); 
} 
} 
} 
} 

Aktualizováno [24.10.2003]: zjistil jsem, že jsem nepřevedl všechny znaky na HTML entity a tudíž kód nebyl celý.

Publikováno pod: code snippet