posts - 84, comments - 124, trackbacks - 1

mercoledì 27 agosto 2008

WPF + LINQ + CollectionViewSource = Rss Reader

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:

image

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 <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" ... 2 <channel> 3 <title>Il blog di Pietro Libro</title> 4 <link>http://blogs.ugidotnet.org/PietroLibroBlog/Default.aspx</link> 5 <description /> 6 <language>it-IT</language> 7 <copyright>Pietro Libro</copyright> 8 <managingEditor></managingEditor> 9 <generator>Subtext Version 1.9.5.176</generator> 10 ... 11 <item> 12 <title>Adeona, il software &amp;quot;salva&amp;quot; notebook</title> 13 <category>OT</category> 14 <category>Varie ed eventuali</category> 15 <link>http://blogs.ugidotnet.org/PietroLibroBlog/archive/2008/08/26/93820.aspx</link> 16 <description>...</description> 17 .... 18 </item>

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<RssItem>();

 _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:

image

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 <CollectionViewSource x:Key="cvs"> 2 <CollectionViewSource.GroupDescriptions> 3 <PropertyGroupDescription PropertyName="Category"/> 4 </CollectionViewSource.GroupDescriptions> 5 </CollectionViewSource>

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 <Style x:Key="CustomGroupStyle" TargetType="{x:Type GroupItem}"> 2 <Setter Property="Margin" Value="0,0,0,5"/> 3 <Setter Property="Template"> 4 <Setter.Value> 5 <ControlTemplate TargetType="{x:Type GroupItem}"> 6 <Expander IsExpanded="False" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1"> 7 <Expander.Header> 8 <DockPanel> 9 <TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/> 10 <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount, 11 Converter={StaticResource CustomConverter}, 12 ConverterParameter=' - Numero di post ({0}) '}"/> 13 </DockPanel> 14 </Expander.Header> 15 <Expander.Content> 16 <ItemsPresenter /> 17 </Expander.Content> 18 </Expander> 19 </ControlTemplate> 20 </Setter.Value> 21 </Setter> 22 </Style>

E gli elementi in essi contenuti:

1 <DataTemplate x:Key="ItemTemplate"> 2 <StackPanel> 3 <TextBlock Text="{Binding Path=Title}"></TextBlock> 4 <TextBlock> 5 <Hyperlink NavigateUri="{Binding Path=Link}" Click="Hyperlink_Click">Visualizza</Hyperlink> 6 </TextBlock> 7 </StackPanel> 8 </DataTemplate>

Non resta che definire il controllo ListBox:

1 <ListBox Name="lstBox" ItemTemplate="{StaticResource ItemTemplate}" Margin="12,41,12,12"> 2 <ListBox.GroupStyle> 3 <GroupStyle ContainerStyle="{StaticResource CustomGroupStyle}"/> 4 </ListBox.GroupStyle> 5 </ListBox>

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:

posted @ mercoledì 27 agosto 2008 13.30 | Feedback (0) | Filed Under [ LINQ WPF ]

martedì 26 agosto 2008

Adeona, il software "salva" notebook

Se avete paura che qualcuno rubi il vostro notebook, non vi resta che installare questo software. E' in versione beta e ha molti punti deboli. ma l'idea è interessante.

posted @ martedì 26 agosto 2008 7.34 | Feedback (2) | Filed Under [ Varie ed eventuali OT ]

domenica 24 agosto 2008

[OT] Ferrari

E' tanto che non postavo a riguardo di un gran premio di Formula 1. Problemi di affidabilità per la Ferrari (rottura del motore per Raikkonen), e in questa fase del mondiale non è l'ideale.

Classifica finale del GP d'Europa:

1° Massa (Ferrari),  2° Hamilton (McLaren), 3° Kubica (Bmw Sauber)

Prossimo appuntamento: GP del Belgio, domenica 7 settembre

Technorati Tag:

posted @ domenica 24 agosto 2008 13.43 | Feedback (7) | Filed Under [ OT ]

ConvertAll<>

Supponiamo di avere una situazione schematizzata dal seguente Class Diagram:

image

Supponiamo di avere un unico metodo Add che aggiunge oggetti di tipo Person ad una lista tipizzata denominata People, come nel caso seguente:

1 2 List<Person> _people = new List<Person>(); 3 4 private void Add(Person p) 5 { 6 _people.Add(p); 7 }

Utilizziamo il seguente codice per popolare la collezione People :

1 Employee e = null; 2 e = new Employee(); 3 e.Age = 21; 4 e.Name = "Peter"; 5 e.Surname = "Book"; 6 e.Office = "Piazzale Kennedy 21"; 7 8 Add(e); 9 10 e = new Employee(); 11 e.Age = 23; 12 e.Name = "Anna"; 13 e.Surname = "Rossi"; 14 e.Office = "Via Ammiraglio Cagni 36"; 15 16 Add(e); 17 18 e = new Employee(); 19 e.Age = 35; 20 e.Name = "Mario"; 21 e.Surname = "Rossi"; 22 e.Office = "Via Duca degli Abruzzi"; 23 24 Add(e);

Se volessimo convertire la nostra lista tipizzata di oggetti Person in una lista tipizzata di oggetti Employee:

1 List<Employee> employees = new List<Employee>();

possiamo seguire due strade, la prima è utilizzare un ciclo foreach:

1 foreach (Person p in _people) 2 employees.Add((Employee)p);

La seconda  è quella di utilizzare il metodo generico ConvertAll<> che esegue la conversione di un Array di un tipo, Person in questo caso, in un Array di un'altro tipo,Employee, utilizzando un Converter, ovvero un metodo che esegue la conversione di un tipo in un altro. In soldoni (righe di codice :-) ):

Il Converter

1 private Employee PersonEmployeeeConverter(Person p) 2 { 3 if (p.GetType() == typeof(Employee)) 4 return (Employee)p; 5 else 6 return null; 7 }

Il codice di conversione:

1 employees = _people.ConvertAll<Employee>(new Converter<Person, Employee>(PersonEmployeeeConverter));

Osservazioni: ConvertAll<> e il concetto di Converter sono stati introdotti con il .Net Framework 2.0

Technorati Tag:

posted @ domenica 24 agosto 2008 13.32 | Feedback (2) | Filed Under [ C# ]

venerdì 22 agosto 2008

.NET Framework Tools

Il .Net Framework mette a disposizione degli sviluppatori un numero veramente grande di tool. Questo è un piccolo promemoria per alcuni di questi.

Storeadm.exe- Isolated Storage Tool

Con il .Net Framework 2.0 è stato introdotto il concetto di Isolated Storage. Un utile strumento per leggere o rimuovere tutti gli stores esistenti per un determinato utente è Storeadm.exe. Digitando Storeadm.exe su linea di comando senza specificare nulla, sono visualizzate le opzioni e la sintassi del tool (stesso risutlato può essere ottenuto digitando storeadm /? o storeadm /help).  Se volessimo ad esempio visualizzare tutti gli stores  sarebbe  sufficiente scrivere:

Storeadm.exe /list

Maggiori dettagli su Storeadm

 

Xsd.exe -XML Schema Definition Tool

Penso che questo tool sia stato utilizzato almeno una volta nella propria vita (professionale) da uno sviluppatore .Net: è  fondamentale per la generazione di schemi XML, di  classi a partire da file XDR, XML e per la generazione di XSD  dai tipi contenuti in un assembly. Le operazioni che possono essere eseguite  sono:

  • XDR->XSD: Generazione di uno schema XML (XSD) a partire da un  XML-Data Reduced schema (XDR)
  • XML->XSD: Generazione di uno schema XML (XSD) a partire da un file XML
  • XSD->DataSet: Generazione di un dataset a partire da uno file XSD.
  • XSD->Classi: Generazione di classi nei linguaggi VB.NET, C# (default), JS o VJS (J#), a partire da un file XSD. Le classi possono essere utilizzate da un Systemt.XML.Serialization.XMLSerializer per leggere e scrivere codice XML secondo lo schema dato
  • Classi->XSD: Generazione a partire da uno o più tipi in un assembly del corrispondente  XML schema.

Maggiori dettagli su Xsd.exe

 

Sn.Exe  - Strong Name Tool

Anche in questo caso penso che uno sviluppatore .Net abbia utilizzato questo tool almeno una volta. In particolare, permette di firmare un assembly con uno strong name (nome sicuro), assicurando che il nome assegnato all'assembly sia univoco. In particolare uno strong name  soddisfa i seguenti requisiti:

  • Univocità dei nomi perchè basati su coppie di chiavi univoche. Nessun'altro può generare lo stesso nome per un assembly diverso.
  • Garanzia sul fatto che nessun'altro può produrre una versione successiva del proprio assembly. Questo garantisce agli utenti che utilizzano una nuova versionie dell'assembly che quest'ultima è stata prodotta dalla stessa fonte del precedente
  • Controllo d'integrità garantendo che il contenuto dell'assembly non sia stato modifcato successivamente alla sua creazione.

Questo tool espone numerose opzioni. Vale la pena guardare anche i vari esempi di utilizzo presenti nella documentazione MSDN.

Maggiori dettagli su Sn.exe

 

SignTool.Exe - File Signing Tool

Questo tool permette di aggiungere una firma digitale ad un file, verificare una firma già presente o aggiungere un Time Stamp. Per questo tool è richiesta l'installazione della versione ridistribuibile di CAPICOM 2.0. Anche in questo caso le opzioni offerte sono numerose scondo del tipo di operazione da eseguire.

 Maggiori dettagli su SignTool.exe

 

Caspol.exe - Code Access Security Policy Tool

Questo tool permete all'utente e agli amministratori di modificare le policy di sicurezza a livello machine, user ed enterprise. L'intersezione dell'insieme delle autorizzazioni forniti da questi tre criteri è l'insieme di autorizzazioni che sono fornite ad un assembly. Strutturalmente, ogni criterio ha una gerarchia di gruppi di codice, dove ognuno di questi rappresenta una condizione di appartenenza che determina quale codice appartiene a tale gruppo. A ogni gruppo è associato un insieme di autorizzazioni che specifica a runtime quali autorizzazioni sono concesse al codice che soddisfa la condizione di appartenenza. Queste poche righe non possono assolutamente esaurire il significato e l'utilità di questo importante strumento, ma è necessaria una più attenta lettura. 

Maggiori dettagli su Caspol.exe

 

Permview.exe - Permission View Tool

Questo strumento permette di visualizzare le autorizzazioni minime, facoltative, rifiutate e richeste da un assembly. Questo tool può essere utilizzato anche per visualizzare la protezione dichiarativa di un assembly. Permview.exe è disponibile nelle versioni 1.0 e 1.1 del .Net Framework. Nella versione .Net 2.0 e superiori è presente il tool PermCalc.exe.

Maggiori Dettagli su PermView.exe

Maggiori Dettagli su PermCalc.exe

posted @ venerdì 22 agosto 2008 8.03 | Feedback (0) | Filed Under [ Tools ]

mercoledì 20 agosto 2008

[OT] Olimpiadi...

Se volete seguire in diretta le finali di Pechino 2008: Yalp!

Technorati Tag:

posted @ mercoledì 20 agosto 2008 12.19 | Feedback (0) | Filed Under [ Varie ed eventuali ]

lunedì 18 agosto 2008

The database principal...and cannot be dropped

Promemoria. Questo link presenta una possibile soluzione nel caso in cui, lavorando con SQL Server 2005, si presenti il seguente messaggio d'errore:

The database principal owns a schema in the database, and cannot be dropped. (Microsoft SQL Server, Error: 15138).

posted @ lunedì 18 agosto 2008 13.29 | Feedback (0) | Filed Under [ SQL ]

EventLog

Piccolo post derivato da una risposta su un forum. Un semplice modo per dotare una nostra applicazione di un log degli eventi, è utilizzare la classe EventLog che il .Net Framework mette a disposizione. MSDN suggerisce, nel caso in cui si debba usare il log per poche operazioni di utilizzare i metodi statici della classe EventLog, altrimenti conviene registrare un log degli eventi personalizzato e utilizzare un'istanza della classe EventLog. Quando detto si traduce in poche righe di codice:

1 if (!EventLog.SourceExists("MyEventLog", ".")) 2 EventLog.CreateEventSource("MyEventLog", "CustomApplication"); 3 4 _demoLog = new EventLog("CustomApplication", ".", "MyEventLog");

Per aggiungere una nuova entry al log è sufficiente utilizzare il metodo WriteEntry:

1 _demoLog.WriteEntry("Evento click eseguito", EventLogEntryType.SuccessAudit);

Se ad esempio, volessimo visualizzare le entries del nostro  log in un controllo ListBox è sufficiente eseguire un ciclo for each sulla collezione Entries esposta dall'istanza di EventLog:

1 string entryMessage =""; 2 foreach (EventLogEntry entry in _demoLog.Entries ){ 3 entryMessage = string.Format ("{0} {1} {2}", 4 entry.EntryType.ToString (),entry.MachineName , 5 entry.Message ); 6 7 listBox1.Items.Add (entryMessage ); 8 }

Per cancellare le entries, utilizziamo il metodo Clear() dell'istanza:

1 _demoLog.Clear();

Nel caso in cui invece volessimo eliminare l'Event Log registrato, è sufficiente utilizzare il metodo statico Delete() :

1 EventLog.Delete("MyEventLog")

Importante (1): Per creare ed eliminare un event source ( e realtive entries)  è necessario che l'utente abbia i diritti di amministrazione, altrimenti a run-time verrà sollevata l'opportuna eccezione. Magari il log degli eventi potrebbe essere registrato sulla macchina in fase di installazione dell'applicazione, fase in cui ci si aspetta che l'utente che esegue il task sia in posseso degli opportuni diritti di amministrazione

Importante (2): Per utilizzare il sistema di logging è necessario che il sistema operativo che ospita l'applicazione sia Windows 2000, Windows XP, Windows 2003 Server o superiori (Windows 98 e Windows Millennium Edition non supportano l'Event log)

Oltre che da codice, possiamo visualizzare lo stato del nostro log utilizzando l'apposito Event Viewer richiamabile anche da riga di comando: eventvwr.exe

Per l'esempio indicato, su Windows Vista Ultimate, otteniamo:

image

Per approfondimenti, MSDN

posted @ lunedì 18 agosto 2008 13.02 | Feedback (2) | Filed Under [ C# ]

domenica 17 agosto 2008

WPF 3.5 SP1: Bindable WebBrowser

Se in una nostra WPF Windows Application abbiamo la necessità di utilizzare il nuovo controllo WebBrowser  per visualizzare del codice HTML, possiamo percorrere due strade, secondo delle nostre esigenze:

1) Il codice HTML deve essere visualizzato (ad esempio) sull'evento click di un controllo button: possiamo utilizzare i metodi NavigateToString(string text) o NavigateToStream(System.IO.Stream stream). Ad esempio, se scriviamo:

1 WebBrowserInstance.NavigateToString("<h2>Prova</h2><p>Ciao</p>");

otteniamo:

image

dove il codice XAML è:

1 <WebBrowser ClipToBounds="True" Name="WebBrowserInstance"></WebBrowser>

2) Se il codice HTML da visualizzare è ottenuto dinamicamente, sembra non essere presente (attualmente) un modo diretto per bindare il codice HTML ad una proprietà del controllo WebBrowser (anche se quest'ultimo è un DependencyObject). Per ovviare al problema possiamo creare un nuovo UserControl contenente un controllo WebBrowser. Il codice XAML dell'UserControl è:

1 <UserControl x:Class="MyNamespace.BindableWebBrowser" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Height="300" Width="300" Loaded="UserControl_Loaded"> 5 <Grid Name="contentGrid"> 6 </Grid> 7 </UserControl> 8

Mentre il codice C# è:

1 namespace MyNamespace 2 { 3 public partial class BindableWebBrowser : UserControl 4 { 5 6 private WebBrowser _wb = null; 7 8 public WebBrowser Wb 9 { 10 get { return _wb; } 11 set { _wb = value; } 12 } 13 14 public BindableWebBrowser() 15 { 16 InitializeComponent(); 17 18 _wb = new WebBrowser(); 19 _wb.Margin = new Thickness(0); 20 contentGrid.Children.Add(_wb); 21 } 22 23 public string HtmlCode 24 { 25 get { return (string)GetValue(TextProperty); } 26 set { SetValue(TextProperty, value); } 27 } 28 29 public static readonly DependencyProperty TextProperty = 30 DependencyProperty.Register("HtmlCode", typeof(string), 31 typeof(BindableWebBrowser), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(HtmlDocumentChangedCallBack))); 32 33 public static void HtmlDocumentChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args) 34 { 35 BindableWebBrowser bindableWebBrowser = (BindableWebBrowser)property; 36 bindableWebBrowser.Wb.NavigateToString((string)args.NewValue); 37 } 38 39 private void UserControl_Loaded(object sender, RoutedEventArgs e) 40 { 41 } 42 } 43 }

In questo modo in una Window WPF, nel codice XAML, possiamo scrivere:

1 <local:BindableWebBrowser HtmlCode="&lt;h3&gt;Hello World!!!&lt;/h3&gt;" Margin="24.577,60,12,0" Grid.Column="1" Height="65" VerticalAlignment="Top"></local:BindableWebBrowser >

oppure eseguire un binding del controllo in questo modo:

1 <local:BindableWebBrowser HtmlCode="{Binding Path=Description}" ></local:BindableWebBrowser >

Ottenendo, utilizzando il codice del primo caso, quanto segue:

image

Se invece il nostro scopo è semplicemente visualizzare un sito web, allora è sufficiente scrivere nel codice XAML:

1 <WebBrowser Source="http://blogs.ugidotnet.org/" ClipToBounds="True" Name="wbTest"></WebBrowser>

Senza scrivere un nuovo UserControl.

L'aspetto grafico dell'UserControl può essere notevolmente migliorato (parliamo di WPF :-)), ma spero che il codice possa ritornare utile.

 

Technorati Tag: ,

 

posted @ domenica 17 agosto 2008 16.16 | Feedback (3) | Filed Under [ WPF ]

giovedì 14 agosto 2008

[OT] Il robot col cervello di ratto...

Potrebbe sembrare uno scherzo, ma non lo è...Gordon è un robot che schiva gli ostacoli, ma ha un cervello biologico. Per chi è interessato, qui l'articolo completo.

posted @ giovedì 14 agosto 2008 8.50 | Feedback (3) | Filed Under [ Varie ed eventuali ]

Powered by: