Sviluppo .NET http://blogs.ugidotnet.org/idamiani/category/Sviluppo .NET.aspx Tip di programmazione, add-on di terze parti, esempi pratici tratti dai casi reali su cui lavoro, data-binding con .NET 2.0, script MSBuild, WinFX it-IT Igor Damiani Subtext Version 2.6.0.0 Un nuovo plugin per WLW: pubblicazione di immagini http://blogs.ugidotnet.org/idamiani/archive/2007/04/17/75692.aspx <p>Mi sono dato alla creazione di un altro plugin per Windows Live Writer. Mi capita spesso di creare post il cui scopo è avere solo link a n immagini che vado preventivamente ad uploadare sul mio sito <a href="http://www.igordamiani.it">www.igordamiani.it</a>. Per esempio, date un'occhiata al post relativo <a href="http://blogs.ugidotnet.org/idamiani/archive/2007/04/15/75506.aspx" target="_blank">all'ultimo workshop</a>.</p> <p><strong><font color="#0000ff">Quali sono le caratteristiche che saltano all'occhio?<br></font></strong>E' presto detto...</p> <ol> <li>C'è un breve commento all'inizio</li> <li>C'è un'immagine o una fotografia visibile direttamente nel post</li> <li>Ci sono una serie di <em>n</em> link, che puntano direttamente a <em>n</em> immagini JPG uploadate sul sito</li></ol> <p>Come dicevo, mi capita spesso di scrivere post di questo tipo e anche se non sembra, è piuttosto noioso farli, perchè sono sempre operazioni ripetitive.&nbsp;Scegli le immagini giuste, ridimensionale, uploadale sul sito, scrivi il post inserendo a mano una descrizione ed il link corretto e via dicendo. Ho deciso quindi di scrivere un plugin per WLW per automizzare il più possibile questa cosa, così vi posso bombardare più spesso di fotografie. :-) Perchè non farlo in WPF per renderlo più accattivante? Arrivo al dunque, altrimenti qua non finisco più.</p> <p><strong>La caratteristica fondamentale: la scelta delle fotografie</strong><br><strong><font color="#0000ff">Il plugin</font></strong>, già in fase di sviluppo, <strong><font color="#0000ff">permette all'utente di puntare una directory sul proprio hard-disk e poter scegliere le immagini&nbsp;da pubblicare oppure no</font></strong>. Do per scontato che le immagini nella directory siano già ridimensionate al punto giusto, perchè non faccio fare al mio plugin cose che altri software fanno già bene (come per esempio IrfanView). Dopo aver indicato la directory, si clicca su un pulsante che fa quanto segue:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:5a1d7fb7-f31b-414c-ad2c-b4b5fc4cae4b" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:#DADAA5;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">void</span><span style="color: #000000; "> btnRefreshLocalPath(</span><span style="color: #0000FF; ">object</span><span style="color: #000000; "> sender, RoutedEventArgs args) { DirectoryInfo di </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> DirectoryInfo(txtLocalPath.Text); FileInfo[] files </span><span style="color: #000000; ">=</span><span style="color: #000000; "> di.GetFiles(</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">*.jpg</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">); lstImages.Items.Clear(); </span><span style="color: #0000FF; ">foreach</span><span style="color: #000000; "> (FileInfo f </span><span style="color: #0000FF; ">in</span><span style="color: #000000; "> files) { ImageElement ie </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> ImageElement(f.FullName); lstImages.Items.Add(ie); } }</span></div></pre></div></p> <p>Il path viene recuperato dalla TextBox, viene utilizzato il metodo GetFiles() per ottenere la lista di tutti i files JPG (<em>to be changed?</em>)&nbsp;nella directory. In seguito, la ListBox viene svuotata e viene creato un oggetto <strong>ImageElement</strong> per ogni file esistente. La classe <strong>ImageElement</strong>&nbsp;incapsula tutta la logica che serve per&nbsp;gestire le immagini.&nbsp;Ogni&nbsp;istanza di <strong>ImageElement</strong>&nbsp;infatti permette di:</p> <ol> <li>selezionare/deselezionare l'immagine (usando una CheckBox)</li> <li>vedere&nbsp;l'immagine e decidere se la si vuole o no (usando un'Image)</li> <li>inserire una breve descrizione relativa all'immagine (usando una TextBox)</li></ol> <p>La classe <strong>ImageElement</strong> è uno UserControl scritto per WPF, quindi un po' XAML ed un po' C#. La parte XAML è forse la più interessante:</p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:c976fa2e-7831-470b-8628-cf9021eb2aa5" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">UserControl </span><span style="color: #FF0000; ">x:Class</span><span style="color: #0000FF; ">=&quot;VivendoByte.PostSomeImages.ImageElement&quot;</span><span style="color: #FF0000; "> xmlns</span><span style="color: #0000FF; ">=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;</span><span style="color: #FF0000; "> xmlns:x</span><span style="color: #0000FF; ">=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">UserControl</span><span style="color: #FF0000; ">.Resources</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Style </span><span style="color: #FF0000; ">x:Key</span><span style="color: #0000FF; ">=&quot;normal&quot;</span><span style="color: #FF0000; "> TargetType</span><span style="color: #0000FF; ">=&quot;{x:Type TextBox}&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="background-color: #F5F5F5; color: #800000; "> &lt;Style.Triggers&gt; &lt;DataTrigger Binding=&quot;</span><span style="background-color: #F5F5F5; color: #000000; ">{</span><span style="background-color: #F5F5F5; color: #FF0000; ">Binding ElementName=chkSelected, Path=IsChecked</span><span style="background-color: #F5F5F5; color: #000000; ">}</span><span style="background-color: #F5F5F5; color: #800000; ">&quot; Value=&quot;True&quot;&gt; &lt;Setter Property=&quot;IsEnabled&quot; Value=&quot;True&quot; /&gt; &lt;/DataTrigger&gt; &lt;DataTrigger Binding=&quot;</span><span style="background-color: #F5F5F5; color: #000000; ">{</span><span style="background-color: #F5F5F5; color: #FF0000; ">Binding ElementName=chkSelected, Path=IsChecked</span><span style="background-color: #F5F5F5; color: #000000; ">}</span><span style="background-color: #F5F5F5; color: #800000; ">&quot; Value=&quot;False&quot;&gt; &lt;Setter Property=&quot;IsEnabled&quot; Value=&quot;False&quot; /&gt; &lt;/DataTrigger&gt; </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Style.Triggers</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Style</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">UserControl.Resources</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">StackPanel </span><span style="color: #FF0000; ">Orientation</span><span style="color: #0000FF; ">=&quot;Vertical&quot;</span><span style="color: #FF0000; "> Name</span><span style="color: #0000FF; ">=&quot;panel&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">CheckBox </span><span style="color: #FF0000; ">Content</span><span style="color: #0000FF; ">=&quot;Select this image&quot;</span><span style="color: #FF0000; "> IsChecked</span><span style="color: #0000FF; ">=&quot;False&quot;</span><span style="color: #FF0000; "> Margin</span><span style="color: #0000FF; ">=&quot;0 6 6 1&quot;</span><span style="color: #FF0000; "> Name</span><span style="color: #0000FF; ">=&quot;chkSelected&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Image </span><span style="color: #FF0000; ">Name</span><span style="color: #0000FF; ">=&quot;imgImage&quot;</span><span style="color: #FF0000; "> Stretch</span><span style="color: #0000FF; ">=&quot;UniformToFill&quot;</span><span style="color: #FF0000; "> Width</span><span style="color: #0000FF; ">=&quot;180&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Image</span><span style="color: #FF0000; ">.Source</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">BitmapImage </span><span style="color: #FF0000; ">UriSource</span><span style="color: #0000FF; ">=&quot;{StaticResource image}&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Image.Source</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Image</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">TextBox </span><span style="color: #FF0000; ">Name</span><span style="color: #0000FF; ">=&quot;txtDescription&quot;</span><span style="color: #FF0000; "> Margin</span><span style="color: #0000FF; ">=&quot;0 3 6 1&quot;</span><span style="color: #FF0000; "> Style</span><span style="color: #0000FF; ">=&quot;{StaticResource normal}&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Label </span><span style="color: #FF0000; ">Height</span><span style="color: #0000FF; ">=&quot;3&quot;</span><span style="color: #FF0000; "> Background</span><span style="color: #0000FF; ">=&quot;SkyBlue&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">StackPanel</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">UserControl</span><span style="color: #0000FF; ">&gt;</span></div></pre></div> <p>Il visual-tree di ogni&nbsp;istanza di <strong>ImageElement</strong> contiene uno StackPanel verticale, che contiene a sua volta una CheckBox, una Image, una TextBox ed infine una Label (che funge semplicemente da separatore - <em>to be changed</em>). <strong><font color="#0000ff">Ho inserito due triggers per rendere più divertente la cosa</font></strong>: quando una certa immagine non è selezionata (proprietà IsChecked della CheckBox = False), allora non ha senso che l'utente ne inserisca la descrizione, quindi setto la proprietà IsEnabled della TextBox a False. Teoricamente, avrei potuto inserire un solo trigger, con qualche giro di binding, ma sono andato in fumo e...proprio non ce la faccio: va bene così! :-)</p> <p>Un piccolo screenshot è come al solito doveroso.</p> <p><img src="http://www.igordamiani.it/blog/PostSomeImages.png"> </p> <p>Adesso è piuttosto spartano, ma l'idea è questa. Le foto selezionate vengono uploadate sul sito indicato, usando username e password specificate, poi ne viene presa la descrizione, viene fatto il link ed il tutto viene restituito a WLW. Appena è pronto lo pubblico, credo che possa interessare un po' di gente. Stay tuned.</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/75692.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/04/17/75692.aspx Tue, 17 Apr 2007 23:16:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/04/17/75692.aspx#feedback 2 http://blogs.ugidotnet.org/idamiani/comments/commentRss/75692.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/75692.aspx Ok, vada per l'hourglass, ma ci sono altri cursori? http://blogs.ugidotnet.org/idamiani/archive/2007/04/11/75213.aspx <p><a href="http://blogs.ugidotnet.org/idamiani/archive/2007/04/11/75189.aspx" target="_blank">Nel pot di prima</a>, abbiamo visto come mostrare l'icona della clessidra sul display del palmare, nel caso in cui dobbiamo comunicare all'utente che l'applicazione sta facendo qualcosa che dura un po' di tempo. Nel codice abbiamo utilizzato una costante intera valorizzata a 32514, 0x7F02. Posso farlo?<br>Quali altre icone abbiamo a disposizione?<br>Che valori devo utilizzare per poterle visualizzare sullo schermo?</p> <p>La soluzione più rapida consiste nell'installarsi in locale sul proprio PC il <strong>Microsoft Platform Builder 5.0</strong>, per gli amici il <strong>Windows Mobile 5.0 Pocket PC SDK</strong>, che contiene tutte le informazioni necessarie allo sviluppo su mobile. Tale documentazione contiene anche info sulla funzione LoadCursor, di cui abbiamo già parlato. La pagina relativa a questa funzione ci svela alcuni dettagli interessanti, come i nomi delle costanti IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND e così via, fino a IDC_WAIT, che guarda caso è la clessidra. Non ci vengono comunicati i valori di queste costanti, ma la soluzione è dietro l'angolo.</p> <p>Il file header di queste funzioni è <em>Winuser.h</em>, è sufficiente cercarlo sul proprio hard-disk: lo troverete solo se avete installato l'SDK di cui sopra. A me è finito in queste directory:</p> <p><em><font color="#008040">C:\Programmi\Microsoft SDKs\Windows\v6.0\Include<br>C:\Programmi\Windows CE Tools\wce500\Windows Mobile 5.0 Pocket PC SDK\Include\Armv4i</font></em></p> <p>Indipendentemente dal file che guardate, i valori delle costanti è ovviamente sempre lo stesso, e cioè:</p> <p></p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:ea2a3dc3-1a6c-43ec-8ad1-778149fbd3a9" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #008000; ">/*</span><span style="color: #008000; "> * Standard Cursor IDs </span><span style="color: #008000; ">*/</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_ARROW MAKEINTRESOURCE(32512)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_IBEAM MAKEINTRESOURCE(32513)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_WAIT MAKEINTRESOURCE(32514)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_CROSS MAKEINTRESOURCE(32515)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_UPARROW MAKEINTRESOURCE(32516)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_SIZE MAKEINTRESOURCE(32640) /* OBSOLETE: use IDC_SIZEALL */</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_ICON MAKEINTRESOURCE(32641) /* OBSOLETE: use IDC_ARROW */</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_SIZENWSE MAKEINTRESOURCE(32642)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_SIZENESW MAKEINTRESOURCE(32643)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_SIZEWE MAKEINTRESOURCE(32644)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_SIZENS MAKEINTRESOURCE(32645)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_SIZEALL MAKEINTRESOURCE(32646)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_NO MAKEINTRESOURCE(32648) /*not in win3.1 */</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#if</span><span style="color: #000000; ">(WINVER &gt;= 0x0500)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_HAND MAKEINTRESOURCE(32649)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#endif</span><span style="color: #000000; "> /* WINVER &gt;= 0x0500 */</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_APPSTARTING MAKEINTRESOURCE(32650) /*not in win3.1 */</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#if</span><span style="color: #000000; ">(WINVER &gt;= 0x0400)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#define</span><span style="color: #000000; "> IDC_HELP MAKEINTRESOURCE(32651)</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">#endif</span><span style="color: #000000; "> /* WINVER &gt;= 0x0400 */</span></div></pre></div> <p></p> <p>Se invece della clessidra volete mettere un'altra icona, è sufficiente cambiare il cursorID. Consiglio spassionato? Definire un enum che contiene tutti i valori per avere codice più leggibile, più chiaro e&nbsp;più semplice da tramandare ai posteri (ogni riferimento a Pier ex-collega è puramente casuale).</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/75213.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/04/11/75213.aspx Wed, 11 Apr 2007 17:12:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/04/11/75213.aspx#feedback 2 http://blogs.ugidotnet.org/idamiani/comments/commentRss/75213.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/75213.aspx Mostrare un Hourglass sul palmare con il Compact Framework http://blogs.ugidotnet.org/idamiani/archive/2007/04/11/75189.aspx <p>Se il software che state sviluppando per un dispositivo mobile compie un'operazione lunga, potete mostrare sullo schermo una clessidra - l'Hourglass dell'oggetto di questo post. Con Windows Mobile 5.0, questa clessidra in realtà è un piccolo cerchio centrato sullo schermo, diviso in 4 settori colorati (rosso/giallo/blu/verde). Ovviamente tale clessidra non è modale, perciò il vostro codice prosegue l'esecuzione fino a quando non siete voi a decidere che la clessidra non è più necessaria. Un'altra piccola precisazione: su palmari non avete il concetto di <em>puntatore del mouse</em>, perciò non potete fare quello che è possibile con il .NET Framework standard, ovvero cambiare il puntatore solo su un controllo che volete voi. Difatti, la visualizzazione della clessidra è consentita solo al centro dello schermo.</p> <p>Come farlo nelle vostre applicazioni per palmari? Seguite la ricetta che ho preparato per voi.</p> <p><strong>Ingredienti per uno sviluppatore (difficoltà bassa)<br></strong>Una costante di tipo <font color="#0000ff">int</font><br>Due funzioni extern static importate dalla coredll.dll<br>Una funzione managed con un parametro di tipo <font color="#0000ff">bool</font> e ritorna <font color="#0000ff">void</font></p> <p><strong>Preparazione<br></strong>Prendete una Windows Form, e dichiarate quanto segue:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:6354ca41-75d6-4a13-8895-481bb4cc729e" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">const</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> HourGlassCursorID </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">32514</span><span style="color: #000000; ">; [DllImport(</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">coredll.dll</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">)] </span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">extern</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">static</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> LoadCursor(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> zeroValue, </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> cursorID); [DllImport(</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">coredll.dll</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">)] </span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">extern</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">static</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> SetCursor(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> cursorHandle);</span></div></pre></div></p> <p>Lasciar cuocere 1 minuto a fuoco lento. La costante è di tipo <font color="#0000ff">int</font> e deve valere 32514, equivalente a 0x7F02. Poi vengono dichiarate due funzioni extern, tramite P/Invoke, entrambe definite nella library coredll.dll. La funzione <strong>LoadCursor</strong> carica un cursore dato un cursorID; il cursore viene ritornato da un tipo <font color="#0000ff">int</font> che è un cursorHandle.<br>Quello che manca è la funzione managed:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:9c5399f7-6696-4b72-af98-597fb3d8a08a" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> ShowWaitCursor(</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; "> ShowCursor) { </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> cursorHandle </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">; </span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (ShowCursor) { cursorHandle </span><span style="color: #000000; ">=</span><span style="color: #000000; "> LoadCursor(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">, HourGlassCursorID); } SetCursor(cursorHandle); }</span></div></pre></div></p> <p>Dare una mescolata con un cucchiaio di legno. La vostra applicazione può tranquillamente utilizzare la funzione <strong>ShowWaitCursor</strong> per visualizzare/nascondere la clessidra sul display del palmare. Attenzione, come dicevo prima, la clessidra, indipendentemente dalla forma che assume, non è modale, perciò, per esempio, potete scrivere quanto segue:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:956f7219-4058-464a-a5f6-8d0d6b7a547d" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> btnMioBottone_Click(</span><span style="color: #0000FF; ">object</span><span style="color: #000000; "> sender, EventArgs e) { ShowWaitCursor(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">); </span><span style="color: #008000; ">/*</span><span style="color: #008000; "> Faccio 1.000 cose che durano un po' </span><span style="color: #008000; ">*/</span><span style="color: #000000; "> ShowWaitCursor(</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">); }</span></div></pre></div></p> <p>Ricordatevi <strong>sempre ed in ogni caso</strong> di disattivare la clessidra, altrimenti questa rimane visibile sullo schermo anche se l'applicazione continua a funzionare regolarmente, ma disturbando notevolmente la digestione dell'utente.</p> <p>Da servire freddo, magari con un buon vino del Friuli. :-)<br>Buon appetito.</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/75189.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/04/11/75189.aspx Wed, 11 Apr 2007 13:08:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/04/11/75189.aspx#feedback 7 http://blogs.ugidotnet.org/idamiani/comments/commentRss/75189.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/75189.aspx Download aggiornato per il VivendoByte Emoticons plug-in http://blogs.ugidotnet.org/idamiani/archive/2007/04/07/74976.aspx <p>Molti, molti mesi fa qualcuno di UGI mi aveva segnalato un problema sul mio plug-in in WPF per Windows Live Writer. Avevo realizzato un piccolo batch che copiava le immagini dalla directory di installazione alla directory dove poi il plug-in si aspettava di trovarle.&nbsp;Il fatto era che il batch le copiava in un posto, e poi il plug-in le cercava in un altro e quindi veniva sollevata un'exception: questo mi pare di ricordare che succedesse solo su un OS non localizzato in italiano, ma non ricordo bene 'sta cosa.</p> <p>L'amico di UGI che mi aveva segnalato la cosa non si deve offendere se non ricordo il suo nome: dimentico un po' troppo spesso i nomi delle persone, persino degli amici che conosco da 20 anni!!!! E non è una frase fatta solo per farmi perdonare, è così sul serio.</p> <p>Ho appena uploadato la versione corretta dell'installer, <a href="http://www.igordamiani.it/download.php?id=16" target="_blank">scaricabile da qui</a>. L'installer installa di default dentro:</p> <p><em><font color="#0000ff">C:\Program Files\VivendoByte\Emoticons plug-in for Windows Live Writer</font></em></p> <p>Qui dentro trovate l'assembly <strong>VivendoByteEmoticons.dll</strong> che va copiato manualmente dentro <strong>C:\Program Files\Windows Live Writer\Plugins</strong>. Volendo lo si può far fare al file batch Install.bat: assicuratevi solo di farlo girare come Administrator, perchè altrimenti non ha i permessi per accedere a C:\Program Files.</p> <p>Anche se non vi dice nulla, al primo avvio il plug-in copia automaticamente le emoticons nella directory giusta, per cui adesso siete a posto. Sotto il mio Windows Vista English tale directory è <strong>C:\ProgramData\VivendoByte Emoticons</strong>. Volendo, potete aggiungere/rimuovere tutte le emoticons che volete, o creare tante cartelle per poterle organizzare come volete voi.</p> <p><a href="http://www.igordamiani.it/download.php?id=16" target="_blank">Il plug-in è scaricabile gratuitamente da qui</a></p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/74976.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/04/07/74976.aspx Sat, 07 Apr 2007 20:27:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/04/07/74976.aspx#feedback http://blogs.ugidotnet.org/idamiani/comments/commentRss/74976.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/74976.aspx Connettere l'emulatore Pocket PC via ActiveSync http://blogs.ugidotnet.org/idamiani/archive/2007/04/06/74895.aspx <p>Avrete capito dagli ultimi post che ho scritto che attualmente mi occupo dello sviluppo su dispositivi mobile, Pocket PC 2003 e Windows Mobile: questo vuol dire finalmente tanto .NET e C#, sfruttando il Compact Framework con tutti gli annessi e connessi. La cosa più affascinante è sentir parlare di classi astratte, di metodi factory e di tutti concetti che nell'azienda dove ero prima semplicemente mi sognavo. :-)</p> <p>Oggi ho cominciato ad ingranare sul serio. E' difficile inserirsi in un progetto già in fase di sviluppo, mi devo ritagliare il mio spazio, e soprattutto inserirmi nel team che definire <strong>vulcanico</strong> è un eufemismo.</p> <p>Sviluppando su palmare, è essenziale sfruttare al meglio l'emulatore offerto da Visual Studio 2005. L'emulatore è piuttosto affidabile, e segue lo stesso comportamento di un palmare vero e proprio. Ci sono ovviamente alcuni vantaggi nell'usare un emulatore piuttosto che un palmare vero e proprio:</p> <ol> <li>non consumate batterie di alcun tipo <li>se dovete digitare qualcosa sull'emulatore, potete farlo usando la tastiera</li></ol> <p><strong>Con Visual Studio 2005 potete persino simulare la connessione tramite ActiveSync</strong>.&nbsp;Ieri pomeriggio ho perso un pochino di tempo su questo. Il mio codice deve scaricare da un server Web un file zip e salvarselo in locale per dezipparlo e fare qualche lavoretto sui files contenuti nel file compresso. Il codice è simile a quanto riportato qui di seguito:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:3de0659a-899c-49ff-b6ca-fc1611811d18" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:#DADAA5;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> DownloadPackage() { </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> byteRead </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">; </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> totalByteRead </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">; </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> offset </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">; </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> packet </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">1024</span><span style="color: #000000; ">; Uri url </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> Uri(</span><span style="color: #000000; ">@&quot;</span><span style="color: #000000; ">http://server/download.zip</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">); HttpWebRequest myReq </span><span style="color: #000000; ">=</span><span style="color: #000000; "> (HttpWebRequest)WebRequest.Create(url); Stream odlDownload </span><span style="color: #000000; ">=</span><span style="color: #000000; "> myReq.GetResponse().GetResponseStream(); ... ... }</span></div></pre></div></p> <p>Una volta ottenuto il riferimento allo stream, è possibile scaricare il file via http. Il problema è: <strong>come poter andare su Internet (o sulla propria LAN)&nbsp;con l'emulatore del Pocket PC?</strong> Per default, l'emulatore non è connesso alla rete, e quindi non può accedere ad http, nemmeno se si tratta di raggiungere un sito Web hostato da IIS sul PC di sviluppo.</p> <p>La risposta, una volta che la si conosce, è piuttosto semplice. Installiamo innanzitutto ActiveSync sul PC su cui stiamo sviluppando. Poi <em>tiriamo su</em> l'emulatore del Pocket PC premendo F5 da Visual Studio 2005:&nbsp; questa operazione compila il progetto, attiva l'emulatore, fa il deploy della soluzione sul palmare e fa partire l'eseguibile. La soluzione consiste nel connettere il Pocket PC emulato all'ActiveSync sul PC: per farlo, è sufficiente andare su Tools --&gt; Device Emulator Manager,&nbsp;trovare l'emulatore che è in esecuzione, cliccare col destro e cliccare sulla voce <strong>Cradle</strong>. Se l'ActiveSync sul PC accetta connessioni da USB, vedrete che l'emulatore si connette normalmente come se fosse un palmare reale a tutti gli effetti. Stessa cosa per il PC: l'icona di ActiveSync nella tray-bar diventa verde e dovrebbe partire il wizard che permette la creazione di una relazione tra PC e palmare. Non fatevi troppe menate, impostate la relazione come 'ospite' e siete a posto.</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/74895.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/04/06/74895.aspx Fri, 06 Apr 2007 10:24:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/04/06/74895.aspx#feedback 3 http://blogs.ugidotnet.org/idamiani/comments/commentRss/74895.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/74895.aspx Zip/Unzip con il compact framework? http://blogs.ugidotnet.org/idamiani/archive/2007/04/04/74757.aspx <p>Anche se fate fatica a trovare qualcosa per manipolare i files zip con il Compact Framework, sappiate che qualcosa c'è.</p> <p>Innanzitutto trovate le <a href="http://xceed.com/Zip_NET_CF_Intro.html" target="_blank">Xceed Zip for .NET CF</a>, ma sono a pagamento, e costano la bellezza di 499,95 dollari. L'elenco delle caratteristiche è certamente interessante, ma se avete bisogno di qualcosa di più semplice (e gratuito) potete ricorrere alla libreria <a href="http://www.icsharpcode.net/OpenSource/SharpZipLib/Default.aspx" target="_blank">SharpZipLib</a>. Essa è disponibile sotto la licenza GPL.</p> <p>L'ho provata in questi giorni per applicarla al progetto su cui sto lavorando. Siccome ho avuto qualche problemino, voglio segnalarlo anche a voi per evitare di perdere troppo tempo come è capitato a me. Innanzitutto, mentre sul PC Desktop l'assembly SharpZipLib deve essere installato nella GAC, questo ovviamente non vale per i Pocket PC: è sufficiente che la dll sia nella stessa directory dell'applicazione per poterla utilizzare.</p> <p>L'altra questione&nbsp;è un po' più pesante, e&nbsp;solo grazie all'aiuto del buon <a href="http://blogs.ugidotnet.org/marcom/" target="_blank">Marco Minerva</a> sono arrivato alla soluzione in tempi rapidi, dato che lui&nbsp;ha incontrato il mio stesso problema <a href="http://blogs.ugidotnet.org/marcom/archive/2007/01/19/67700.aspx" target="_blank">più o meno un mesetto fa</a>. Se utilizzate esattamente lo stesso assembly che scaricate dal sito, al momento di comprimere/decomprimere utilizzando la classe FastZip il codice solleverà un'eccezione, che adesso purtroppo non riesco ad indicarvi in modo preciso: dovrebbe essere qualcosa del tipo MethodNotFoundException. Questo è dovuto al fatto che l'assembly di SharpZipLib tenta di accedere alla proprietà <strong>CurrentCulture</strong> esposta da <strong>System.Threading.Thread.CurrentThread</strong>. La proprietà CurrentCulture esiste nel .NET Framework, ma non nella sua versione per palmari. SharpZipLib ha bisogno di accedere a questa proprietà per poter formattare a video le date in modo opportuno in base alla vostra Culture. Se non avete bisogno di queste informazioni, ad esempio se volete semplicemente prendere uno zip e decomprimerlo da qualche parte, potete fare quello che ha fatto il buon Marco, che ha esaminato il codice, lo ha ripulito e reso "funzionante" davvero su CF.</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/74757.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/04/04/74757.aspx Wed, 04 Apr 2007 23:24:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/04/04/74757.aspx#feedback 3 http://blogs.ugidotnet.org/idamiani/comments/commentRss/74757.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/74757.aspx Bitmap vettoriali sulle Window http://blogs.ugidotnet.org/idamiani/archive/2007/03/26/73933.aspx <p>Con WPF tutto deve (può?) essere scalabile. Basta usare le bitmap, quindi, perchè se ridimensioniamo una Window, l'immagine viene ingrandita a sua volta e potrebbe <em>sgranare</em> in modo spiacevole. L'ideale è quindi usare icone ed immagini vettoriali, che possano scalare in modo naturale.</p> <p>Il sito <a href="http://www.grafile.com/presentation/icon_library.html" target="_blank">grafile.com</a> mette a disposizione (<strong>a pagamento</strong>, e secondo me sono anche piuttosto cari) diverse collection di icone vettoriali, suddivise per argomenti (business, character, control, icon, database, documentation, file &amp; folder, network...insomma, le solite cose). Dicevo...mi sembrano un po' cari, perchè far pagare&nbsp;72 dollari un pacchetto con 56 icone mi sembra un po' esagerato. Il resto dei pacchetti non è da meno. Fate voi, io l'ho segnalato e basta.</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/73933.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/03/26/73933.aspx Mon, 26 Mar 2007 19:01:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/03/26/73933.aspx#feedback 2 http://blogs.ugidotnet.org/idamiani/comments/commentRss/73933.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/73933.aspx Le mie (personali) linee-guida per il layout con WPF http://blogs.ugidotnet.org/idamiani/archive/2007/03/26/73931.aspx <p>WPF è un framework dalle innumerevoli caratteristiche che possono lasciare - credo - disorientato il neofita. Al di là di tante squisitezza tecnologiche, la cosa che mi ha fatto fatto più perdere tempo è l' <em>auto-layout</em>, ovvero la capacità di WPF di dimensionare e di posizionare i controlli sulle Windows o sulle Page in modo completamente automatico, seguendo logiche che probabilmente sono più vicine alla creazione delle pagine Web.</p> <p>L'auto-layout permette innanzitutto di creare interfacce utente indipendenti dalla risoluzione. Scordatevi quindi di trascinare controlli dalla ToolBox sulla Window e di posizionarli dove e come volete voi. In realtà, potete farlo ancora: è sufficiente utilizzare il Canvas per ottenere esattamente lo stesso tipo di posizionamento che avete oggi con le classiche Windows Forms. Ovvio dire che l'utilizzo del Canvas è sconsigliato. La morale è questa: qualsiasi controllo assume le dimensioni del container che lo ospita. Se avete una Window e all'interno di essa definite una Grid con una sola riga ed una sola colonna, tale Grid sarà grande esattamente come la Window:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:6e83f8de-f946-4eeb-87b9-eff4e488b4aa" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Window </span><span style="color: #FF0000; ">x:Class</span><span style="color: #0000FF; ">=&quot;VivendoByte.CastingManager.TestWindow&quot;</span><span style="color: #FF0000; "> xmlns</span><span style="color: #0000FF; ">=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;</span><span style="color: #FF0000; "> xmlns:x</span><span style="color: #0000FF; ">=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;</span><span style="color: #FF0000; "> Title</span><span style="color: #0000FF; ">=&quot;test&quot;</span><span style="color: #FF0000; "> Background</span><span style="color: #0000FF; ">=&quot;LightYellow&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Grid </span><span style="color: #FF0000; ">Background</span><span style="color: #0000FF; ">=&quot;LightGray&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Grid</span><span style="color: #FF0000; ">.ColumnDefinitions</span><span style="color: #0000FF; ">&gt;&lt;</span><span style="color: #800000; ">ColumnDefinition </span><span style="color: #0000FF; ">/&gt;&lt;/</span><span style="color: #800000; ">Grid.ColumnDefinitions</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Grid</span><span style="color: #FF0000; ">.RowDefinitions</span><span style="color: #0000FF; ">&gt;&lt;</span><span style="color: #800000; ">RowDefinition </span><span style="color: #0000FF; ">/&gt;&lt;/</span><span style="color: #800000; ">Grid.RowDefinitions</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Grid</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Window</span><span style="color: #0000FF; ">&gt;</span></div></pre></div></p> <p>La Grid ha uno sfondo LightGray, mentre il colore di sfondo della Window è LightYellow. Quest'ultimo colore però non viene visto, proprio perchè la Grid assume automaticamente le stesse dimensioni della Window e ne ricopre interamente la superficie. Se alla Grid assegniamo una proprietà Margin le cose cominciano a cambiare:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:80ab9611-3211-4d7c-8b6a-653cc991a1de" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Grid </span><span style="color: #FF0000; ">Background</span><span style="color: #0000FF; ">=&quot;LightGray&quot;</span><span style="color: #FF0000; "> Margin</span><span style="color: #0000FF; ">=&quot;20&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> ... </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Grid</span><span style="color: #0000FF; ">&gt;</span></div></pre></div></p> <p>In questi giorni, di sera, sto sviluppando un software commerciale in WPF, convertendo un software scritto in VB6.0 alcuni anni fa. Ho dovuto quindi ripensare in parte la struttura dell'applicazione, e ho dovuto anche ridisegnare le form trasformandole in Window di WPF. Come è facile intuire, non sono form semplici e ho dovuto litigare un pochino per ottenere un layout degno di questo nome. Le mie linee-guide sono, per adesso, le seguenti:</p> <ol> <li>Non assegno mai dimensioni ai controlli, lascio che esse vengano ereditate dai controlli contenitori e più in alto nel visual-tree</li> <li>Per evitare di avere Window definite in un unico gigantesco (e poco leggibili)&nbsp;XAML, definisco uno o più UserControl separati, che mi aiutano a disegnare poco alla volta&nbsp;o a debuggare meglio lo XAML stesso.</li> <li>Evito di assegnare in modo statico le dimensioni ai vari UserControl: come nel punto (1), faccio in modo che le dimensioni vengono assegnate automaticamente dal container nel quale lo UserControl viene inserito.</li> <li>Gioco con i Margin (spazio tra un controllo e l'altro) ed i Padding (spazio tra il bordo di un controllo ed il suo contenuto)&nbsp;per staccare i controlli l'uno dall'altro e per riempire in modo piacevole uno spazio (che sia GroupBox, un TabItem o simili).</li> <li>Gli unici momenti&nbsp;in cui assegno esplicitamente una Width&nbsp;od una Height sono le Grid. Se una Grid possiede solo righe, assegno le altezze di queste righe. Se una Grid possiede solo colonne, assegno le larghezze di queste colonne.</li> <li>Mi piace assegnare MinWidth e MinHeight alle Window. L'utente può ridimensionare finchè vuole, ma senza esagerare.</li> <li>Non uso mai e poi mai unità di misura particolari (come px, cm o roba del genere).</li> <li>Uso spesso l'asterisco (*) nei dimensionamenti, per indicare quando una colonna od una riga possono allargarsi o stringersi a piacere. Esempio: una Grid con&nbsp;2 righe. 1° Riga : altezza fissa, contiene uno UserControl per&nbsp;esprimere diversi criteri&nbsp;con i quali fare ricerche. 2° Riga : altezza a piacere, contiene una ListBox che contiene i risultati della ricerca.</li></ol> <p>Non so se sono indicazioni corrette oppure no. Le ho semplicemente estratte dagli XAML e dalla mia esperienza proveniente dal mio primo vero progetto serio in WPF e mi sono state utili.</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/73931.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/03/26/73931.aspx Mon, 26 Mar 2007 18:40:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/03/26/73931.aspx#feedback 2 http://blogs.ugidotnet.org/idamiani/comments/commentRss/73931.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/73931.aspx Il TabControl di WPF http://blogs.ugidotnet.org/idamiani/archive/2007/03/23/73675.aspx <p>Uno dei controlli che Petzold non cita nel suo libro è il <strong>TabControl</strong>, al punto che pensavo che non fosse compreso tra i controlli nativi di WPF. Ieri, sfogliando MSDN a piacimento, ho raggiunto il namespace <a href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.aspx" target="_blank"><strong>System.Windows.Controls</strong></a> e mi capita sottomano proprio la classe <strong><a href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.tabcontrol.aspx" target="_blank">TabControl</a></strong>. Quando mi capita di vedere una classe che non sapevo esistesse, mi diverto sempre a guardare la Inheritance Hierarchy, perchè&nbsp;vedendo da cosa deriva una determinata classe si possono intuire caratteristiche o capacità ereditate da altri. In questo caso specifico, ho notato che <strong>TabControl</strong> eredita anche da <a href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.itemscontrol.aspx" target="_blank"><strong>ItemsControl</strong></a>, così come la ListBox, i menù, la StatusBar e così via. MSDN dà una descrizione per la classe ItemsControl, che recita così:</p> <p><em><font color="#0000ff">Represents a control that can be used to present a collection of items.</font></em></p> <p>La collection di items per un controllo di tipo <strong>TabControl</strong> non sono nient'altro che i suoi <strong>TabItem</strong>. L'utente naviga all'interno di un <strong>TabControl</strong> selezionando il tab che gli interessa: seleziona un Item tra gli Items disponibili, esattamente come accade con una ListBox. Il <strong>TabControl</strong> è ovviamente definibile via XAML in modo molto semplice:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:fc11c05c-db45-4a9c-8f62-ab0585b879a3" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">TabControl </span><span style="color: #FF0000; ">Name</span><span style="color: #0000FF; ">=&quot;tabSchede&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">TabItem </span><span style="color: #FF0000; ">Header</span><span style="color: #0000FF; ">=&quot;Dati Anagrafici&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">TabItem </span><span style="color: #FF0000; ">Header</span><span style="color: #0000FF; ">=&quot;Dati Fisici&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">TabItem </span><span style="color: #FF0000; ">Header</span><span style="color: #0000FF; ">=&quot;Altre Informazioni&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">TabControl</span><span style="color: #0000FF; ">&gt;</span></div></pre></div></p> <p>Via codice, possiamo capire se un TabControl ha qualche Items accedendo alla proprietà <a title="HasItems" href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.itemscontrol.hasitems.aspx">HasItems</a>. La proprietà <a href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.headereditemscontrol.header.aspx" target="_blank">Header</a> di ciascun TabItem non è semplicemente string, ma può essere un qualsiasi content, esattamente come accade per tutti gli altri controlli di WPF. Ieri sera mi sono divertito a rendere un po' diverso l'header di ciascun TabItem. Per esempio, con XAML possiede utilizzare la notazione nota con <em>property elements</em> e scrivere una cosa simile a questa:</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:3a28c028-9ab6-403f-b467-2048451bcff6" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">TabControl </span><span style="color: #FF0000; ">Margin</span><span style="color: #0000FF; ">=&quot;5&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">TabItem</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">TabItem</span><span style="color: #FF0000; ">.Header</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">StackPanel </span><span style="color: #FF0000; ">Orientation</span><span style="color: #0000FF; ">=&quot;Horizontal&quot;</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Rectangle </span><span style="color: #FF0000; ">Stroke</span><span style="color: #0000FF; ">=&quot;Black&quot;</span><span style="color: #FF0000; "> Fill</span><span style="color: #0000FF; ">=&quot;Red&quot;</span><span style="color: #FF0000; "> Width</span><span style="color: #0000FF; ">=&quot;10&quot;</span><span style="color: #FF0000; "> Height</span><span style="color: #0000FF; ">=&quot;10&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Label </span><span style="color: #FF0000; ">Margin</span><span style="color: #0000FF; ">=&quot;6 2 0 0&quot;</span><span style="color: #FF0000; "> Padding</span><span style="color: #0000FF; ">=&quot;0&quot;</span><span style="color: #FF0000; "> Content</span><span style="color: #0000FF; ">=&quot;Dati Anagrafici&quot;</span><span style="color: #FF0000; "> </span><span style="color: #0000FF; ">/&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">StackPanel</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">TabItem.Header</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">TabItem</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">TabControl</span><span style="color: #0000FF; ">&gt;</span></div></pre></div></p> <p>L'header del TabItem contiene uno StackPanel orizzontale, che contiene a sua volta un piccolo rettangolo colorato ed una semplice Label con del testo. Ho giocherellato con Padding e Margin per posizionare gli elementi in modo decente. Ipotizzo che avremmo potuto usare anche la proprietà <a title="ItemTemplate" href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemtemplate.aspx">ItemTemplate</a>, che definisce l'aspetto utilizzato per generare gli items: ci proverò, perchè questa è una cosa che ho solo in testa e non l'ho provata sul campo. La cosa interessante è che i TabItem possono anche essere generati dal runtime di WPF via data-binding, perchè è sufficiente <em>bindare</em> <a title="ItemsSource" href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemssource.aspx">ItemsSource</a>&nbsp;così come faremmo con una ListBox.</p> <p>Ovviamente, ogni TabItem ha una proprietà Content che definisce il contenuto reale di ogni tab. Il più delle volte il contenuto è organizzato con un altro Panel (la Grid, per esempio) per posizionare i vari controlli che ci interessa vedere sulla Window. La proprietà <a title="TabStripPlacement" href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.tabcontrol.tabstripplacement.aspx">TabStripPlacement</a>&nbsp;permette di decidere dove vogliamo vedere i tab del TabControl (Top, Bottom, Left o Right). La proprietà <a title="SelectedItem" href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.primitives.selector.selecteditem.aspx">SelectedItem</a>&nbsp;ci dà un riferimento al TabItem correntemente selezionato, mentre <a title="SelectedIndex" href="http://msdn2.microsoft.com/en-us/library/system.windows.controls.primitives.selector.selectedindex.aspx">SelectedIndex</a>&nbsp;il suo indice.</p> <p>Basta dare un'occhiata all'elenco delle public properties del TabControl per capire come sia possibile applicare diversi stili o template ad ogni elemento che compone&nbsp;il controllo, fornendo un'ampia gamma di possibilità per personalizzare l'aspetto del TabControl stesso.</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/73675.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/03/23/73675.aspx Fri, 23 Mar 2007 12:15:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/03/23/73675.aspx#feedback http://blogs.ugidotnet.org/idamiani/comments/commentRss/73675.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/73675.aspx Un domain model per lavorare con WPF e NHibernate http://blogs.ugidotnet.org/idamiani/archive/2007/03/19/73262.aspx <p>Da quando ho scoperto <strong>NHibernate</strong>, ormai più di un anno fa, ho sempre disegnato il mio domain-model in modo tale che far persistere le mie entità con questo framework fosse il più naturale possibile. Dal punto di vista pratico, ciò significa che tutte le entità del domain-model derivino tutte da una classe astratta <strong>Entity</strong>, che definisce una sola proprietà ID di tipo <font color="#0000ff">int</font>.</p> <p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:9baf19ec-93de-4cd4-b347-8c96efab72a1" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">abstract</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> Entity { </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> ID { </span><span style="color: #0000FF; ">get</span><span style="color: #000000; "> { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> _id; } </span><span style="color: #0000FF; ">set</span><span style="color: #000000; "> { _id </span><span style="color: #000000; ">=</span><span style="color: #000000; "> value; } } }</span></div></pre></div></p> <p>Ogni volta che una classe&nbsp;del mio domain-model deve essere persistita&nbsp;attraverso NHibernate, la faccio derivare da Entity. In questo modo, ogni classe espone una proprietà <strong>ID</strong> che contiene la chiave univoca dell'istanza. Ovviamente tutte le entità hanno un <strong>unsaved-value = 0</strong>, e questo permette all'engine di NHibernate di capire in che modo persistere una determinata istanza sul database.&nbsp;Quando ID = 0, l'oggetto esiste solo in memoria (<font color="#008040">oggetto <em>transiente</em></font>)&nbsp;e non è stato ancora persistito. Se ID != 0, l'oggetto in memoria esiste anche sul database (<font color="#008040"><em>oggetto persistente</em></font>).</p> <p><strong>Tutto deriva da Entity? Bene, un vantaggio anche per il mio DAL</strong><br>Il fatto che tutte le mie entità derivino dalla classe astratta <strong>Entity</strong> porta anche un vantaggio all'interno del DAL. Di solito, i miei DAL utilizzano i generics. Pertanto, posso applicare un constraint, dicendo che il tipo T deve per forza rappresentare una classe derivata da <strong>Entity</strong>. La morale di tutto questo è che non posso istanziare un'istanza di DataProvider&lt;int&gt; o un DataProvider&lt;MyAppSettings&gt;, ma solo di DataProvider&lt;Artista&gt;, DataProvider&lt;Auto&gt;, DataProvider&lt;Competenza&gt; e così via, cioè solo di quelle classi che so che dovranno essere rese persistenti da NHibernate.</p> <p>Fin qua nulla di male. Cosa succede però se devo creare un domain-model da utilizzare all'interno di un'applicazione WPF?&nbsp;Con .NET 2.0, ad esempio, per poter sfruttare le potenzialità di data-binding sulle Windows Forms era opportuno creare un domain-model le cui classi implementassero l'interfaccia <strong>INotifyPropertyChanged</strong>. Questa interfaccia consente l'implementazione di un data-binding efficiente tra un oggetto ed i controlli sulla UI, e viceversa. Questo approccio funziona ancora in WPF, non è cambiato nulla da questo punto di vista, ma per tutta una serie di ragioni è opportuno ragionare in modo diverso.</p> <p><strong>Perchè utilizzare le <strong>dependency properties</strong>?</strong><br>Questo "modo diverso" consiste nell'utilizzare le <strong>dependency properties</strong>, che consentono di sfruttare features avanzate come data-binding <strong>serio</strong>, che con WPF va molto al di là delle potenzialità offerte da .NET 2.0. Per esempio, potremmo <em>bindare</em> la proprietà <strong>Eta</strong> della classe <strong>Artista</strong> con uno Shape di qualche tipo, in modo tale che a seconda dell'età&nbsp;il Rectangle che abbiamo sulla Window si allunghi o si accorci. Oppure, potremmo definire un trigger sulla proprietà booleana&nbsp;<strong>PossiedeAuto</strong>, in modo tale che quando vale <font color="#0000ff">true</font>, appaia un'immagine da qualche parte, altrimenti no. In generale, molte delle funzionalità di WPF (stili, template, trigger, data-binding, animazioni, etc.) sfruttano proprio&nbsp;le dependency properties: se sappiamo che il nostro domain-model dovrà in qualche modo interagire con l'engine di WPF, forse è&nbsp;bene pensarci prima e disegnare le nostri classi in modo opportuno.</p> <p>Quando un oggetto intende utilizzare le dependency properties, deve derivare dalla classe <strong>DependencyObject</strong> di WPF. Qui abbiamo un problema: le entità del nostro domain-model dovrebbero derivare da due classi, sia <strong>DependencyObject</strong> che <strong>Entity</strong>. Questo in .NET non è possibile, in quanto non è prevista ereditarietà multipla. Poco male, è sufficiente pensare diversamente e invece di aver a che fare con una classe&nbsp;<strong>Entity</strong>, definiamo un'interfaccia <strong>IPersistableEntity</strong> (dal nome altisonante :-).</p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:362177d6-69b4-4b07-aafb-dc5a71eb52d8" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: #0000FF; ">using</span><span style="color: #000000; "> System; </span><span style="color: #0000FF; ">using</span><span style="color: #000000; "> System.Windows; </span><span style="color: #0000FF; ">namespace</span><span style="color: #000000; "> DomainModel { </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">interface</span><span style="color: #000000; "> IPersistableEntity { </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> ID { </span><span style="color: #0000FF; ">get</span><span style="color: #000000; ">; </span><span style="color: #0000FF; ">set</span><span style="color: #000000; ">; } } </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> Artista : DependencyObject, IPersistableEntity { </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">static</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">readonly</span><span style="color: #000000; "> DependencyProperty IDProperty; </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> ID { </span><span style="color: #0000FF; ">get</span><span style="color: #000000; "> { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> (</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">)GetValue(IDProperty); } </span><span style="color: #0000FF; ">set</span><span style="color: #000000; "> { SetValue(IDProperty, value); } } </span><span style="color: #0000FF; ">static</span><span style="color: #000000; "> Artista() { Type tp </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">typeof</span><span style="color: #000000; ">(Artista); IDProperty </span><span style="color: #000000; ">=</span><span style="color: #000000; "> DependencyProperty.Register(</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">ID</span><span style="color: #000000; ">&quot;</span><span style="color: #000000; ">, </span><span style="color: #0000FF; ">typeof</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">), tp); } } }</span></div></pre></div> <p>La classe <strong>Artista</strong> qui sopra è&nbsp;stata compressa/adattata/sistemata per riportare&nbsp;parte&nbsp;del codice. Il codice di produzione è evidentemente organizzato&nbsp;un po' meglio e&nbsp;migliorato sotto diversi aspetti.&nbsp;Comunque sia, la classe eredita da <strong>DependencyObject</strong> ed implementa l'interfaccia <strong>IPersistableEntity</strong>. Quest'ultima in particolare richiede l'implementazione di una proprietà ID di tipo <font color="#0000ff">int</font>, che è stata realizzata come dependency property. Questo implica una serie di cose che sono ben visibili dal codice: un field pubblico IDProperty di tipo <strong>DependencyProperty</strong>, un costruttore privato che registra tutte le dependency properties che mi servono.</p> <p><strong>Conclusione<br></strong>Implementando il domain-model secondo questi criteri, sviluppo classi che possono&nbsp;lavorare sull'interfaccia di&nbsp;WPF e possono essere persistite da NHibernate passando dal DAL del progetto che sto sviluppando (progetto per una volta non freeware).</p><img src="http://blogs.ugidotnet.org/idamiani/aggbug/73262.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2007/03/19/73262.aspx Mon, 19 Mar 2007 13:54:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2007/03/19/73262.aspx#feedback 11 http://blogs.ugidotnet.org/idamiani/comments/commentRss/73262.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/73262.aspx