<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>xlab &#187; reflection</title>
	<atom:link href="http://xlab.pl/tag/reflection/feed/" rel="self" type="application/rss+xml" />
	<link>http://xlab.pl</link>
	<description>XSolve laboratory - dzielimy się tym co wiemy...</description>
	<lastBuildDate>Thu, 12 Apr 2012 07:23:58 +0000</lastBuildDate>
	<language>pl</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Dynamiczne tworzenie interfejsu użytkownika przy pomocy refleksji.</title>
		<link>http://xlab.pl/dynamiczne-tworzenie-interfejsu-uzytkownika-przy-pomocy-refleksji/</link>
		<comments>http://xlab.pl/dynamiczne-tworzenie-interfejsu-uzytkownika-przy-pomocy-refleksji/#comments</comments>
		<pubDate>Sat, 19 Feb 2011 07:47:16 +0000</pubDate>
		<dc:creator>Jarek Kożdoń</dc:creator>
				<category><![CDATA[Programowanie .NET]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[reflection]]></category>
		<category><![CDATA[UI]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=1762</guid>
		<description><![CDATA[Niedawno, przy okazji rozmyślania nad konstrukcją interfejsu użytkownika dla narzędzia tworzonego dla własnych potrzeb, przypomniał mi się pewien projekt, nad którym miałem okazję pracować. Projekt nie był wielki, ale naszpikowany ciekawymi rozwiązaniami, m.in. LINQ (zarówno dla bazy danych jak i LINQ to XML), SQL Server Compact Edition, przetwarzanie bardzo sporych XMLi. Było również jedno rozwiązanie, [...]]]></description>
			<content:encoded><![CDATA[<p>Niedawno, przy okazji rozmyślania nad konstrukcją interfejsu użytkownika dla narzędzia tworzonego dla własnych potrzeb, przypomniał mi się pewien projekt, nad którym miałem okazję pracować. Projekt nie był wielki, ale naszpikowany ciekawymi rozwiązaniami, m.in. LINQ (zarówno dla bazy danych jak i LINQ to XML), SQL Server Compact Edition, przetwarzanie bardzo sporych XMLi. Było również jedno rozwiązanie, które szczególnie zapadło mi w pamięci – kontrolka do edycji danych obiektów, co ciekawe – dla wszystkich obiektów bazodanowych (a było ich trochę, nie setki – ale powiedzmy kilkanaście) była tylko i wyłącznie jedna kontrolka. Nasuwa się tutaj pytanie „ale jak?!, przecież to musiało mieć milion linii kodu!”. Ale odpowiedź brzmi „wcale nie”. Z pomocą przyszła refleksja&#8230;</p>
<p>Zamieszczam więc niniejszym skromny instruktarz, który być może kogoś zainspiruje, do kodu kontrolki obecnie dostępu nie posiadam, przedstawię więc bardziej ideę jako propozycje alternatywy dla typowego myślenia o edycji właściwości naszych obiektów.</p>
<p style="padding-left: 30px"><strong>1. Co to jest refleksja?</strong></p>
<p>Dla tych co wiedzą – przypomnienie (bądź akapit do przeskrolowania), dla pozostałych – kilka podstawowych informacji.</p>
<p><strong>Refleksja </strong>to mechanizm pozwalający na pozyskiwanie informacji o klasach, czy kodzie skompilowanych już klas (na przykład właśnie wykonywanego programu, czy też odczytanych przez niego binarek).</p>
<p>W celu użycia refleksji w projekcie <strong>.NET</strong>owym należy użyć przestrzeń nazw <strong>System.Reflection</strong>. Dzięki temu będziemy mieli dostęp do podstawowych klas refleksji czyli: <strong>Type</strong>, <strong>MethodInfo</strong>, <strong>ProprtyInfo</strong>, <strong>EventInfo</strong>,	<strong> FieldInfo</strong>, <strong>ConstructorInfo</strong>&#8230; Każda odpowiada jednemu z elementów tworzących klasy.</p>
<p>Żeby pobrać informacje dotyczące obiektu danej klasy wywołać należy na nim metodę <strong>.GetType()</strong> dziedziczoną z bazowej klasy <strong>Object</strong>.</p>
<p style="padding-left: 30px"><strong>2. Skąd wiemy co wyświetlać?</strong></p>
<p>Metod na pozyskanie interesującej nas informacji jest co najmniej kilka. Możemy pozyskać informacje o klasie wczytując plik binarny i w nim szukając, możemy szukać po nazwie klasy i jej przestrzeni nazw, ale zdecydowanie najłatwiejszym sposobem jest wywołanie wspomnianej powyżej metody <strong>GetType()</strong> na obiekcie, który nas interesuje.</p>
<p>Co wyświetlać? O tym decydujemy już wyłącznie my! Powinniśmy jednak coś założyć, w tym przykładzie wyświetlać chciałbym wszystkie publiczne <strong>Property </strong>obiektów.</p>
<p>Skoro już wiemy jak pobrać typ obiektu oraz wiemy co będziemy wyświetlać pozostaje wyłącznie pobrać listę, nie jest to nic trudnego, wystarczy wywołać metodę GetProperties() klasy Type, która zwraca tablicę obiektów PropertyInfo danego typu. Stworzymy więc taki kod:</p>
<pre style="padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">var</span></span><span style="color: #000000"><span style="font-family: Consolas"> type = ob.GetType();
</span></span><span style="color: #0000ff"><span style="font-family: Consolas">var</span></span><span style="color: #000000"><span style="font-family: Consolas"> properties = type.GetProperties();
</span></span></pre>
<p>kod ten nie spełnia jednak naszego założenia, bowiem zwrócone zostaną <span style="text-decoration: underline">wszystkie</span> publiczne properties (domyślne działanie nie zwraca prywatnych właściwości!) łącznie z tymi statycznymi (chcemy tylko właściwości obiektu). W celu pozyskania odpowiedniej listy użyć należy przeładowania metody GetProperties wraz z argumentem w postaci wartości enuma <strong>BindingFlags</strong> (dopuszczając bitowe dodawanie wielu wartości)</p>
<pre style="padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">var</span></span><span style="color: #000000"><span style="font-family: Consolas"> type = ob.GetType();
</span></span><span style="color: #0000ff"><span style="font-family: Consolas">var </span></span><span style="color: #000000"><span style="font-family: Consolas">properties = type.GetProperties(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">BindingFlags</span></span><span style="color: #000000"><span style="font-family: Consolas">.Public | </span></span><span style="color: #2b91af"><span style="font-family: Consolas">BindingFlags</span></span><span style="color: #000000"><span style="font-family: Consolas">.Instance);

</span></span></pre>
<p>Lista gotowa!</p>
<p style="padding-left: 30px"><strong>3. A co jeżeli jakiegoś pola NIE chcę wyświetlać?</strong></p>
<p>Nie, nie trzeba w takim przypadku robić dziwnych warunków czy aby dane property jest jednym z wykluczonych, czy nie, dużo bardziej uniwersalnym sposobem jest użycie <strong>atrybutu</strong>. Można działać w 2 strony – stworzyć atrybut informujący o tym, że dane pole powinno być wyświetlane, bądź atrybut informujący o tym, że jego edycja nie jest potrzebna. Pokażę drugie rozwiązanie, jako bardziej ogólne i bardziej do mnie przemawiające, poza tym pozwalające w dalszej drodze edytować wartości obiektów nie należących do danego projektu.</p>
<p>Najpierw klasa atrybutu:</p>
<p style="padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">public</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">class</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">HiddenValueAttribute</span></span><span style="color: #000000"><span style="font-family: Consolas"> : <span style="color: #2b91af">Attribute</span><br />
{<br />
}</span></span></p>
<p>Po czym umieszczamy atrybut przy Property, którego nie chcemy widzieć:</p>
<p style="padding-left: 30px"><span style="font-family: Consolas">[</span><span style="color: #2b91af"><span style="font-family: Consolas">HiddenValue</span></span><span style="color: #000000"><span style="font-family: Consolas">]</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
public</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">string</span></span><span style="color: #000000"><span style="font-family: Consolas"> HiddenName { </span></span><span style="color: #0000ff"><span style="font-family: Consolas">get</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">set</span></span><span style="color: #000000"><span style="font-family: Consolas"> }</span></span></p>
<p>a teraz modyfikacja naszej metody pobierającej listę do wyświetlenia (dodajemy również jej nazwę):</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm"><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm"><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">public</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">IEnumerable</span></span><span style="color: #000000"><span style="font-family: Consolas">&lt;</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas">&gt; GetPropertiesToDisplay(</span></span><span style="color: #0000ff"><span style="font-family: Consolas">object</span></span><span style="color: #000000"><span style="font-family: Consolas"> ob)</span></span></p>
<p style="padding-left: 30px"><span style="color: #000000"><span style="font-family: Consolas"> {</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
</span></span><span style="color: #0000ff"><span style="font-family: Consolas">var</span><span style="color: #000000"><span style="font-family: Consolas"> type = ob.GetType();</span></span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
var</span></span><span style="color: #000000"><span style="font-family: Consolas"> properties = type.GetProperties(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">BindingFlags</span></span><span style="color: #000000"><span style="font-family: Consolas">.Public | </span></span><span style="color: #2b91af"><span style="font-family: Consolas">BindingFlags</span></span><span style="color: #000000"><span style="font-family: Consolas">.Instance);</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
var</span></span><span style="color: #000000"><span style="font-family: Consolas"> returnValue = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">from</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas"> p </span></span><span style="color: #0000ff"><span style="font-family: Consolas">in</span></span><span style="color: #000000"><span style="font-family: Consolas"> properties </span></span><span style="color: #0000ff"><span style="font-family: Consolas">where</span></span><span style="color: #000000"><span style="font-family: Consolas"> p.GetCustomAttributes(</span></span><span style="color: #0000ff"><span style="font-family: Consolas">typeof</span></span><span style="color: #000000"><span style="font-family: Consolas">(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">HiddenValueAttribute</span></span><span style="color: #000000"><span style="font-family: Consolas">), </span></span><span style="color: #0000ff"><span style="font-family: Consolas">true</span></span><span style="color: #000000"><span style="font-family: Consolas">).Length == 0 </span></span><span style="color: #0000ff"><span style="font-family: Consolas">select</span></span><span style="color: #000000"><span style="font-family: Consolas"> p;</span></span><br />
<span style="color: #000000"><span style="font-family: Consolas"> }</span></span></p>
<p><span style="color: #000000"><span style="font-family: Consolas"> </span></span></p>
<p><span style="color: #000000"><span style="font-family: Consolas"> </span></span></p>
<p>Jak widać dodana została linijka wyszukująca tylko te <strong>PropertyInfo</strong>, które wśród swoich atrybutów nie mają tego o typie <span style="color: #2b91af"><span style="font-family: Consolas">HiddenValueAttribute</span></span>, w celu tym użyta zostało użyte zapytanie <strong>LINQ</strong> wywołujące metodę <strong>GetCustomAttributes</strong>, która zwraca tablicę atrybutów spełniających warunki. Warunki podawane w argumentach to typ atrybutu (opcjonalny parametr, w drugim przeładowaniu metody nie występuje) oraz czy brać pod uwagę dziedziczone atrybuty.</p>
<p style="padding-left: 30px"><strong>4. Jak wyświetlać?</strong></p>
<p>Przechodzimy teraz do najistotniejszego punktu – wyświetlanie. Założymy, że wyświetlamy kontrolki na obiekcie klasy Panel o nazwie&#8230; panel!</p>
<p>Metoda wyświetlająca kosztować będzie nas najwięcej pracy – tutaj generalnie przewidzieć będziemy musieli wszystkie potencjalnie wyświetlane typy oraz obsługę każdego z nich.</p>
<p>Ale powoli.</p>
<p>Zacznijmy od organizacji naszego panelu, proponuję wyświetlać wartości w wierszach, w każdym etykieta z nazwą i odpowiednia kontrolka dla wartości &#8211; taka para dla każdej Property naszego obiektu. Czyli na przykład:</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">private</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">void</span></span><span style="color: #000000"><span style="font-family: Consolas"> CreateControlsForProps(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">IEnumerable</span></span><span style="color: #000000"><span style="font-family: Consolas">&lt;</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas">&gt; properties, </span></span><span style="color: #0000ff"><span style="font-family: Consolas">object</span></span><span style="color: #000000"><span style="font-family: Consolas"> ob)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
foreach</span></span><span style="color: #000000"><span style="font-family: Consolas"> (</span></span><span style="color: #0000ff"><span style="font-family: Consolas">var</span></span><span style="color: #000000"><span style="font-family: Consolas"> prop </span></span><span style="color: #0000ff"><span style="font-family: Consolas">in</span></span><span style="color: #000000"><span style="font-family: Consolas"> properties)<br />
{</span></span><span style="color: #2b91af"><span style="font-family: Consolas"><br />
Panel</span></span><span style="color: #000000"><span style="font-family: Consolas"> row = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Panel</span></span><span style="color: #000000"><span style="font-family: Consolas">();<br />
row.Dock = </span></span><span style="color: #2b91af"><span style="font-family: Consolas">DockStyle</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">.Top;</span><br />
row.Height = 36;</span><span style="color: #0000ff"><span style="font-family: Consolas"> </span></span></p>
<p style="padding-left: 30px">this<span style="color: #000000"><span style="font-family: Consolas">.panel.Controls.Add(row);</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
row.Controls.Add(</span></span><span style="color: #0000ff"><span style="font-family: Consolas">this</span></span><span style="color: #000000"><span style="font-family: Consolas">.GetLabelForProperty(prop));</span></span><span style="color: #0000ff"><span style="font-family: Consolas"> </span></span></p>
<p style="padding-left: 30px">if<span style="color: #000000"><span style="font-family: Consolas"> (prop.PropertyType == </span></span><span style="color: #0000ff"><span style="font-family: Consolas">typeof</span></span><span style="color: #000000"><span style="font-family: Consolas">(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">String</span></span><span style="color: #000000"><span style="font-family: Consolas">))</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="font-family: Consolas"><br />
row.Controls.Add(</span><span style="color: #0000ff"><span style="font-family: Consolas">this</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">.GetInputForString(prop));</span><br />
}</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
else</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">if</span></span><span style="color: #000000"><span style="font-family: Consolas"> (prop.PropertyType == </span></span><span style="color: #0000ff"><span style="font-family: Consolas">typeof</span></span><span style="color: #000000"><span style="font-family: Consolas">(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">Int32</span></span><span style="color: #000000"><span style="font-family: Consolas">))</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="font-family: Consolas"><br />
row.Controls.Add(</span><span style="color: #0000ff"><span style="font-family: Consolas">this</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">.GetInputForNumber(prop));</span><br />
}</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
else</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">if</span></span><span style="color: #000000"><span style="font-family: Consolas"> (prop.PropertyType == </span></span><span style="color: #0000ff"><span style="font-family: Consolas">typeof</span></span><span style="color: #000000"><span style="font-family: Consolas">(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">Color</span></span><span style="color: #000000"><span style="font-family: Consolas">))</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="font-family: Consolas"><br />
row.Controls.Add(</span><span style="color: #0000ff"><span style="font-family: Consolas">this</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">.GetInputForColor(prop));</span><br />
}</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
else</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">if</span></span><span style="color: #000000"><span style="font-family: Consolas"> (prop.PropertyType.IsEnum)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="font-family: Consolas"><br />
row.Controls.Add(</span><span style="color: #0000ff"><span style="font-family: Consolas">this</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">.GetInputForEnum(prop));</span><br />
}</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
else</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="font-family: Consolas"><br />
row.Controls.Add(</span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Label</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">()</span><br />
{</span><span style="font-family: Consolas"><br />
Text = </span><span style="color: #a31515"><span style="font-family: Consolas">&#8222;Niestety, typu &#8222;</span></span><span style="color: #000000"><span style="font-family: Consolas"> + prop.PropertyType.Name + </span></span><span style="color: #a31515"><span style="font-family: Consolas">&#8221; jeszcze nie obsługujemy.&#8221;</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">,</span><br />
Left = row.Controls[0].Right + 15,</span><span style="font-family: Consolas"><br />
AutoSize = </span><span style="color: #0000ff"><span style="font-family: Consolas">true</span></span><span style="color: #000000"><span style="font-family: Consolas">,</span></span><span style="font-family: Consolas"><br />
Anchor = Anchor | </span><span style="color: #2b91af"><span style="font-family: Consolas">AnchorStyles</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">.Right</span><br />
});<br />
}<br />
}<br />
}</span></p>
<p><span style="color: #000000"><span style="font-family: Consolas"> </span></span></p>
<p>Tak przygotowany kod wystarczy „tylko” uzupełnić o metody tworzące odpowiednie kontrolki dla wprowadzania wartości i etykiety. Podam przykład dla klasy String, dla pozostałych typów odsyłam to pokładów własnej inwencji (nie jest to nic trudnego!):</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">private</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Control</span></span><span style="color: #000000"><span style="font-family: Consolas"> GetInputForString(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas"> prop)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><br />
<span style="color: #2b91af"><span style="font-family: Consolas">TextBox</span></span><span style="color: #000000"><span style="font-family: Consolas"> tb = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">TextBox</span></span><span style="color: #000000"><span style="font-family: Consolas">();</span></span><span style="color: #000000"><span style="font-family: Consolas">tb.Left = 75;<br />
tb.Anchor = tb.Anchor | <span style="color: #2b91af">AnchorStyles</span>.Right;</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
return</span></span><span style="color: #000000"><span style="font-family: Consolas"> tb;</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
}</span></span></p>
<p>i dla etykiet:</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">private</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Control</span></span><span style="color: #000000"><span style="font-family: Consolas"> GetLabelForProperty(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas"> prop)<br />
{</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
</span></span><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --><span style="color: #2b91af"><span style="font-family: Consolas">Label</span></span><span style="color: #000000"><span style="font-family: Consolas"> l = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Label</span></span><span style="color: #000000"><span style="font-family: Consolas">();</span></span><span style="font-family: Consolas"><br />
l.Text = prop.Name;</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
return</span></span><span style="color: #000000"><span style="font-family: Consolas"> l;</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
}</span></span></p>
<p style="padding-left: 30px">
<p style="padding-left: 30px"><strong>5. A co z wartościami?!</strong></p>
<p>Bardzo dobre pytanie&#8230; jak widać&#8230; nic. Ale przecież nie po to tworzymy naszą kontrolkę do wyświetlania wartości, żeby wartości nie wyświetlać&#8230;</p>
<p>Dlatego konieczna będzie mała modyfikacja powyższego kodu. Zacznijmy od podstaw – jak w ogóle pobrać wartość Property obiektu, którego typu nie znamy z początku, zresztą sami przecież nie wiemy co to za Property (więc rzutowanie, czy wpisywanie nazwy Property po kropce nie jest możliwe)? Nic trudnego, w naszym kodzie mamy już obiekt opisujący Property, potrzebujemy jeszcze obiekt typu, którego dane Property dotyczy – a potem już tylko wywołanie metody GetValue:</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm"><span style="font-family: Consolas">prop.GetValue(ob, </span><span style="color: #0000ff"><span style="font-family: Consolas">null</span></span><span style="color: #000000"><span style="font-family: Consolas">);</span></span></p>
<p>W naszym kodzie więc potrzebujemy obiektu, którego wartość chcemy pobierać, modyfikacje będą więc następujące:</p>
<p>Metoda tworząca kontrolkę do edycji/wyświetlania wartości:</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">private</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Control</span></span><span style="color: #000000"><span style="font-family: Consolas"> GetInputForString(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas"> prop, </span></span><span style="color: #0000ff"><span style="font-family: Consolas">object</span></span><span style="color: #000000"><span style="font-family: Consolas"> ob)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="color: #2b91af"><span style="font-family: Consolas"><br />
TextBox</span></span><span style="color: #000000"><span style="font-family: Consolas"> tb = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">TextBox</span></span><span style="color: #000000"><span style="font-family: Consolas">();</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
tb.Text = (</span></span><span style="color: #2b91af"><span style="font-family: Consolas">String</span></span><span style="color: #000000"><span style="font-family: Consolas">)prop.GetValue(ob, </span></span><span style="color: #0000ff"><span style="font-family: Consolas">null</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">);</span>tb.Left = 75;<br />
tb.Anchor = tb.Anchor | <span style="color: #2b91af">AnchorStyles</span>.Right;</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
return</span></span><span style="color: #000000"><span style="font-family: Consolas"> tb;</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
}</span></span></p>
<p>I co za tym idzie uzupełnienie wywołań w głównej pętli wyświetlającej nasze wartości:</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm"><span style="color: #000000"><span style="font-family: Consolas">row.Controls.Add(</span></span><span style="color: #0000ff"><span style="font-family: Consolas">this</span></span><span style="color: #000000"><span style="font-family: Consolas">.GetInputForString(prop, ob));</span></span></p>
<p><span style="color: #000000"><span style="font-family: Consolas"> </span></span></p>
<p>i podobnie dla wszystkich pozostałych metod dotyczących Property. <strong> </strong></p>
<p style="padding-left: 30px"><strong>6. Ale opis jest brzydki&#8230;</strong></p>
<p>Fakt, opisy w tym momencie stanowią nazwy pól w klasie – brak spacji, często niewłaściwy język i skrótowe nazwy niewiele mówią potencjalnemu użytkownikowi. Tutaj z pomocą znowu przyjść nam mogą atrybuty.</p>
<p>Zacznijmy więc od zadeklarowania odpowiedniego (powinien zawierać przeładowany konstruktor oraz musi zawierać Property dla wyświetlanej wartości):</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">public</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">class</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">VisualDisplayAttribute</span></span><span style="color: #000000"><span style="font-family: Consolas"> : </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Attribute</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
public</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">string</span></span><span style="color: #000000"><span style="font-family: Consolas"> VisualDisplay { </span></span><span style="color: #0000ff"><span style="font-family: Consolas">get</span></span><span style="color: #000000"><span style="font-family: Consolas">; </span></span><span style="color: #0000ff"><span style="font-family: Consolas">set</span></span><span style="color: #000000"><span style="font-family: Consolas">; }</span></span><span style="color: #0000ff"><span style="font-family: Consolas"> </span></span></p>
<p style="padding-left: 30px">public<span style="color: #000000"><span style="font-family: Consolas"> VisualDisplayAttribute(</span></span><span style="color: #0000ff"><span style="font-family: Consolas">string</span></span><span style="color: #000000"><span style="font-family: Consolas"> VisualDisplay)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
: </span></span><span style="color: #0000ff"><span style="font-family: Consolas">base</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">()</span><br />
{</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
this</span></span><span style="color: #000000"><span style="font-family: Consolas">.VisualDisplay = VisualDisplay;</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
}<br />
}</span></span></p>
<p>Następnie niewygodne Property „ozdabiamy” takim atrybutem:</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="font-family: Consolas">[</span><span style="color: #2b91af"><span style="font-family: Consolas">VisualDisplay</span></span><span style="color: #000000"><span style="font-family: Consolas">(</span></span><span style="color: #a31515"><span style="font-family: Consolas">"Tło"</span></span><span style="color: #0000ff"><span style="font-family: Consolas">)]<br />
public</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Color</span></span><span style="color: #000000"><span style="font-family: Consolas"> Background { </span></span><span style="color: #0000ff"><span style="font-family: Consolas">get</span></span><span style="color: #000000"><span style="font-family: Consolas">; </span></span><span style="color: #0000ff"><span style="font-family: Consolas">set</span></span><span style="color: #000000"><span style="font-family: Consolas">; }</span></span></p>
<p>I modyfikujemy tworzenie etykiety:</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">private</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Control</span></span><span style="color: #000000"><span style="font-family: Consolas"> GetLabelForProperty(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas"> prop)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="color: #2b91af"><span style="font-family: Consolas"><br />
Label</span></span><span style="color: #000000"><span style="font-family: Consolas"> l = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Label</span></span><span style="color: #000000"><span style="font-family: Consolas">();</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
var</span></span><span style="color: #000000"><span style="font-family: Consolas"> attributes = prop.GetCustomAttributes(</span></span><span style="color: #0000ff"><span style="font-family: Consolas">typeof</span></span><span style="color: #000000"><span style="font-family: Consolas">(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">VisualDisplayAttribute</span></span><span style="color: #000000"><span style="font-family: Consolas">), </span></span><span style="color: #0000ff"><span style="font-family: Consolas">true</span></span><span style="color: #000000"><span style="font-family: Consolas">);</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
if</span></span><span style="color: #000000"><span style="font-family: Consolas"> (attributes.Length &gt; 0)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="font-family: Consolas"><br />
l.Text = ((</span><span style="color: #2b91af"><span style="font-family: Consolas">VisualDisplayAttribute</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">)attributes[0]).VisualDisplay;</span><br />
}</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
else</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{<br />
</span>l.Text = prop.Name;<br />
}</span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
return</span></span><span style="color: #000000"><span style="font-family: Consolas"> l;</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
}</span></span> <strong><br />
</strong></p>
<p style="margin-bottom: 0cm;padding-left: 30px">
<p style="margin-bottom: 0cm;padding-left: 30px"><strong>7. Czy można zrobić pola tylko do 	odczytu? Albo obowiązkowe?</strong></p>
<p>Jak najbardziej – proponowane rozwiązanie to odpowiedni zestaw atrybutów. Np. nowy atrybut ReadOnlyValueAttribute, którego posiadanie przez Property badamy w identyczny sposób jak dla wcześniejszych przykładów i w przypadku znalezienia ustawiamy np. wartość enabled tworzonej kontrolki na false.</p>
<p>Dzięki odpowiedniemu zestawowi atrybutów możemy praktycznie dowolnie sterować całą naszą kontrolką – od możliwości edycji danej wartości, po kolor tła kontrolki, czy jej kontenera – granicą jest chyba tylko nasza wyobraźnia. Faktem jest, że pewne rzeczy dużo łatwiej było by zaprojektować w &#8222;tradycyjny&#8221; sposób, ustawiając kontrolki w designerze, niż odczytywać wartości z atrybutów pól klasy, po czym przeliczać je na wartości, które &#8222;ręcznie&#8221; trzeba ustawić w kontrolkach, granicę opłacalności wyznaczyć musimy sobie sami. <strong> </strong></p>
<p style="padding-left: 30px"><strong>8. Zaraz, zaraz&#8230; miała być edycja!</strong></p>
<p>I nikt o niej nie zapomniał. Jak łatwo zauważyć – wartości co prawda odczytujemy&#8230; ale ich już nie zmieniamy&#8230; metod jest kilka, ale wszystkie wymagają jednego – zapamiętania lub pozyskania obiektu PropertyInfo oraz obiektu, który wyświetlamy / edytujemy. Kiedy już owe obiekty mamy, wystarczy nam&#8230; jak zwykle jedna linijka&#8230; a dokładniej wywołanie metody SetValue klasy PropertyInfo</p>
<p>Zmodyfikujmy raz jeszcze nasz kod – do pól Tag paneli przypiszemy odpowiednie informacje (dla obiektu panel naturalny będzie obiekt na panelu wyświetlany, dla panelu wiersza – PropertyInfo, którego wiersz dotyczy):</p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">private</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas">void</span></span><span style="color: #000000"><span style="font-family: Consolas"> CreateControlsForProps(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">IEnumerable</span></span><span style="color: #000000"><span style="font-family: Consolas">&lt;</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas">&gt; properties, </span></span><span style="color: #0000ff"><span style="font-family: Consolas">object</span></span><span style="color: #000000"><span style="font-family: Consolas"> ob)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
this</span></span><span style="color: #000000"><span style="font-family: Consolas">.panel.Tag = ob;</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
foreach</span></span><span style="color: #000000"><span style="font-family: Consolas"> (</span></span><span style="color: #0000ff"><span style="font-family: Consolas">var</span></span><span style="color: #000000"><span style="font-family: Consolas"> prop </span></span><span style="color: #0000ff"><span style="font-family: Consolas">in</span></span><span style="color: #000000"><span style="font-family: Consolas"> properties)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="color: #2b91af"><span style="font-family: Consolas"><br />
Panel</span></span><span style="color: #000000"><span style="font-family: Consolas"> row = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Panel</span></span><span style="color: #000000"><span style="font-family: Consolas">();</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
row.Tag = prop;<br />
&#8230;</span></span></p>
<p>Dodajmy obsługę zdarzenia zmiany wartości kontrolki edytującej Property, na przykład:</p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">private</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">Control</span></span><span style="color: #000000"><span style="font-family: Consolas"> GetInputForString(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas"> prop, </span></span><span style="color: #0000ff"><span style="font-family: Consolas">object</span></span><span style="color: #000000"><span style="font-family: Consolas"> ob)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="color: #2b91af"><span style="font-family: Consolas"><br />
TextBox</span></span><span style="color: #000000"><span style="font-family: Consolas"> tb = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">TextBox</span></span><span style="color: #000000"><span style="font-family: Consolas">();</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
tb.Text = (</span></span><span style="color: #2b91af"><span style="font-family: Consolas">String</span></span><span style="color: #000000"><span style="font-family: Consolas">)prop.GetValue(ob, </span></span><span style="color: #0000ff"><span style="font-family: Consolas">null</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">);</span><br />
tb.Left = 75;</span><span style="font-family: Consolas"><br />
tb.TextChanged += </span><span style="color: #0000ff"><span style="font-family: Consolas">new</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">EventHandler</span></span><span style="color: #000000"><span style="font-family: Consolas">(tb_TextChanged);</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
return</span></span><span style="color: #000000"><span style="font-family: Consolas"> tb;</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
}</span></span></p>
<p style="margin-bottom: 0cm;padding-left: 30px"><span style="color: #0000ff"><span style="font-family: Consolas">void</span></span><span style="color: #000000"><span style="font-family: Consolas"> tb_TextChanged(</span></span><span style="color: #0000ff"><span style="font-family: Consolas">object</span></span><span style="color: #000000"><span style="font-family: Consolas"> sender, </span></span><span style="color: #2b91af"><span style="font-family: Consolas">EventArgs</span></span><span style="color: #000000"><span style="font-family: Consolas"> e)</span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
{</span></span><span style="color: #2b91af"><span style="font-family: Consolas"><br />
TextBox</span></span><span style="color: #000000"><span style="font-family: Consolas"> tb = sender </span></span><span style="color: #0000ff"><span style="font-family: Consolas">as</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">TextBox</span></span><span style="color: #000000"><span style="font-family: Consolas">;</span></span><span style="color: #2b91af"><span style="font-family: Consolas"><br />
PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas"> prop = tb.Parent.Tag </span></span><span style="color: #0000ff"><span style="font-family: Consolas">as</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas">;</span></span><span style="color: #0000ff"><span style="font-family: Consolas"><br />
object</span></span><span style="color: #000000"><span style="font-family: Consolas"> ob = tb.Parent.Parent.Tag;</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span></p>
<p style="padding-left: 30px">prop.SetValue(ob, tb.Text, <span style="color: #0000ff"><span style="font-family: Consolas">null</span></span><span style="font-family: Consolas"><span style="font-family: Consolas">);</span><br />
}</span></p>
<p>Gotowe!</p>
<p><em>Zdaję sobie sprawę, że posługiwanie się polem Tag bywa mętne i trochę utrudnia analizę kodu następcom, ale zostało użyte raczej jako przykład niż wzór – osobiście proponowałbym np. podziedziczyć po panelu i stworzyć klasę panelu wiersza z odpowiednim, dedykowanym Property dla PropertyIfno itp. Takie rozwiązanie będzie dużo bardziej czytelne no i nikt nam wtedy wartości nie nadpisze, gdy stwierdzi, że fajnie by było dla niego przechować coś w Tagu</em> <strong> </strong></p>
<p style="padding-left: 30px"><strong>9.Przecież refleksja jest wolna!</strong></p>
<p>Tak, refleksja działa wolniej niż pobierania i ustawianie wartości &#8222;normalnymi&#8221; odwołaniami do właściwości obiektów, jednak stworzenie i wyświetlenie jednej kontrolki zajmuje nieporównanie więcej czasu&#8230; więc nie stanowi to w tym przypadku żadnego argumentu, ani nie powinno nas to napełniać obawami. <strong> </strong></p>
<p style="padding-left: 30px"><strong>10. Plus i minus, to jedyne co słyszę 	– czyli kiedy stosować?</strong></p>
<p>Zdecydowanym plusem takiego rozwiązania jest jego elastyczność, można wyświetlić w ten sposób wszystko. Dosłownie. Wystarczy spojrzeć na Visualowe komponenty do edycji obiektów – PropertyGrid&#8217;y, które mają bardzo szerokie możliwości, a działają na dokładnie takiej samej zasadzie. Będzie to rozwiązanie szczególnie cenne, kiedy klasy wyświetlane zmieniają się co chwila, albo interfejs powstaje w celu edycji danych, których jeszcze nie znamy.</p>
<p>Kolejnym plusem jest modna „reusability”, jeżeli stworzymy dobrze taką kontrolkę oraz zestaw atrybutów użycie ich w kolejnym projekcie będzie dziecinnie proste – w końcu nie mamy żadnych odwołań do klas encji biznesowych, czy jakichkolwiek innych klas, które chcemy wyświetlać.</p>
<p>Oczywiście są i minusy&#8230;<br />
M.in. dużo cięższe debugowanie wymuszone przez użytą refleksję.</p>
<p>Brak możliwości podejrzenia jak wygląda kontrolka w trybie design, jedyna możliwość to uruchomienie aplikacji na przykładowych danych.</p>
<p>Monotonność interfejsu i dużo trudniejsze modyfikacje wyglądu kontrolek pod konkretne Propery. Bez napracowania się nad atrybutami i ich interpretacją każde użycie kontrolki do wyświetlania danych wyglądać będzie identycznie.</p>
<p>Można zadać sobie pytanie po co tak się męczyć? przecież są wspomniane już PropertyGrid&#8217;y, są dobre do edycji danych Gridy. Ciężko temu argumentowi zaprzeczyć, to prawda. Tym niemniej tych gridów nie zmodyfikujemy, nie powiemy im co mają wyświetlać, a co nie w tak łatwy sposób itp. To rozwiązanie da nam pełną dowolność, której być może będziemy potrzebować.</p>
<div style="width: 1px;height: 1px;overflow: hidden">
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm"><span style="font-size: small"><span style="color: #0000ff"><span style="font-family: Consolas">public</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">IEnumerable</span></span><span style="color: #000000"><span style="font-family: Consolas">&lt;</span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas">&gt; GetPropertiesToDisplay(</span></span><span style="color: #0000ff"><span style="font-family: Consolas">object</span></span><span style="color: #000000"><span style="font-family: Consolas"> ob)</span></span></span></p>
<p style="margin-bottom: 0cm"><span style="color: #000000"><span style="font-family: Consolas"><span style="font-size: small">{</span></span></span></p>
<p style="margin-bottom: 0cm"><span style="color: #000000"> </span><span style="font-size: small"><span style="color: #0000ff"><span style="font-family: Consolas">var</span></span><span style="color: #000000"><span style="font-family: Consolas"> type = ob.GetType();</span></span></span></p>
<p style="margin-bottom: 0cm"><span style="color: #000000"> </span><span style="font-size: small"><span style="color: #0000ff"><span style="font-family: Consolas">var</span></span><span style="color: #000000"><span style="font-family: Consolas"> properties = type.GetProperties(</span></span><span style="color: #2b91af"><span style="font-family: Consolas">BindingFlags</span></span><span style="color: #000000"><span style="font-family: Consolas">.Public | </span></span><span style="color: #2b91af"><span style="font-family: Consolas">BindingFlags</span></span><span style="color: #000000"><span style="font-family: Consolas">.Instance);</span></span></span></p>
<p style="margin-bottom: 0cm"><span style="color: #000000"> </span><span style="font-size: small"><span style="color: #0000ff"><span style="font-family: Consolas">var</span></span><span style="color: #000000"><span style="font-family: Consolas"> returnValue = </span></span><span style="color: #0000ff"><span style="font-family: Consolas">from</span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas">PropertyInfo</span></span><span style="color: #000000"><span style="font-family: Consolas"> p </span></span><span style="color: #0000ff"><span style="font-family: Consolas">in</span></span><span style="color: #000000"><span style="font-family: Consolas"> properties </span></span><span style="color: #0000ff"><span style="font-family: Consolas">where</span></span><span style="color: #000000"><span style="font-family: Consolas"> p.GetCustomAttributes(</span></span><span style="color: #0000ff"><span style="font-family: Consolas"> </span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #2b91af"><span style="font-family: Consolas"> </span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas"> </span></span><span style="color: #000000"><span style="font-family: Consolas"> </span></span><span style="color: #0000ff"><span style="font-family: Consolas"> </span></span><span style="color: #000000"><span style="font-family: Consolas"><br />
</span></span></span></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/dynamiczne-tworzenie-interfejsu-uzytkownika-przy-pomocy-refleksji/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Pobranie parametrów metody w C# wraz z wartościami</title>
		<link>http://xlab.pl/pobranie-parametrow-metody-w-c-wraz-z-wartosciami/</link>
		<comments>http://xlab.pl/pobranie-parametrow-metody-w-c-wraz-z-wartosciami/#comments</comments>
		<pubDate>Mon, 12 Oct 2009 15:44:32 +0000</pubDate>
		<dc:creator>Mateusz Kubiczek</dc:creator>
				<category><![CDATA[Programowanie .NET]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[logging]]></category>
		<category><![CDATA[reflection]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=558</guid>
		<description><![CDATA[Dosyć długo szukałem w jaki sposób pobrać nazwy parametrów metody wraz z ich wartościami w sposób uniwersalny. Ułatwiłoby to logowanie wszystkiego, co zachodzi w aplikacji. Wywołując metodę: public void CountPrice(int bar, string baz) { } chciałbym otrzymać w logach wpis: Method&#8217;s Foo arguments >> bar: 1, baz: zzz Najpierw spróbowałem za pomocą metody GetParameters klasy [...]]]></description>
			<content:encoded><![CDATA[<p>Dosyć długo szukałem w jaki sposób pobrać <em>nazwy</em> parametrów metody wraz z ich <em>wartościami</em> w sposób uniwersalny.</p>
<p>Ułatwiłoby to logowanie wszystkiego, co zachodzi w aplikacji. Wywołując metodę:</p>
<pre name="code" class="c-sharp">
public void CountPrice(int bar, string baz)
{

}</pre>
<p>chciałbym otrzymać w logach wpis:</p>
<blockquote><p>Method&#8217;s Foo arguments >> bar: 1, baz: zzz</p></blockquote>
<p><span id="more-558"></span></p>
<p>Najpierw spróbowałem za pomocą metody <a href="http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getparameters.aspx"><strong>GetParameters</strong> klasy <strong>MethodInfo</strong></a>. Zwrócone obiekty typu <strong>ParameterInfo</strong> zawierają jednak tylko informacje o typach parametrów &#8211; nie mają wartości tych parametrów w czasie wykonywania kodu.</p>
<p>Moim celem było stworzenie metody, którą mógłbym użyć do pobrania sformatowanej informacji o parametrach metody &#8211; na przykład w celu zalogowania ich. Załóżmy dla prostoty testowania, że nasza metoda <strong>Foo</strong> będzie zwracała sformatowany napis z jej parametrami i ich wartościami.</p>
<p>Pierwsza wersja:</p>
<pre name="code" class="c-sharp">
public class TestClass
{
  public static string Foo(int bar, string baz)
  {
    return LogingHelpers.GetMethodArguments();
  }
}

public class LoggingHelpers
{
  public static string GetMethodArguments()
  {
    //TODO tutaj magia :-)
    return "";
  }
}</pre>
<p>Niestety &#8211; z taką składnią nie udało mi się. Z kontekstu <strong>LoggingHelpers.GetMethodArguments</strong> nie da się (lub nadal nie potrafię :-) ) dostać się do parametrów metody wywołującej. Działająca składnia jest nieco inna i odrobinę mniej wygodna:</p>
<pre name="code" class="c-sharp">
public class TestClass
{
  public static string Foo(int bar, string baz)
  {
    return GetMethodArguments(() => bar, () => baz);
  }

  public static string NoParameters()
  {
    return GetMethodArguments();
  }
}

public class LoggingHelpers
{
  public static string GetMethodArguments(params Func&lt;object&gt;[] expr)
  {
    //TODO tutaj magia, ujawnione już za chwilę!
    return "";
  }
}</pre>
<p>Ale najpierw test z wykorzystaniem NUnit.</p>
<pre name="code" class="c-sharp">
    using NUnit.Framework;
    using NUnit.Framework.SyntaxHelpers;

    [TestFixture]
    public class LoggingTests
    {

        [Test]
        public void TwoParametersCorrectlyFormatted
        {
            string parameterNamesAndValues = TestClass.Foo(1, "zzz");
            Assert.That(parameterNamesAndValues, Is.EqualTo("Method's Foo arguments >> bar: 1, baz: zzz");
        }

        [Test]
        public void NoParametersCorrectlyFormatted
        {
            string noParameters = TestClass.NoParameters();
            Assert.That(noParameters, Is.EqualTo("Method's Foo arguments >> No arguments.");
        }
    }</pre>
<p>Odpalamy testy, który oczywiście nie przechodzą. </p>
<p>Żeby testy zadziałały, nasza klasa <strong>LoggingHelpers</strong> wraz z metodą <strong>GetMethodParameters</strong> może wyglądać tak:</p>
<pre name="code"  class="c-sharp">
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

public class LoggingHelpers
{
    public static string GetMethodParameters(params Func&lt;object&gt;[] expr)
    {
      // Pobieramy nazwę metody.
      string methodname = new StackFrame(1).GetMethod().Name;

      // wstępne formatowanie wynikowego napisu:
      string logmessage = "Method's " + methodname + " arguments >> ";

      // Tablica wyrażeń lambda jest pusta
      if (expr.Length == 0)
      {
        logmessage += "No arguments.";
      }
      else
      {
        try
        {
          // dla każdego Expression
          foreach (var par in expr)
          {
            // Zaawansowane sztuczki. Za pomocą MSIL znajdujemy informacje o Expression
            byte[] il = par.Method.GetMethodBody().GetILAsByteArray();

            // bajty 2-6 oznaczają uchwyt do naszych parametrów
            int fieldHandle = BitConverter.ToInt32(il, 2);

            // pobieramy FieldInfo tego parametru
            FieldInfo field = par.Target.GetType().Module.ResolveField(fieldHandle);

            // Formatujemy ostatecznie napis dla tego parametru.
            // Nazwę parametru uzyskujemy z FieldInfo, natomiast wartość wykonując przekazane Expression.
            logmessage += string.Format("{0}: {1}. ", field.Name, par() ?? "@null");
          }
        }
        catch
        {
        // Ignorujemy błędy - nic nie jest gorsze od doskonale działającej logiki aplikacji, która wywala się na narzędziach do logowania :-)
        // Tego typu try-catch to zła praktyka w aplikacji! W tym miejscu jest to stosowne, ale przestrzegam przed nadużywaniem "zjadania" wyjątków.

          logmessage += " FAILED reading arguments.";
        }
      }
    }
}</pre>
<p>Klasa do wykorzystania w aplikacji.</p>
<p>W kolejnej notce pokażę, jak to spiąć z log4net.</p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/pobranie-parametrow-metody-w-c-wraz-z-wartosciami/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

