<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>WPF</title>
        <link>http://blogs.ugidotnet.org/pietrolibroblog/category/WPF.aspx</link>
        <description>WPF</description>
        <language>it-IT</language>
        <copyright>Pietro Libro</copyright>
        <managingEditor>pietro.libro@libero.it</managingEditor>
        <generator>Subtext Version 2.6.0.0</generator>
        <item>
            <title>ASP.NET 4.5 e WebSocket: Real Time e Two-Way (parte 1)</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2012/09/07/asp.net-4.5-e-websocket-real-time-e-two-way-parte.aspx</link>
            <description>Chi sviluppa per il Web, si scontra ogni giorno con le “limitazioni” del modello request-response di HTTP, il quale non è pensato “ad hoc” per applicazioni real time, o comunicazione bidirezionale client-server. Per questi scenari, spesso vengono utilizzate tecniche di polling, da parte del client, per verificare l’eventuale presenza di dati “freschi”, eseguendo continuamente delle richieste HTTP, comportando dei “costi”: il server deve utilizzare differenti connessioni TCP per ogni client (una per inviare informazioni ed una per rispondere alla richiesta di aggiornamenti), un continuo scambio di messaggi client-server (comportando quindi un aumento del traffico di rete). Potrebbe sembrare cosa da poco, ma se pensiamo ad applicazioni web con migliaia di client connessi, le risorse di server e banda potrebbero essere velocemente consumate (e dato che la nostra epoca è affamata di risorse potremmo trovarci in situazioni poco piacevoli).   Quindi, quale potrebbe essere la soluzione ? utilizzare una singola connessione TCP per gestire il traffico in entrambe le direzioni, ovvero il protocollo WebSocket, che se gestito mediante le WebSocket API (WSAPI), fornisce un’ottima alternativa alla tecnica di polling di cui parlavamo in precedenza. Possiamo utilizzare i WebSocket in diversi scenari: giochi, applicazioni di editing real time multi-utente, UI web che espongono dati lato server in tempo reale ecc .… tutto questo condividendo le porte standard HTTP 80 e 443, “attraversando” firewalls, proxies e router senza problemi. Possiamo riassumere le due “tecniche” discusse in precedenza con la figura seguente;    dalla quale si evince come l’utilizzo del “polling” comporti una maggiore latenza nella trasmissione delle informazioni utili. Nell’immagine, nella comunicazione bidirezionale offerta dai WebSocket, come primo passo (dal client verso il server) c’è la voce “WebSocket upgrade”, cosa sarà mai ?, un passo per volta.  Il protocollo è diviso in due parti: handshakes e transfer data. Durante la  prima parte (di negoziazione),  client e server comunicano scambiandosi dei pacchetti di controllo per stabilire una connessione WebSocket.  Il client invia una richiesta di questo tipo:   
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Version: 13
ed il server (normalmente) dovrebbe rispondere con: 

 
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

In dettaglio, il processo di connessione parte con una standard GET HTTP, e fin qui tutto regolare:

GET /echo HTTP/1.1
Host: example.microsoft.com

poi, l’header “Upgrade” richiede al server di cambiare il protocollo a livello di application-layer da HTTP a WebSocket:

Upgrade: websocket
Connection: Upgrade

Il valore presente in Sec-WebSocket-Key inviato dal client verrà utilizzato dal server per far capire al chiamante che “comprende” la richiesta:

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

L’header Sec-WebSocket-Version identifica la versione del protocollo da utilizzare:

Sec-WebSocket-Version: 13

A questo punto, se il server “comprende” la nostra richiesta , il server eseguirà un “Upgrade” dell’application-layer e risponderà con lo stato HTTP 101. Il valore contenuto in Sec-WebSocket-Accept verrà utilizzato dal client per validare che il server è un WebSocket server e non un “semplice” HTTP server. 

Una volta che client e server hanno entrambi inviati i loro handshakes, (e l’handshaking ha avuto successo) può iniziare il trasferimento dati su un unico canale di comunicazione bidirezionale, tra client e server, sfruttando la connessione TCP precedentemente stabilita.

I dati trasferiti tra le due parti sono racchiusi in unità logiche chiamate “messaggi”, ogni messaggio è fisicamente  composto da uno o più frames. 

Dopo la teoria la pratica…

Il protocollo WebSocket, definisce due nuove tipologie di URI:


  ws://host [: port] path [? query] (di default usa la porta 80 e connessione non cifrata) 

  wss://host [:port] path [? query] (di default utilizza la porta 443, ed una connessione cifrata HTTP over TLS) 


Le ultime specifiche del protocollo  sono definite nel documento RFC 6455. Per “sporcarci le mani” con i  WebSocket, possiamo sviluppare un semplice pagina web con estensione .html, come la seguente:



I bottoni Open e Close, sono rispettivamente utilizzati per aprire e chiudere la connessione del WebSocket, mentre il campo text per inviare un messaggio al server, il quale risponderà inviando al client la lunghezza della stringa inviata (ovviamente il livello pratico dell’esempio è praticamente nullo ).

Per stabilire una connessione con il server, utilizziamo le seguenti righe di codice client-script:

var uri = 'ws://localhost:9915/WebSocketHandler.ashx';
websocket = new WebSocket(uri);

Per conoscere quando la connessione con il server è stata creata è necessario sottoscriversi all’evento onopen:

 websocket.onopen = function () {
    appendData("Connected.");
    appendData(uri);

    $('#wsform').submit(function (event) {
        websocket.send($('#inputbox').val());
        $('#inputbox').val('');
        event.preventDefault();
    });

    $("#closeButton").removeAttr("disabled");
};
Con lo script precedente, all’apertura della connessione visualizziamo il messaggio “Connected.” e l’URI utilizzato per la connessione. Dopodiché ci registriamo all’evento submit per inviare al server, utilizzando l’API Send, messaggi al WebSocket server sotto forma di testo UTF-8 o Blobs. 

Per ricevere messaggi, dobbiamo registrarsi all’evento onmessagge dell’oggetto websocket il quale riceve messageEvent che espone la proprietà data, contenente le informazioni inviate al client: 

websocket.onmessage = function (messageEvent) {
    var receivedData = messageEvent.data.toString();
    appendData(receivedData);
};

Il codice precedente è veramente banale, in quanto si limita a recuperare il testo inviato dal server in risposta al client e visualizzarlo sul browser tramite un div.

Come per l’apertura, c’è una sorta di handshaking per la chiusura della connessione, che può essere iniziata indipendentemente dal client o dal server. Chi inizia il processo di chiusura, invia uno speciale frame, detto close frame, che può contenere, opzionalmente, un codice di stato (il protocollo fornisce un insieme di codici di stato appropriati per la chiusura) e la ragione  (una descrizione testuale) della chiusura. Quando una delle due parti riceve un close frame, invia lo stesso frame all’altra parte (eventualmente, prima di rinviarlo, invia i messaggi pending) e la comunicazione viene chiusa.

Per iniziare il processo di close handshake, bisogna invocare l’API close:

function closeWebSocket() {
   websocket.close(1000, "Normal.");
}

Il valore 1000 indica lo stato “Normal Closure”. Come “ragione” della chiusura specifichiamo il testo “Normal”. Per sapere quando la chiusura è stata completata, ci sottoscriviamo all’evento onclose:

websocket.onclose = function () {
    appendData("Closed.");

    $("#closeButton").attr("disabled", "disabled");
};

Con la funzione precedente, visualizziamo il messaggio “Closed.”, e disabilitiamo il button per la chiusura del WebSocket.

La parte server è composta da una classe IHttpHandler che fa uso della classe WebSocketContext (.NET 4.5) utilizzata per accedere alle informazioni di WebSocket handshake. Attraverso altri semplici post, l’obiettivo è realizzare una semplice “lavagna” interattiva real time, utilizzando sia un browser web, che un client WPF, come mostrato seguente:



A tal fine, utilizzeremo lato Windows, la classe ClientWebSocket, presente nel namespace System.Net.WebSockets, la quale fornisce un client per connettersi a servizi esposti tramite WebSocket.

Source Code Source Code&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/101203.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2012/09/07/asp.net-4.5-e-websocket-real-time-e-two-way-parte.aspx</guid>
            <pubDate>Fri, 07 Sep 2012 07:58:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2012/09/07/asp.net-4.5-e-websocket-real-time-e-two-way-parte.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/101203.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/101203.aspx</trackback:ping>
        </item>
        <item>
            <title>Bing Maps WPF Control (Beta)</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2011/08/24/bing-maps-wpf-control-beta.aspx</link>
            <description>SDK per utilizzare le mappe di Bing all’interno delle nostre applicazioni WPF. Una volta installato, è sufficiente trascinare il controllo “Map” all’interno del Designer di WPF e scrivere praticamente nulla, piccolo esempio:     &amp;lt;my:Map HorizontalAlignment="Center" Margin="0" Name="map1" VerticalAlignment="Center"   Height="270" Width="500" CredentialsProvider="BING_KEY" Mode="AerialWithLabels"  Center="41.90,12.60" ZoomLevel="10"  /&amp;gt;

  





Ovviamente per l’utilizzo è necessario essere in possesso di una Bing Map Key. Download del controllo qui.&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/100295.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2011/08/24/bing-maps-wpf-control-beta.aspx</guid>
            <pubDate>Wed, 24 Aug 2011 12:24:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2011/08/24/bing-maps-wpf-control-beta.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/100295.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/100295.aspx</trackback:ping>
        </item>
        <item>
            <title>WPF, Windows Xp e file .ico</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2011/08/03/wpf-windows-xp-e-file-.ico.aspx</link>
            <description>Post promemoria. Se dopo aver deployato con ClickOnce un’applicazione WPF  (composta da due WPF Windows, senza nessuna forma di connessione a database) all’avvio si riscontra un errore del tipo:  Application: X     Framework Version: v4.0.30319      Description: The process was terminated due to an unhandled exception.      Exception Info: System.Windows.Markup.XamlParseException      Stack:         at System.Windows.Markup.XamlReader.RewrapException(System.Exception, System.Xaml.IXamlLineInfo, System.Uri)         at System.Windows.Markup.WpfXamlLoader.Load(System.Xaml.XamlReader, System.Xaml.IXamlObjectWriterFactory, Boolean, System.Object, System.Xaml.XamlObjectWriterSettings, System.Uri)  ….  Allora è importante verificare che l’applicazione non utilizzi file icona al di fuori dello standard Windows XP. Maggiori dettagli sulla soluzione proposta su Connect - WPF application fails if Window.Icon is a Vista .ico file.&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/100248.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2011/08/03/wpf-windows-xp-e-file-.ico.aspx</guid>
            <pubDate>Wed, 03 Aug 2011 11:35:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2011/08/03/wpf-windows-xp-e-file-.ico.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/100248.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/100248.aspx</trackback:ping>
        </item>
        <item>
            <title>In the box - MVVM Training</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/12/07/in-the-box-mvvm-training.aspx</link>
            <description>What is In the Box?  In the Box is a high quality, multi-media training that is consumed within Visual Studio 2010.  Content is navigated and delivered using a next generation computer based training (CBT) experience, the Visual Studio 2010 Feature Extension  Link: In the Box – MVVM Training&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/99538.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/12/07/in-the-box-mvvm-training.aspx</guid>
            <pubDate>Tue, 07 Dec 2010 07:31:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/12/07/in-the-box-mvvm-training.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/99538.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/99538.aspx</trackback:ping>
        </item>
        <item>
            <title>WPF: System.Windows.Threading.Dispatcher e BeginInvoke</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/08/28/wpf-system.windows.threading.dispatcher-e-begininvoke.aspx</link>
            <description>Spesso, quando si lavora con applicazioni multithread (soprattutto in applicazioni Windows Form), siamo abituati a ricorrere alla proprietà InvokeRequierd dei controlli per verificare se il thread chiamante è proprietario del controllo o se lo è un altro, e   nel caso, utilizzare un delegate per utilizzare la proprietà del controllo a cui siamo interessati.  Per un elemento WPF  dobbiamo utilizzare un’istanza di della classe System.Windows.Threading.Dispatcher, presente tra le proprietà che un elemento WPF espone. Ad esempio, supponiamo di utilizzare un’istanza della classe System.Timers.Timer per visualizzare l’ora corrente in un elemento Label:             1: _timer = new System.Timers.Timer();


       2: _timer.Interval = 1000;


       3: _timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);


       4: _timer.Start();



La variabile _timer è dichiarata a livello di modulo nel code-behind di una Window WPF. Se nel gestore dell’evento Elapsed del Timer scrivessimo del codice simile al seguente:


  
       1: void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)


       2: {


       3:     label1.Content = DateTime.Now.ToLongTimeString();            


       4: }



Verrebbe sollevata un’eccezione del tipo InvalidOperationException riportante  il seguente messaggio: “The calling thread cannot access this object because a different thread owns it.”  

Proviamo a scrivere un po’ di codice per utilizzare il Dispatcher:


  
       1: public delegate void onTickTimerDelegate();        


       2:  


       3: private void UpdateTimeLabel()


       4: {


       5:     if (label1.Dispatcher.Thread == Thread.CurrentThread)


       6:     {


       7:                 


       8:         label1.Content = DateTime.Now.ToLongTimeString();


       9:     }


      10:     else


      11:     {


      12:         onTickTimerDelegate tickTimerDelegate = new onTickTimerDelegate(UpdateTimeLabel);


      13:         label1.Dispatcher.BeginInvoke(tickTimerDelegate,


      14:             System.Windows.Threading.DispatcherPriority.Normal,


      15:             null);


      16:     }


      17: }



Se scriviamo il gestore di  Elapsed in questo modo:


  
       1: void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)


       2: {


       3:     UpdateTimeLabel();


       4: }



Tutto funziona correttamente. Facciamo un ulteriore passo  e modifichiamo il codice in modo che la il nostro metodo (e associato delegate) accetti anche un parametro:


  
       1: public delegate void onTickTimerDelegateWithParameter(object par);


       2: private void UpdateTimeLabel(object par)


       3: {


       4:     if (label1.Dispatcher.Thread == Thread.CurrentThread)


       5:     {


       6:         label1.Content = DateTime.Now.ToLongTimeString() + " PAR: " + par.ToString();


       7:     }


       8:     else


       9:     {


      10:         onTickTimerDelegateWithParameter tickTimerDelegateWithParameter = new onTickTimerDelegateWithParameter(UpdateTimeLabel);


      11:         label1.Dispatcher.BeginInvoke(tickTimerDelegateWithParameter,


      12:             System.Windows.Threading.DispatcherPriority.Normal,


      13:             par);


      14:     }


      15: }



In questo caso, nella contenuto della label visualizzeremo una stringa rappresentante l’ora  (ore, minuti e secondi) concatenata ad una stringa che “rappresenta” l’oggetto passato come argomento. Ad esempio, se scrivessimo:


  
       1: void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)


       2: {


       3:     UpdateTimeLabel(new Random().Next());


       4: }





Oltre l’ora, verrebbe visualizzato un numero (intero) generato dalla classe Random.

Citando MSDN, la classe Dispatcher rappresenta una coda con priorità dei work item associati ad uno specifico thread. In particolare, in WPF, un oggetto Dispatcher può essere acceduto solo dal relativo  Dispatcher: ad esempio un thread di Background non può essere utilizzato per aggiornare il contenuto di un Button (o una Label come nel caso specifico del post), perché quest’ultimo è associato con il Dispatcher dell’interfaccia utente (sollevando l’eccezione precedentemente descritta). Possiamo quindi utilizzre Invoke e BeginInvoke secondo se vogliamo utilizzare rispettivamente un approccio sincrono o asincrono per l’esecuzione dell’operazione da noi richiesta. Quando aggiungiamo le operazioni ad una coda è importante specificare con quale priorità vogliamo aggiungerla rispetto alle altre operazioni presenti: per far questo utilizziamo l’enum System.Windows.Threading.DispatcherPriority. I valori dell’enum variano da 0 a 10 (in verità da –1 a 10), in ordine di priorità crescente:


  0, l’operazione non è processata (Inactive) 

  1, l’operazione è processata quando il sistema è idle (SystemIdle) 

  2, l’operazione è processata quanto l’applicazione è idle (ApplicationIdle) 

  3, l’operazione è processata dopo che le operazioni di background sono state completate (ContextIdle) 

  4, le operazioni sono processate dopo che tutte le operazioni non-idle sono state completate (Background) 

  5, le operazioni sono processate con la stessa priorità dell’input (Input) 

  6, le operazioni sono processate quando il layout ed il render hanno terminato, ma prima delle operazioni Input 

  7, le operazioni sono processate con la stessa priorità del Render (Render) 

  8, le operazioni sono processate con la stessa priorità del Data Binding (DataBinding) 

  9, le operazioni sono processate a priorità normale (tipica dell’applicazione) (Normal) 

  10, le operazioni sono processare prima delle operazioni asincrone, rappresenta la priorità più alta (Send) 
&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/99120.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/08/28/wpf-system.windows.threading.dispatcher-e-begininvoke.aspx</guid>
            <pubDate>Sat, 28 Aug 2010 20:12:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/08/28/wpf-system.windows.threading.dispatcher-e-begininvoke.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/99120.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/99120.aspx</trackback:ping>
        </item>
        <item>
            <title>WPF: PriorityBinding</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/04/07/wpf-prioritybinding.aspx</link>
            <description>   Può capitare di dover mostrare un risultato non  immediatamente disponibile per la lettura, perché, ad esempio, il suo valore dipende da altri parametri e deve quindi essere calcolato. A tal fine, vogliamo mostrare un messaggio all’utente per indicare che l’applicazione è in esecuzione e che non è in una fase di stallo,  qualcosa di simile quando utilizziamo un UpdateProgress dell’Ajax Control Toolkit (o corrispondenti soluzioni custom) nelle nostre pagine Web. In WPF, possiamo utilizzare il PriorityBiding per fare qualcosa di simile (ma non solo). Il PriorityBinding permette di associare ad una proprietà di un elemento, un elenco di associazioni (invece di una singola associazione dati come nel “normale” Binding). Tra le associazioni, viene considerata la prima che restituisce correttamente un valore: ovvero se il Path del Binding viene risolto correttamente, se il valore restituito da un Converter è valido (sempre che ne venga utilizzato uno) o se il valore è valido per la proprietà di destinazione. Supponiamo di avere una classe Data come la seguente:          1:      public class Data : INotifyPropertyChanged

     2:      {

     3:          private double _salary = 0;

     4:          private string _employeeName = "";

     5:          public event PropertyChangedEventHandler PropertyChanged;

     6:   

     7:          public string EmployeeName

     8:          {

     9:              get { return _employeeName; }

    10:              set

    11:              {

    12:                  _employeeName = value;

    13:                  OnPropertyChanged("EmployeeName");

    14:              }

    15:          }

    16:   

    17:          public void Calculate() { OnPropertyChanged("Salary"); }

    18:   

    19:          public double Salary

    20:          {

    21:              get

    22:              {

    23:                  _salary = 0;

    24:                  if (!string.IsNullOrEmpty(_employeeName))

    25:                  {

    26:                      //Supponiamo un'operazione che impieghi un certo tempo...

    27:                      System.Threading.Thread.Sleep(3000);

    28:   

    29:                      switch (_employeeName.ToLower())

    30:                      {

    31:                          case "pietro":

    32:                              _salary = 1000;

    33:                              break;

    34:                          case "giovanni":

    35:                              _salary = 2000;

    36:                              break;

    37:                          default:

    38:                              _salary = 0;

    39:                              break;

    40:                      }

    41:                  }

    42:                  return _salary;

    43:              }

    44:          }

    45:   

    46:          protected void OnPropertyChanged(string prop)

    47:          {

    48:              if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(prop));

    49:          }

    50:   

    51:          public string WaitMessage1 { get { return "Loading..."; } }

    52:   

    53:          public string WaitMessage2

    54:          {

    55:              get

    56:              {

    57:                  System.Threading.Thread.Sleep(2000);

    58:                  return "Loading...please wait";

    59:              }

    60:          }



Il cui funzionamento è abbastanza semplice: secondo del nome dell’impiegato selezionato tramite la proprietà EmployeeName quando viene richiamato Calculate() viene eseguita una notifica di cambiamento tramite OnPropertyChanged(“…”) della proprietà Salary (Ci servirà nel codice XAML). Particolare attenzione  deve essere posta alle proprietà WaitMessage1 e WaitMessage2, utilizzate nella UI per visualizzare due messaggi di attesa per l’utente: il primo subito, il secondo da visualizzare nel caso in cui l’operazione “di calcolo” si prolunghi oltre un certo tempo.

Per testare il funzionamento della classe utilizziamo una Windows WPF molto semplice:

 

Il codice XAML associato alla controllo label in cui viene visualizzato il salario dell’impiegato (calcolato), e a cui è associato il PriorityBinding è il seguente:


  
    
         1:  &amp;lt;Label.Content&amp;gt;

         2:     &amp;lt;PriorityBinding&amp;gt;

         3:        &amp;lt;Binding Path="Salary" Source="{StaticResource es}" IsAsync="True"  /&amp;gt;

         4:        &amp;lt;Binding Path="WaitMessage2" IsAsync="True"  Source="{StaticResource es}"/&amp;gt;

         5:        &amp;lt;Binding Path="WaitMessage1" IsAsync="False" Source="{StaticResource es}"/&amp;gt;

         6:     &amp;lt;/PriorityBinding&amp;gt;

         7:  &amp;lt;/Label.Content&amp;gt;
    
    
  





Associamo i Binding nell’ordine in cui vogliamo vengano eseguiti: Salary, ritorna il valore dello stipendio secondo dell’impiegato specificato nel campo TextBox, WaitMessage2 è il messaggio che dovrebbe essere visualizzato se l’operazione di calcolo necessità più di due secondi, WaitMessage1 è il primo messaggio da visualizzare non appena viene fatto click sul bottone Calcola Stipendio. Affinché tutto funzioni è necessario che la proprietà IsAsync del Binding sulle proprietà Salary e WaitMessage2 sia impostato su True altrimenti l’operazione attenderà il completamento dell’associazione prima di eseguire il prossimo (eventuale) Binding (perdendo i vantaggi del PriorityBinding).

La risorsa statica es, è dichiarata a livello di Windows in questo modo: 

&amp;lt;Window.Resources&amp;gt;
   &amp;lt;local:Data x:Key="es"/&amp;gt;
&amp;lt;/Window.Resources&amp;gt;

Technorati Tag: WPF,PriorityBinding
&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/98323.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/04/07/wpf-prioritybinding.aspx</guid>
            <pubDate>Wed, 07 Apr 2010 12:07:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2010/04/07/wpf-prioritybinding.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/98323.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/98323.aspx</trackback:ping>
        </item>
        <item>
            <title>WPF: Control Template</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/11/06/wpf-control-template.aspx</link>
            <description>In WPF, l'aspetto grafico di un controllo può essere cambiato utilizzando un Control Template, ovvero un'insieme di risorse, triggers ed elementi che descrivono il comportamento grafico del controllo. I Control Template sono utili ogni qual volta si vuole modificare l'aspetto grafico di un controllo, ma allo stesso tempo conservare le funzionalità del controllo stesso. Senza WPF, magari saremmo costretti a costruire un nuovo controllo replicando funzionalità e riscrivendo del codice funzionante. Supponiamo ad esempio di voler cambiare l'aspetto del solito controllo Button, rendendolo fashion e trendy. Quello che vogliamo ottenere è qualcosa del tipo:       Nella seconda figura, è mostrato il comportamento del controllo Button quando il cursorse del mouse è posizionato sopra.   Uno dei metodi più semplici per cambiare il Template del controllo è quello di scrivere il codice XAML di descrizione direttamente all'interno del tag Button.Template,  ad esempio:  1 &amp;lt;Button Height="16" Margin="51,0,42,12" Name="button2" VerticalAlignment="Bottom" Content="Bottone"&amp;gt;
2     &amp;lt;Button.Template   &amp;gt;
3         &amp;lt;ControlTemplate&amp;gt;
4             &amp;lt;Grid&amp;gt;
5                 &amp;lt;!-- codice XAML --&amp;gt;
6             &amp;lt;/Grid&amp;gt;                    
7         &amp;lt;/ControlTemplate&amp;gt;
8     &amp;lt;/Button.Template &amp;gt;
9 &amp;lt;/Button&amp;gt;

Volendo cambiare l'aspetto di tutti i controlli Button presenti in una Form WPF, andiamo ad utilizzare un oggetto Style, aggiungendolo  alla collezione delle risorse della Window WPF su cui stiamo lavorando:


  1 &amp;lt;Window.Resources&amp;gt;
2    &amp;lt;Style TargetType="Button"&amp;gt;
3       &amp;lt;Setter Property="Template"&amp;gt;
4       ...
5       &amp;lt;/Setter&amp;gt;
6    &amp;lt;/Style&amp;gt;
7 &amp;lt;/Window.Resources&amp;gt;
8 


Utilizzando una classe Setter, andiamo ad impostare la proprietà Template del generico controllo Button, aggiungendo in cascata, una Grid, un Border ed un Rectangle. 


   1 &amp;lt;Grid&amp;gt;
 2     &amp;lt;Border BorderBrush="Aquamarine"   BorderThickness="1" Name="borderButton"&amp;gt;
 3         &amp;lt;Rectangle  Name="sfondo"&amp;gt;
 4             &amp;lt;Rectangle.Fill&amp;gt;
 5                 &amp;lt;LinearGradientBrush StartPoint="0,0" EndPoint="1,0" Opacity="1"&amp;gt;
 6                     ...
 7                 &amp;lt;/LinearGradientBrush&amp;gt;
 8             &amp;lt;/Rectangle.Fill&amp;gt;
 9         &amp;lt;/Rectangle&amp;gt;
10     &amp;lt;/Border&amp;gt;
11     &amp;lt;ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"  /&amp;gt;
12 &amp;lt;/Grid&amp;gt;


E' importante spendere qualche parola su ContentPresenter. ContentPresenter serve a visualizzare il contenuto (in questo caso la semplice stringa "Button") di un ContentControl come ad esempio Frame, ToolTip, Label, GroupItem,ButtonBase etc...Se non utilizzassimo ContentPresenter durante la definizione del Template del nostro Button, in fase di Rendering, non verrebbe visualizzata nessuna dicitura sul nostro Button, semplicemente perchè non abbiamo detto a WPF dove vogliamo visualizzarla. Possiamo vedere il ContentPresenter come un segnaposto per indicare dove visualizzare il Content. Utilizzando una Grid, come in questo caso, ci assicuriamo che i vari oggetti siano tutti visualizzati in un'unica cella della griglia. Impostando HorizontalAlignment e VerticalAlignment su Center, ci assicuriamo di visualizzare il contenuto del controllo al centro.

Per cambiare il Background del Button quando il cursorse del mouse viene posizionato sopra la superficie del controllo, come per Style, anche per ControlTemplate abbiamo a disposizione  i Triggers da sfruttare:


   1 &amp;lt;ControlTemplate.Triggers&amp;gt;
 2     &amp;lt;Trigger  Property="IsMouseOver" Value="true"&amp;gt;
 3         &amp;lt;Setter TargetName="sfondo" Property="Fill" &amp;gt;
 4             &amp;lt;Setter.Value&amp;gt;
 5                 &amp;lt;LinearGradientBrush StartPoint="0,0" EndPoint="1,0" Opacity="0.8"&amp;gt;
 6                 ...
 7                 &amp;lt;/LinearGradientBrush&amp;gt;
 8             &amp;lt;/Setter.Value&amp;gt;
 9         &amp;lt;/Setter&amp;gt;
10     &amp;lt;/Trigger&amp;gt;
11 &amp;lt;/ControlTemplate.Triggers&amp;gt;


Molto semplicemente, quando ci troviamo con il cursore del mouse sul Button, il Trigger  cambia "il valore" (in verità l'oggetto LinearGradientBrush) della proprietà Fill dell'oggetto che ha la proprietà Name impostata su Sfondo. Nel procedente codice XAML, questo è Rectangle, posto all'interno dell'oggetto Border , di nome borderButton.

Fino  a questo punto, abbiamo raggiunto il nostro primo obiettivo.  Supponiamo ora di voler aggiungere un'ulteriore funzione grafica al nostro controllo. Supponiamo di voler aggiungere un effetto luce diffusa (Glow), quando ci troviamo ad indicare il button con il mouse (sfruttando in pratica lo stesso Trigger per IsMouseOver). A questo punto, niente di più facile, dato che l'oggetto Border espone una proprietà BitmapEffect. Scrivendo del codice XAML simile a questo:


  1 &amp;lt;Setter TargetName="borderButton" Property="BitmapEffect"&amp;gt;
2     &amp;lt;Setter.Value &amp;gt;
3         &amp;lt;OuterGlowBitmapEffect GlowColor="Yellow" GlowSize="10" /&amp;gt;
4     &amp;lt;/Setter.Value&amp;gt;
5 &amp;lt;/Setter&amp;gt;


Otterremo l'effetto desiderato:

 

Personalmente penso che le potenzialità che il WPF offre per personalizzare l'aspetto dei controll sia veramente impressionante. Utilizzando Expression Blend si possono sviluppare delle applicazioni davvero accattivanti (soprattutto se si ha un pò di dimestichezza con la grafica :-))

Codice XAML completo della Window WPF

Technorati Tag: WPF,ControlTemplate&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/94536.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/11/06/wpf-control-template.aspx</guid>
            <pubDate>Thu, 06 Nov 2008 14:05:03 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/11/06/wpf-control-template.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/94536.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/94536.aspx</trackback:ping>
        </item>
        <item>
            <title>WPF: Style, Trigger e Multitrigger</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/09/17/94063.aspx</link>
            <description>Supponiamo di avere una Window WPF, contentente un singolo controllo TextBox. Vogliamo, senza utilizzare il code-behind, che a secondo del contenuto del TextBox, questo cambi il colore di background. Nello specifico, se nel TextBox c'è scritto "SI", il Background deve essere Verde, se c'è scritto "NO" rosso. Piccola prefazione: uno Style, è generalmente un'insieme di proprietà che applicato ad un'insieme di contenuti quali testi o immagini, ne determina l'aspetto grafico. Parlando di Style, in WPF, questo non è solo un semplice insieme di proprietà, dato che, include comportamenti specifici come la possibilità di cambiare l'aspetto di un controllo basandosi sugli eventi dell'utente, utilizzando gli event trigger. Per lo scopo del post, utilizzeremo invece dei Property Triggers, ovvero dei Trigger, che "osservano" una particolare Dependency Property per verificare se assume un certo valore. Nel caso in cui, il valore "osservato" sia quello atteso, si può impostare una qualche proprietà del  controllo Target, ad esempio il Background o il testo del ToolTip.   Aggiungiamo il codice del nostro Style personalizzato alla collezione delle risorse presenti nella Window:     1 &amp;lt;Style x:Key="CustomTextBoxStyle" TargetType="{x:Type TextBox}"&amp;gt;
 2     &amp;lt;Style.Triggers&amp;gt;                
 3         &amp;lt;Trigger Property="Text" Value="SI"&amp;gt;
 4             &amp;lt;Setter Property="Background" Value="Green" /&amp;gt;
 5             &amp;lt;Setter Property="ToolTip" Value="Il valore SI Ã¨ corretto"/&amp;gt;
 6         &amp;lt;/Trigger&amp;gt;
 7         &amp;lt;Trigger Property="Text" Value="NO"&amp;gt;
 8             &amp;lt;Setter Property="Background" Value="Red" /&amp;gt;
 9         &amp;lt;/Trigger&amp;gt;
10     &amp;lt;/Style.Triggers&amp;gt;
11 &amp;lt;/Style&amp;gt;



Il codice su esposto è relativamente semplice: in entrambi i trigger andiamo a monitorare la proprietà Text del controllo TextBox. Se questo assume il valore "SI" (case-sensitive), impostiamo  la proprietà Background del controllo su Green ed il testo da visualizzare con il ToolTip. Se invece Text è uguale a "NO" allora impostiamo il colore di Background su Red.

Il codice XAML della Window è il seguente:

1    [...]
2     &amp;lt;Grid&amp;gt;
3         &amp;lt;Grid.RowDefinitions&amp;gt;
4             &amp;lt;RowDefinition Height="117*" /&amp;gt;
5             &amp;lt;RowDefinition Height="145*" /&amp;gt;            
6         &amp;lt;/Grid.RowDefinitions&amp;gt;
7         &amp;lt;TextBox  Margin="66,48,77,30" Name="textBox1"  Style="{StaticResource CustomTextBoxStyle}" /&amp;gt;
8     &amp;lt;/Grid&amp;gt;
9 &amp;lt;/Window&amp;gt;

Supponiamo invece, di voler cambiare il valore del Background sul Giallo, quando si verificano più condizioni contemporaneamente, ad esempio quando il controllo TextBox ha il Focus e non contiene Testo. Per questo scopo utilizziamo invece un MultiTrigger:

1 &amp;lt;MultiTrigger&amp;gt;
2     &amp;lt;MultiTrigger.Conditions&amp;gt;
3         &amp;lt;Condition Property="Text" Value=""/&amp;gt;
4         &amp;lt;Condition Property="IsFocused" Value="True"/&amp;gt;
5     &amp;lt;/MultiTrigger.Conditions&amp;gt;
6     &amp;lt;Setter Property="Background" Value="Yellow"/&amp;gt;
7 &amp;lt;/MultiTrigger&amp;gt;

Codice XAML completo della Window WPF.

Technorati Tag: WPF,Style,Property Trigger,MultiTrigger&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/94063.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/09/17/94063.aspx</guid>
            <pubDate>Wed, 17 Sep 2008 12:15:21 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/09/17/94063.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/94063.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/94063.aspx</trackback:ping>
        </item>
        <item>
            <title>WPF: XmlDataProvider</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/08/31/93879.aspx</link>
            <description>Supponiamo di avere un file XML molto semplice, del tipo:       1 &amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;
 2 &amp;lt;books&amp;gt;
 3   &amp;lt;book id="1"&amp;gt;
 4     &amp;lt;title&amp;gt;Introducing To Microsoft LINQ&amp;lt;/title&amp;gt;
 5     &amp;lt;isbn&amp;gt;43554354523&amp;lt;/isbn&amp;gt;
 6   &amp;lt;/book&amp;gt;
 7   &amp;lt;book id="2"&amp;gt;
 8     &amp;lt;title&amp;gt;Pro WPF Windows Presentation Foundation in dot NET 3.0&amp;lt;/title&amp;gt;
 9     &amp;lt;isbn&amp;gt;232132142&amp;lt;/isbn&amp;gt;
10   &amp;lt;/book&amp;gt;
11   &amp;lt;book id="3"&amp;gt;
12     &amp;lt;title&amp;gt;Foundations of F#&amp;lt;/title&amp;gt;
13     &amp;lt;isbn&amp;gt;772337271&amp;lt;/isbn&amp;gt;
14   &amp;lt;/book&amp;gt;
15   &amp;lt;book id="4"&amp;gt;
16     &amp;lt;title&amp;gt;Algorithms In Bioinformatics&amp;lt;/title&amp;gt;
17     &amp;lt;isbn&amp;gt;12345678&amp;lt;/isbn&amp;gt;
18   &amp;lt;/book&amp;gt;
19 &amp;lt;/books&amp;gt;



Vogliamo mostrare il contenuto del documento XML in un controllo WPF ListView o ListBox. Per effettuare il binding con i dati XML, WPF mette a disposizione degli sviluppatori la classe XmlDataProvider, la quale permette d'impostare in modo  rapido e semplice  l'uso di elementi XML come binding source per i controlli delle nostre applicazioni. Trattando nell'esempio un'applicazione WPF Windows Form, nel dizionario delle risorse della Window, aggiungiamo un'istanza della classe XmlDataProvider, specificando la chiave (key) d'identificazione all'interno della collezione, il Source, ovvero il percorso del file XML contenente i dati e l'espressione XPath da utilizzare per il recupero dei nodi interessati.





1   &amp;lt;Window.Resources&amp;gt;
2         &amp;lt;XmlDataProvider x:Key="BooksProvider"  Source="Data.xml" XPath="./books/book" /&amp;gt;
3     &amp;lt;/Window.Resources&amp;gt;



Per visualizzare i dati in un ListView, nel codice XAML possiamo scrivere:

 1 &amp;lt;ListView Margin="12,45,12,117" Name="listView1" DataContext="{StaticResource BooksProvider}" ItemsSource="{Binding}"
 2    SelectionChanged="listView1_SelectionChanged" MouseDoubleClick="listView1_MouseDoubleClick"&amp;gt;
 3     &amp;lt;ListView.View &amp;gt;
 4         &amp;lt;GridView&amp;gt;
 5             &amp;lt;GridViewColumn Header="ID" Width="50" DisplayMemberBinding="{Binding XPath=@id}" &amp;gt;&amp;lt;/GridViewColumn&amp;gt;
 6             &amp;lt;GridViewColumn Header="Title" Width="250" DisplayMemberBinding="{Binding XPath=title}"&amp;gt;&amp;lt;/GridViewColumn&amp;gt;
 7             &amp;lt;GridViewColumn Header="ISBN" Width="150" DisplayMemberBinding="{Binding XPath=isbn}"&amp;gt;
 8             &amp;lt;/GridViewColumn&amp;gt;
 9         &amp;lt;/GridView&amp;gt;
10     &amp;lt;/ListView.View&amp;gt;
11 &amp;lt;/ListView&amp;gt;

Notiamo come la proprietà DataContext del ListView sia impostata sull'istanza del XmlDataProvider dichiarata nelle risorse,e per il binding, invece di utilizzare la proprietà Path, andiamo ad utilizzare la proprietà XPath, anteponendo in caso di attributi, il carattere @.

Se invece di utilizzare un controllo ListView, volessimo presentare i dati utilizzando un ListBox, il codice XAML da scrivere potrebbe essere simile al seguente:

 1         &amp;lt;ListBox Margin="12,0,12,25" Name="listBox1" DataContext="{StaticResource BooksProvider}" 
 2          ItemsSource="{Binding}" Height="76" VerticalAlignment="Bottom"&amp;gt;
 3             &amp;lt;ListBox.ItemTemplate&amp;gt;
 4                 &amp;lt;DataTemplate &amp;gt;
 5                     &amp;lt;StackPanel Orientation="Horizontal"&amp;gt;
 6                         &amp;lt;TextBlock Text="{Binding XPath=@id}"&amp;gt; -
 7                     &amp;lt;TextBlock Text="{Binding XPath=title}"&amp;gt;&amp;lt;/TextBlock&amp;gt;
 8                 &amp;lt;/TextBlock&amp;gt;
 9                     &amp;lt;/StackPanel&amp;gt;
10                 &amp;lt;/DataTemplate&amp;gt;
11             &amp;lt;/ListBox.ItemTemplate&amp;gt;
12         &amp;lt;/ListBox&amp;gt;

Nel caso in cui volessimo associare del codice sull'evento Double-Click del ListView per ottenere informazioni sul nodo correttamente selezionato, come suggerito da Giuseppe Lippolis, possiamo scrivere nel codice XAML:


  1 &amp;lt;ListView Margin="12,45,12,117" Name="listView1" DataContext="{StaticResource BooksProvider}" ItemsSource="{Binding}" ListViewItem.MouseDoubleClick="Item_MouseDoubleClick"&amp;gt;
2 ....
3 &amp;lt;/ListView &amp;gt;


Nel code-behind:

1 private void Item_MouseDoubleClick(object sender, MouseButtonEventArgs e)
2 {
3 ListView lsv = (ListView)e.Source;
4 XmlElement book = (XmlElement)lsv.SelectedItem;
5 string message = String.Format("Double Click: {0} - {1} - {2}",
6 book.GetAttribute("id"), book["title"].InnerText,
7 book["isbn"].InnerText);
8 MessageBox.Show(message);
9 } 



Technorati Tag: WPF,XmlDataProvider&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/93879.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/08/31/93879.aspx</guid>
            <pubDate>Sun, 31 Aug 2008 12:43:11 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/08/31/93879.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/93879.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/93879.aspx</trackback:ping>
        </item>
        <item>
            <title>WPF + LINQ + CollectionViewSource = Rss Reader</title>
            <link>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/08/27/93837.aspx</link>
            <description>Questo post è nato con uno scopo diverso da quello dell'implementazione di un Rss Reader, dato che, è sufficiente cercare su internet per trovarne di belli e pronti, ma per enfatizzare alcune proprietà molto interessanti dei controlli WPF, come  la possibilità di eseguire il raggruppamento dei dati nei controlli ListBox o ListView. Piccola parentesi: per chi volesse conoscere in dettaglio le specifiche RSS 2.0 le può trovare qui. Iniziamo sviluppando un set di classi come riassunto dal seguente Class Diagram:     Nel resto dell'esempio saranno prese in considerazione solo istanze di tipo Rss e RssItem. Generalmente, senza considerare gli elementi opzionali, la struttura xml di un documento RSS è il seguente:   1 &amp;lt;rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" ...
 2   &amp;lt;channel&amp;gt;
 3     &amp;lt;title&amp;gt;Il blog di Pietro Libro&amp;lt;/title&amp;gt;
 4     &amp;lt;link&amp;gt;http://blogs.ugidotnet.org/PietroLibroBlog/Default.aspx&amp;lt;/link&amp;gt;
 5     &amp;lt;description /&amp;gt;
 6     &amp;lt;language&amp;gt;it-IT&amp;lt;/language&amp;gt;
 7     &amp;lt;copyright&amp;gt;Pietro Libro&amp;lt;/copyright&amp;gt;
 8     &amp;lt;managingEditor&amp;gt;&amp;lt;/managingEditor&amp;gt;
 9     &amp;lt;generator&amp;gt;Subtext Version 1.9.5.176&amp;lt;/generator&amp;gt;
10     ...
11      &amp;lt;item&amp;gt;
12       &amp;lt;title&amp;gt;Adeona, il software &amp;amp;amp;quot;salva&amp;amp;amp;quot; notebook&amp;lt;/title&amp;gt;
13       &amp;lt;category&amp;gt;OT&amp;lt;/category&amp;gt;
14       &amp;lt;category&amp;gt;Varie ed eventuali&amp;lt;/category&amp;gt;
15       &amp;lt;link&amp;gt;http://blogs.ugidotnet.org/PietroLibroBlog/archive/2008/08/26/93820.aspx&amp;lt;/link&amp;gt;
16       &amp;lt;description&amp;gt;...&amp;lt;/description&amp;gt;
17       ....
18     &amp;lt;/item&amp;gt;

Per leggere un tale documento, utilizziamo un'istanza della classe XDocument, presente nel namespace System.Xml.Linq, contenente metodi che permettono la navigazione dei nodi del documento in maniera molto semplice. Se a questo aggiungiamo la potenza di Linq, veramente c'è poco da dire. Prima di tutto creiamo un'istanza  di XDocument e leggiamo il documento passando direttamente l'uri del fornitore del feed RSS:


  1 XDocument xDocument = new XDocument();                
2 xDocument = XDocument.Load(blogUri);


Per recuperare tutti gli elementi item contenuti nel documento, utilizziamo Linq. Per far questo utilizziamo il metodo Descendants passando come parametro la stringa "Item":


  1 _items =
2     (from item in xDocument.Descendants("item")
3      select new RssItem
4      {
5          Title = item.Element("title").Value,
6          Description =item.Element ("description").Value,
7          Category = item.Element("category") == null ? "Nessuna Categoria" : item.Element("category").Value ,
8          Link = item.Element("link") == null ? "": item.Element("link").Value
9      }).ToList&amp;lt;RssItem&amp;gt;();


 _items è una lista tipizzata di oggetti RssItem.

Per presentare i dati recuperati  utilizziamo una Window WPF. Il nostro obiettivo è mostrare i dati raggruppati per categoria, ovvero ottenere qualcosa del tipo:

 

Per raggiungere il goal,  invece di utilizzare la proprietà ItemSource esposta dal controllo ListBox, utilizziamo un'istanza di oggetto di tipo CollectionViewSource la quale altro non è una che un Proxy della classe CollectionView. Quest'ultima permette di eseguire operazioni di raggruppamento, ordinamento, navigazione e ricerca su di una collezione di dati. Definiamo un'istanza di tipo CollectionViewSource nella collezione Window.Resources della nostra Window WPF:


  1 &amp;lt;CollectionViewSource x:Key="cvs"&amp;gt;
2     &amp;lt;CollectionViewSource.GroupDescriptions&amp;gt;
3         &amp;lt;PropertyGroupDescription PropertyName="Category"/&amp;gt;
4     &amp;lt;/CollectionViewSource.GroupDescriptions&amp;gt;
5 &amp;lt;/CollectionViewSource&amp;gt;


Così facendo, aggiungiamo alla collezione GroupDescriptions un oggetto di tipo GroupDescription che descrive il criterio in base al quale i  dati devono essere raggruppati nella vista, nello specifico in base al valore della proprietà Category. 

Tramite codice XAML, andiamo a descrivere come devono essere visualizzati i nostri Gruppi:

 1 &amp;lt;Style x:Key="CustomGroupStyle" TargetType="{x:Type GroupItem}"&amp;gt;
 2     &amp;lt;Setter Property="Margin" Value="0,0,0,5"/&amp;gt;
 3     &amp;lt;Setter Property="Template"&amp;gt;
 4         &amp;lt;Setter.Value&amp;gt;
 5             &amp;lt;ControlTemplate TargetType="{x:Type GroupItem}"&amp;gt;
 6                 &amp;lt;Expander IsExpanded="False" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1"&amp;gt;
 7                     &amp;lt;Expander.Header&amp;gt;
 8                         &amp;lt;DockPanel&amp;gt;
 9                             &amp;lt;TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/&amp;gt;
10                             &amp;lt;TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount, 
11                                     Converter={StaticResource CustomConverter},
12                                     ConverterParameter=' - Numero di post ({0}) '}"/&amp;gt;
13                         &amp;lt;/DockPanel&amp;gt;
14                     &amp;lt;/Expander.Header&amp;gt;
15                     &amp;lt;Expander.Content&amp;gt;
16                         &amp;lt;ItemsPresenter /&amp;gt;
17                     &amp;lt;/Expander.Content&amp;gt;
18                 &amp;lt;/Expander&amp;gt;
19             &amp;lt;/ControlTemplate&amp;gt;
20         &amp;lt;/Setter.Value&amp;gt;
21     &amp;lt;/Setter&amp;gt;
22 &amp;lt;/Style&amp;gt;

E gli elementi in essi contenuti:

1 &amp;lt;DataTemplate x:Key="ItemTemplate"&amp;gt;
2     &amp;lt;StackPanel&amp;gt;
3         &amp;lt;TextBlock Text="{Binding Path=Title}"&amp;gt;&amp;lt;/TextBlock&amp;gt;
4         &amp;lt;TextBlock&amp;gt;
5     &amp;lt;Hyperlink NavigateUri="{Binding Path=Link}" Click="Hyperlink_Click"&amp;gt;Visualizza&amp;lt;/Hyperlink&amp;gt;
6         &amp;lt;/TextBlock&amp;gt;
7     &amp;lt;/StackPanel&amp;gt;
8 &amp;lt;/DataTemplate&amp;gt;

Non resta che definire il controllo ListBox:


  1 &amp;lt;ListBox Name="lstBox" ItemTemplate="{StaticResource ItemTemplate}" Margin="12,41,12,12"&amp;gt;
2     &amp;lt;ListBox.GroupStyle&amp;gt;
3         &amp;lt;GroupStyle ContainerStyle="{StaticResource CustomGroupStyle}"/&amp;gt;
4     &amp;lt;/ListBox.GroupStyle&amp;gt;
5 &amp;lt;/ListBox&amp;gt;


Utilizzando un controllo Button per recuperare il contenuto del documento RSS, scriviamo il seguente codice C#:


  1 LinqRss.Rss rss = new LinqRss.Rss();
2 if (rss.Load("http://blogs.ugidotnet.org/PietroLibroBlog/Rss.aspx"))
3 {
4     CollectionViewSource collectionViewSource = this.FindResource("cvs") as CollectionViewSource ;
5     collectionViewSource.Source = rss.Items;
6     lstBox.ItemsSource = collectionViewSource.View;
7 }


In sintesi, recuperata l'istanza di CollectionViewSource contenuta nelle risorse della nostra Window WPF,  impostiamo la proprietà Source dell'oggetto CollectionViewSource utilizzando la collezione Items esposta dall'istanza Rss. Non resta che impostare la proprietà ItemSource del controllo ListBox con la proprietà View esposta da CollectionViewSource. Osservazione: i GroupItem sono costituiti da controlli Expander. Nell'ItemTemplate è presente anche un HyperLink che permette la navigazione verso il post d'interesse.

Codice C# e XAML

Technorati Tag: CollectionViewSource&lt;img src="http://blogs.ugidotnet.org/pietrolibroblog/aggbug/93837.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pietro Libro</dc:creator>
            <guid>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/08/27/93837.aspx</guid>
            <pubDate>Wed, 27 Aug 2008 14:30:58 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/pietrolibroblog/archive/2008/08/27/93837.aspx#feedback</comments>
            <slash:comments>9</slash:comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/pietrolibroblog/comments/commentRss/93837.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/pietrolibroblog/services/trackbacks/93837.aspx</trackback:ping>
        </item>
    </channel>
</rss>