di .NET e di altre amenità

Usability: Ridotta leggibilità per i menù giustificati a destra

Da un recente studio per che i menù giustificati a destra riducano la leggibilità per gli utenti che leggono da sinistra verso destra. Questo avviene perchè l'utente cerca di scandire la lista muovendo l'occhio rapidamente sulla parte iniziale del testo sulla sinistra. Trovando gli elementi allineati a sinistra la scansione sarà più rapida e di conseguenza il sito apparità più leggibile.

Interessanti anche le regole che vengono proposte:

  • Left-justify the menu, so that the user's eyes can move in a straight line and don't have to re-acquire the beginning of each new line.
  • Start each menu item with the one or two most information-carrying words.
  • Avoid using the same few words to start list items, because doing so makes them harder to scan.

Chi fosse interessato potrà trovare una analisi del problema in apparso nell'alertbox di oggi di .

Technorati Tag: ,

Articolo: Migliorare il databinding di Silverlight 2.0 con un controllo CollectionViewSource

Ho finalmente pubblicato la mia ultima fatica. Si tratta di un , accompagnato da un po' di codice rilasciato su codeplex, che spiega come fare a creare un controllo CollectionViewSource per Silverlight 2.0. Il controllo , presente all'interno di Windows Presentation foundation fin dalla prima versione ma assente in Silverlight 2.0, consente di operare facilmente delle trasformazioni a sorgenti dati senza dover intervenire sulla stessa. Esso funziona come un filtro che si interpone tra la datasource e il controllo che consuma i dati e trasforma il contenuto della datasource operando filtri o ordinamenti. Nel caso di WPF esso è anche in grado di supportare il grouping ma dato che in Silverlight non vi sono controlli che possono consumarlo non mi sono preoccupato di implementare anche questa feature.

Il controllo CollectionViewSource che ho creato è stato inserito all'interno di una libreria open source su codeplex ed è liberamente scaricabile sotto licenza CCPL non commerciale al seguente indirizzo:

L'articolo, che ho scritto completamente in inglese per cercare di raggiungere un pubblco più vasto oltre che per misurare la mia capacità di scrivere in questa lingua, si concentra innanzitutto sul funzionamento del Databinding in Silverlight 2.0 e poi introduce il controllo spiegando le scelte operate per renderlo funzionante. Chi fosse interessato può leggerlo qui:

Spero di avere al più presto i vostri feedback oltre che sul contenuto dell'articolo anche sulla qualità del mio - probabilmente stentato - inglese.

Improving Silverlight 2.0 databinding with a CollectionViewSource control

People that have worked with Windows Presentation Foundation probably know the usefulness of the CollectionViewSource control. This control is simple to be configured and enables to easily filter, sort and group data items before binding them to an ItemsControl, without any need to change the underlying datasource. Many times we have to operate filtering on a datasource before displaying it in a listbox, or we need to sort the same data answering to a user request. Both this behaviors may be handled using the CollectionViewSource; filtering will raise an event asking to choice the items to show or to hide and we have simply to select the elements with the correct criteria. On the other side, the control can sort elements on one or more properties ascending or descending. If the target control support grouping we can also specify a grouping criteria and the control will operate seamless for us. But probably the better thing is that we can change sorting, filtering or grouping and the target control will be updated according to the new criteria.

Unfortunately Silverlight 2.0 does not have a CollectionViewSource but while exploring the capabilities of databinding I found that it is possible to build a control similar to the CollectionViewSource that support both filtering and sorting. There aren't controls supporting grouping in Silverlight 2.0 so it is unuseful to implement it in this control. Before getting to know how the control works it is better to understand how databinding works in Silverlight 2.0 so let me begin with a brief introduction to this argument.

Databinding in Silverlight 2.0

Databinding is a feature introduced many years ago so there is no need to explain what it is. Windows Presentation Foundation has taken this ancient tecnique to an high level of simplicity and Silverlight 2.0 implements a databinding capability very close to WPF with some little limitations.

The two main components of databinding are DataContext and the Binding markup extension. DataContext is a property, exposed by all the classes inheriting from FrameworkElement so it is available on all the controls, shapes, panels and other user interface elements. Through DataContext property we may specify a class instance that will be the source of databinding for the target element and for all his children. The DataContext will propagate to children elements until the end of the hierarchy or until another DataContext will be encountered. All the elements of the hierarchy may be binded with a property of the object that is associated to the DataContext. To bind the properties each other we have to use the Binding markup extension. In the simplest form the markup extension require that we specify the property of the datasource to bind to.

Another way to bind to a user interface control is using an ItemsControl. Connecting a datasource to its ItemsSource property will cause the collection to be displayed in the control repeating a DataTemplate for every item in the collection. The DataTemplate will be binded to each element throught the Binding markup extension itself. Here is an example showing both the tecniques applied.

   1: <UserControl x:Class="Elite.Silverlight.Samples.Databinding"
   2:     xmlns="http://schemas.microsoft.com/client/2007" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:d="clr-namespace:Elite.Silverlight.Samples"
   5:     Width="400" Height="300">
   6:     <UserControl.Resources>
   7:         <d:DataSource x:Key="data" />
   8:     </UserControl.Resources>
   9:     <Grid x:Name="LayoutRoot" DataContext="{StaticResource data}" Background="White">
  10:         <ItemsControl ItemsSource="{Binding Strings}">
  11:             <ItemsPanelTemplate>
  12:                 <DataTemplate>
  13:                     <TextBlock Text="{Binding}" />
  14:                 </DataTemplate>
  15:             </ItemsPanelTemplate>
  16:         </ItemsControl>
  17:     </Grid>
  18: </UserControl>

In this sample an instance of a class named DataSource has been referenced in the user control resources. This class has a property named "Strings" returning an IEnumerable<string>. The yielded strings is the data to be displayed in the ItemsControl. The DataContext property of the Grid is assigned to a reference of the DataSource class and the ItemsSource property is binded to the Strings property throught the Binding markup extension. The data collected from the datasource become the DataContext for the each item with the template specified in ItemsTemplatePanel so the Binding markup extension assign each string to the Text property of the TextBlock.

For the purpose of this article we don't need to deepen our knowledge of this aspect. Instead, we need to concentrate on another aspect. Databinding enable developers to bind properties of a control to properties of a data item and have them updated when the data item change. In the same way it is possible to bind a collection to a specific property and have the control updated when we add or remove items to the collection. This two behaviors came from two interfaces: INotifyPropertyChanged and INotifyCollectionChanged. Objects implementing one of these interfaces have to notify internal changes through an event. PropertyChanged notify changes to single properties and is useful for objects binded directly to control properties, instead CollectionChanged notify about adding or removal of items from a collection and it has to be used for controls binded to collections like an ItemsControl.

Silverlight 2.0 and WPF come with a generic collection ObservableCollection<T> that implements INotifyCollectionChanged interface. This class helps us to bind collection of objects to an ItemsControl. We may create a class, expose a property of this type and bind it to an ItemsControl. Then we can add or remove elements to the collection and them will be added to or removed from the ItemsControl seamless.

Thinking about our CollectionViewSource

It is now the time to think about how to implement our CollectionViewSource. We need to create the control as a filter that will be interposed between the datasource and the consuming control and it will operate notifying changes responding to user choices. So probably our control has to appear as a collection for  the control that will displays the data items. To notify changes we need to implement the INotifyCollectionChanged interface so we get the consuming control to update its appearance responding to changes to the filtering or sorting criteria. Watching to the control from the side of the datasource it has to expose a Source property that we'll connect to the underlying datasource we need to transform.

So let start creating the control. It will be called CollectionViewSource as expected and extends the Control class. We cannot create a simple class that does not implement any other base class because we need to get access to the DependencyProperty system. The Control class is tipically dedicated to elements that have a visual aspect, so probably it would be better we extend a class like DependencyObject for our control to work. Unfortunately we have to use the Control class because all the base classes from FrameworkElement to DependencyObject does not have a public constructor so we are unable to inherit from them. The Control class is not the best choice, but it is the only choice available.

   1: public class CollectionViewSource : Control, IEnumerable, INotifyCollectionChanged
   2: {
   3:     // implement the control... 
   4: }

The class has also to implement two interfaces: IEnumerable and INotifyCollectionChanged. The first interface is required to grant our control the capability to be consumed from an ItemsControl. Using the IEnumerable interface we get two goals. We can assign the CollectionViewSource directly to the ItemsSource property and we can use LINQ to Objects to yield resulting items to the consuming control. Taking advantage of LINQ to Objects will simplify our control structure and get our code to a better level of readability.

   1: #region IEnumerable Members
   2:  
   3: /// <summary>
   4: /// Returns an enumerator that iterates through a collection.
   5: /// </summary>
   6: /// <returns>
   7: /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
   8: /// </returns>
   9: public IEnumerator GetEnumerator()
  10: {
  11:     var result = this.Source
  12:         .Cast<object>();
  13:  
  14:     return result.GetEnumerator();
  15: }
  16:  
  17: #endregion

For the filtering to work we have to expose an event that we'll call every time we have to filter the collection, one time for every item in the collection asking the user to choice the items to show. The simplest thing to do is to create a delegate, that we use as a Where clause lambda expression in the Linq query yielding items. So the delegate will be called by the query every time it needs to examine an element to decide if it has to be yielded or skipped.

   1: /// <summary>
   2: /// Occurs when [filter].
   3: /// </summary>
   4: public event EventHandler<FilterCollectionEventArgs> Filter;
   5:  
   6: /// <summary>
   7: /// Returns an enumerator that iterates through a collection.
   8: /// </summary>
   9: /// <returns>
  10: /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
  11: /// </returns>
  12: public IEnumerator GetEnumerator()
  13: {
  14:     var result = this.Source
  15:         .Cast<object>()
  16:         .Where(FilterItem);
  17:  
  18:     return result.GetEnumerator();
  19: }
  20:  
  21: /// <summary>
  22: /// Filters the item.
  23: /// </summary>
  24: /// <param name="o">The o.</param>
  25: /// <returns></returns>
  26: private bool FilterItem(object o)
  27: {
  28:     FilterCollectionEventArgs args = new FilterCollectionEventArgs(o);
  29:     OnFilter(args);
  30:     return args.Accept;
  31: }
  32:  
  33: /// <summary>
  34: /// Raises the <see cref="E:Filter"/> event.
  35: /// </summary>
  36: /// <param name="e">The <see cref="Elite.Silverlight.FilterCollectionEventArgs"/> instance containing the event data.</param>
  37: protected virtual void OnFilter(FilterCollectionEventArgs e)
  38: {
  39:     EventHandler<FilterCollectionEventArgs> handler = Filter;
  40:  
  41:     if (handler != null)
  42:         handler(this, e);
  43: }

To configure the sorting behavior we have to expose a property that collect the sorting criteria in a way similar to the CollectionViewSource contained in the Windows Presentation Foundation. This require to create a SortDescriptionCollection class that will collect the SortDescription instances. Every SortDescription will require a PropertyName and a Direction property to tell the control wich property use for sorting and the direction of the sorting. All the names I've proposed come directly from the WPF control. Using this namig will grant our CollectionViewSource the maximum compatibility with the WPF control so we may use code snippet created for WPF in Silverlight 2.0.

We use the same LINQ to Objects expression adding OrderBy and ThenBy calls to the query if the sorting is enabled. The sorting will be called immediately after the Where clause.

   1: /// <summary>
   2: /// Returns an enumerator that iterates through a collection.
   3: /// </summary>
   4: /// <returns>
   5: /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
   6: /// </returns>
   7: public IEnumerator GetEnumerator()
   8: {
   9:     var result = this.Source
  10:         .Cast<object>()
  11:         .Where(FilterItem);
  12:  
  13:     foreach (SortDescription sd in this.SortDescriptions)
  14:     {
  15:         if (sd.Direction == ListSortDirection.Ascending)
  16:             result = AppendOrderBy(result, sd.PropertyName);
  17:         else
  18:             result = AppendOrderByDescending(result, sd.PropertyName);
  19:     }
  20:  
  21:     return result.GetEnumerator();
  22: }
  23:  
  24: /// <summary>
  25: /// Appends the order by.
  26: /// </summary>
  27: /// <param name="result">The result.</param>
  28: /// <param name="propertyName">Name of the property.</param>
  29: /// <returns></returns>
  30: private IEnumerable<object> AppendOrderBy(IEnumerable<object> result, string propertyName)
  31: {
  32:     if (result is IOrderedEnumerable<object>)
  33:         return ((IOrderedEnumerable<object>)result).ThenBy(o => GetPropertyValue(o, propertyName));
  34:  
  35:     return result.OrderBy(o => GetPropertyValue(o, propertyName));
  36: }
  37:  
  38: /// <summary>
  39: /// Appends the order by descending.
  40: /// </summary>
  41: /// <param name="result">The result.</param>
  42: /// <param name="propertyName">Name of the property.</param>
  43: /// <returns></returns>
  44: private IEnumerable<object> AppendOrderByDescending(IEnumerable<object> result, string propertyName)
  45: {
  46:     if (result is IOrderedEnumerable<object>)
  47:         return ((IOrderedEnumerable<object>)result).ThenByDescending(o => GetPropertyValue(o, propertyName));
  48:  
  49:     return result.OrderByDescending(o => GetPropertyValue(o, propertyName));
  50: }

With the INotifyCollectionChanged interface we'll notify the control to update his aspect when changes occur. The only thing we have to take care is the way to notify events. The NotifyCollectionChangedEventArgs class we have to use to raise the CollectionChanged event require us to specify the kind of event we are raising. The constructor of this class need to specify a NotifyCollectionChangedAction enumeration parameter that define if we are updating the datasource for Add, Remove, Replace, Move or Reset. To get the ItemsControl updating we need to specify the NotifyCollectionChangedAction.Reset value because every other value does not have an effect on the ItemsControl. This behavior become from the implementation of the ItemsSource property. Inspecting this property with Lutz Roeder Reflector it will show us that it is wrapped with an internal private EnumerableCollectionView that will mask every other type of event. The drawback of this choice is that the target control will be updated entirely instead of handling only the added or removed items. I hope that a future implementation of this control will change this thing, but now we have to accept this behavior to get the CollectionViewSource working.

We have to handle two type of events that raise the CollectionChanged event. The first one is when the underliyng collection raise the same event. This is because if the datasource change we have to reflect this change to the user interface itself forwarding this event. So when the Source property will be assigned we have to hook up its CollectionChanged event (if the source itself implements the INotifyCollectionChanged). For this purpose we create the source property as a DependencyProperty to take advantage of the PropertyChangedCallback that will notify when the Source property change its value. When the value change we have to remove event from a previous instance before to hook the new instance to avoid memory leaks and unexpected results.

   1: public static readonly DependencyProperty SourceProperty =
   2:     DependencyProperty.Register("Source", typeof(IEnumerable), typeof(CollectionViewSource), new PropertyChangedCallback(SourceProperty_Changed));
   3:  
   4: /// <summary>
   5: /// Gets or sets the source.
   6: /// </summary>
   7: /// <value>The source.</value>
   8: public IEnumerable Source
   9: {
  10:     get { return (IEnumerable)this.GetValue(SourceProperty); }
  11:     set { this.SetValue(SourceProperty, value); }
  12: }
  13:  
  14: /// <summary>
  15: /// Sources the property_ changed.
  16: /// </summary>
  17: /// <param name="sender">The sender.</param>
  18: /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
  19: private static void SourceProperty_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e)
  20: {
  21:     CollectionViewSource cvs = sender as CollectionViewSource;
  22:  
  23:     if (cvs != null)
  24:     {
  25:         if (e.OldValue != null && e.OldValue is INotifyCollectionChanged)
  26:             ((INotifyCollectionChanged)e.OldValue).CollectionChanged -= new NotifyCollectionChangedEventHandler(cvs.CollectionViewSource_CollectionChanged);
  27:  
  28:         if (e.NewValue != null && e.NewValue is INotifyCollectionChanged)
  29:             ((INotifyCollectionChanged)e.NewValue).CollectionChanged += new NotifyCollectionChangedEventHandler(cvs.CollectionViewSource_CollectionChanged);
  30:     }
  31: }
  32:  
  33: /// <summary>
  34: /// Handles the CollectionChanged event of the CollectionViewSource control.
  35: /// </summary>
  36: /// <param name="sender">The source of the event.</param>
  37: /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
  38: private void CollectionViewSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  39: {
  40:     if (sender != null)
  41:         this.OnCollectionChanged(e);
  42: }
  43:  
  44: /// <summary>
  45: /// Raises the <see cref="E:CollectionChanged"/> event.
  46: /// </summary>
  47: /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
  48: protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  49: {
  50:     NotifyCollectionChangedEventHandler handler = CollectionChanged;
  51:  
  52:     if (handler != null)
  53:         handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  54: }

Last but not least we have to expose a method to enable users to force the CollectionViewSource to refresh the criteria and notifying the changes if needed. This is the second type of event we have to handle to notify the collection change. If the user call this method we will assume that something has been changed in the criteria and we raise the CollectionChanged event.

   1: /// <summary>
   2: /// Refreshes this instance.
   3: /// </summary>
   4: public void Refresh()
   5: {
   6:     this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
   7: }

Using the CollectionViewSource control

Now that the control has been completed we have to build a sample project that use the CollectionViewSource. For this purpose I have created an application that read the rss feed of my weblog (http://blog.boschin.it) and display it in an ItemsControl. The interface have a TextBox that we use to filter the post by title and two radio buttons that decide if sort the items ascending or descending by the publish date. The application is very simple but its purpose is only to dimostrate how to use the CollectionViewSource control and how simple is to use and customize its behavior. Here is a brief markup defining the application interface:

   1: <UserControl x:Class="Silverlight.Page"
   2:     xmlns="http://schemas.microsoft.com/client/2007" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:s="clr-namespace:Silverlight" Loaded="UserControl_Loaded"
   5:     xmlns:e="clr-namespace:Elite.Silverlight;assembly=Elite.Silverlight"
   6:     xmlns:ed="clr-namespace:Elite.Silverlight.Data;assembly=Elite.Silverlight"
   7:     Width="400" Height="400">
   8:     <UserControl.Resources>
   9:         <s:DataSource x:Name="dataSource" />
  10:         <ed:CollectionViewSource x:Name="rssView" Source="{Binding Items, Source={StaticResource dataSource}}" Filter="rssView_Filter">
  11:             <CollectionViewSource.SortDescriptions>
  12:                 <ed:SortDescription PropertyName="PublishDate" Direction="Ascending" />
  13:             </CollectionViewSource.SortDescriptions>
  14:         </ed:CollectionViewSource>
  15:         <e:StringToUriConverter x:Name="stringToUri" />
  16:         <e:FormatDateTimeConverter x:Name="dateToString" />
  17:     </UserControl.Resources>
  18:     <Grid x:Name="LayoutRoot" Background="#ffffffff">
  19:         <Grid.RowDefinitions>
  20:             <RowDefinition Height="*" />
  21:             <RowDefinition Height="80" />
  22:         </Grid.RowDefinitions>
  23:         <Grid.Resources>
  24:             <DataTemplate x:Name="rssTemplate">
  25:                 <Border Background="#ffeeeeee" Margin="2,2,2,0" Padding="5" BorderBrush="#00000000" BorderThickness="1" CornerRadius="3,3">
  26:                     <Grid>
  27:                         <Grid.ColumnDefinitions>
  28:                             <ColumnDefinition Width="50*"/>
  29:                             <ColumnDefinition Width="50*"/>
  30:                         </Grid.ColumnDefinitions>
  31:                         <Grid.RowDefinitions>
  32:                             <RowDefinition Height="60*" />
  33:                             <RowDefinition Height="40*" />
  34:                         </Grid.RowDefinitions>
  35:                         <HyperlinkButton x:Name="itemLabel" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="3,3,3,3" 
  36:                                          Content="{Binding Title.Text}" NavigateUri="{Binding Id, Converter={StaticResource stringToUri}}" 
  37:                                          TargetName="_blank" FontSize="14" FontFamily="Verdana" Foreground="#ffff9900" />
  38:                         <TextBlock Grid.Column="0" Grid.Row="1" Margin="3,3,3,3" FontSize="14" FontFamily="Verdana" 
  39:                                    Foreground="#ff666666" Text="{Binding PublishDate, Converter={StaticResource dateToString}, ConverterParameter=dd.MM.yyyy - hh:mm}" />
  40:                         <TextBlock Grid.Column="1" Grid.Row="1" Margin="3,3,3,3" FontSize="14" FontFamily="Verdana"
  41:                                    Foreground="#ff666666" Text="{Binding Copyright}" />
  42:                     </Grid>
  43:                 </Border>
  44:             </DataTemplate>
  45:         </Grid.Resources>
  46:         <Border Grid.Column="0" Grid.Row="0" Margin="2" BorderBrush="#ffff9900" BorderThickness="1" CornerRadius="3,3">
  47:             <ScrollViewer DataContext="{StaticResource dataSource}" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
  48:                 <ItemsControl ItemsSource="{Binding Source={StaticResource rssView}}" ItemTemplate="{StaticResource rssTemplate}">
  49:                 </ItemsControl>
  50:             </ScrollViewer>
  51:         </Border>
  52:         <Grid Grid.Column="0" Grid.Row="1" Background="#55ff9900" Margin="2">
  53:             <Grid.ColumnDefinitions>
  54:                 <ColumnDefinition Width="*"/>
  55:                 <ColumnDefinition Width="*"/>
  56:             </Grid.ColumnDefinitions>
  57:             <Grid.RowDefinitions>
  58:                 <RowDefinition Height="50*" />
  59:                 <RowDefinition Height="50*" />
  60:             </Grid.RowDefinitions>
  61:             <RadioButton x:Name="sortDescending" TextAlignment="Right" HorizontalAlignment="Left" Margin="10" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="1" Content="Descending" GroupName="sort" Checked="sort_Checked" IsChecked="true" />
  62:             <RadioButton x:Name="sortAscending" TextAlignment="Left" HorizontalAlignment="Right" Margin="10" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="1" Content="Ascending" GroupName="sort" Checked="sort_Checked" />
  63:             <WatermarkedTextBox x:Name="txtKey" Padding="3" Grid.Column="0" Grid.Row="0" Margin="5" FontSize="14" Grid.ColumnSpan="2" TextChanged="txtKey_TextChanged" Watermark="Insert Keywork..." />
  64:         </Grid>
  65:     </Grid>
  66: </UserControl>

In this code snippet it is visible how to bind correctly the datasource to the CollectionViewSource and the CollectionViewSource itselft to the consuming ItemsControl. When we have established this connection we hook up the filtering event where we use a Contains() to check that the element contains the text written in the textbox. Every time we press a key the TextChanged event simply call the Refresh() method of the CollectionViewSource causing the filtering to happen again with the new text entered. This is a very intensive use of the  CollectionViewSource but with a few collection items (up to one or two hundred) there will be no side effects. In a production application this scenario probably will be handled in a different way, but I'm sure there is many other times you may take advantage of my control. The sorting happen in a similar way. When a radio button has been hit we swap the direction of the configured sorting and then recall the Refresh() method to rebind the interface. Try changing the SortDescription properties to sort by another property.

   1: public partial class Page : UserControl
   2: {
   3:     public Page()
   4:     {
   5:         InitializeComponent();
   6:     }
   7:  
   8:     private void UserControl_Loaded(object sender, RoutedEventArgs e)
   9:     {
  10:         this.dataSource = this.Resources["dataSource"] as DataSource;
  11:         this.dataSource.Load();
  12:     }
  13:  
  14:     private void sort_Checked(object sender, RoutedEventArgs e)
  15:     {
  16:         CollectionViewSource rssView = this.Resources["rssView"] as CollectionViewSource;
  17:  
  18:         if (rssView != null)
  19:         {
  20:             SortDescription sort = rssView.SortDescriptions[0];
  21:             sort.Direction = (sender == sortAscending ? ListSortDirection.Ascending : ListSortDirection.Descending);
  22:             rssView.Refresh();
  23:         }
  24:     }
  25:  
  26:     private void txtKey_TextChanged(object sender, TextChangedEventArgs e)
  27:     {
  28:         rssView.Refresh();
  29:     }
  30:  
  31:     private void rssView_Filter(object sender, Elite.Silverlight.FilterCollectionEventArgs e)
  32:     {
  33:         SyndicationItem item = e.Item as SyndicationItem;
  34:  
  35:         if (item != null)
  36:         {
  37:             e.Accept = item.Title.Text.ToLower().Contains(txtKey.Text.ToLower()) || txtKey.Text == txtKey.Watermark || txtKey.Text == string.Empty;
  38:         }
  39:     }
  40: }

Now we may run the application and try to enter some text in the TextBox. We will see that the displayed item will change according to the text entered. Also checking and unchecking the sort direction radio button will change the order of the the items.

Conclusion

The CollectionViewSource control is part of my Elite.Silverlight Library 1.0.3037.0 where I'm collecting all my reusable controls and classes. Please download it from codeplex (http://www.codeplex.com/silverlightlibrary) and use for free in your projects. The project come under the CCPL license and is free for non-commercial purposes. For use in commercial project please contact me on my weblog contact form at (http://blog.boschin.it/contact.aspx).

 

Rilasciato il Training Kit di .NET 3.5 enhancements

E' stato rilasciato un training kit relativo i futuri "enhancements" del Framework 3.5. Si tratta di un pacchetto di hands-on-lab che comprende le seguenti tecnologie:

  • ADO.NET Data Services
  • ADO.NET Entity Framework
  • ASP.NET AJAX History
  • ASP.NET Dynamic Data
  • ASP.NET MVC
  • ASP.NET Silverlight controls
  • Per maggiori dettagli leggete questo post:

    http://lostintangent.com/2008/04/16/net-35-enhancements-training-kit/

    Il download pubblico lo trovate qui:

    http://www.microsoft.com/downloads/details.aspx?FamilyID=355c80e9-fde0-4812-98b5-8a03f5874e96&displaylang=en

    Summit 2008: Ancora foto...

    Ho appena terminato di caricare le ultime 150 foto su Picasa. Le potete vedere allo stesso indirizzo dell'altra volta:

    http://picasaweb.google.it/codeblock/Seattle?authkey=23xipySjxqE

    Inoltre trovate anche un album pubblico su facebook completamente taggato:

    http://www.facebook.com/album.php?aid=20906&id=621769502

    Aggiungete pure i vostri commenti.

    Technorati Tag: ,

    Summit 2008: Ecco dove lavorano i dipendenti Microsoft...

    DSC_6087
    il servizio navetta
    DSC_6088
    nel parcheggio del Building 9
    DSC_6089
    Uno scorcio del Building 8
    DSC_6090
    Sempre il Building 8
    DSC_6091
    La dentro qualcuno lavora...
    DSC_6093
    Un campo di Soccer
    DSC_6094
    La gente passeggia tra i Building
    DSC_6095
    Il conference center

    Tutto ciò è davvero sconfortante se paragonato alle nostre condizioni di lavoro... o almeno alle mie

    Summit 2008: Le foto ricordo

    DSC_6123

    DSC_6133

    Ed ecco le foto ricordo di questo summit...

    Summit 2008: Aggiornato l'album web

    Ho aggiunto più di 60 nuove immagini.

    http://picasaweb.google.it/codeblock/Seattle?authkey=23xipySjxqE

    Technorati Tag: ,

    Summit 2008: We apologize... no WI-FI on the bus

    non ho letteralmente parole... ecco cosa appariva su un autobus nel campus:

     

    DSC_6076

    This is a spare bus and not your regular bus.

    We apologize for not having Wi-Fi available

    Summit 2008: Effetti collaterali

    DSC_6077 DSC_6078
    DSC_6080 DSC_6081

    Ecco cosa può causare una giornata al campus Microsoft durante il Summit. Qui sta animatamente spiegando le sue impressioni nell'incontro con Oslo...

    Technorati Tag: ,

    ASP.NET: Disponibile update MVC Framework su codeplex

    We've just released the latest source code for ASP.NET MVC...this latest release includes Visual Studio templates and unit tests. You can find this release here

    http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=12640

    Summit 2008: Ragionamenti a colazione

    Stamattina è iniziata bene. Scendo a colazione e una inserviente al piano mi saluta e mi sorride. Arrivo a colazione e un cameriere mi saluta. Vabbè dico io, è il loro lavoro sarà perfettamente normale... ma la cosa continua. un tizio al tavolo si avvicina e mi stringe la mano per salutarmi, poi si siede e parte una piacevole chiacchierata. Chi lo ha mai visto...

    Esco e mi reco allo Starbucks e mentre do uno sguardo interessato alle brioches un tizio dall'altra parte mi fa : "hello, how are you!". Sorriso stampato sulle labbra. Sono le 6:30 di mattina. Dalle nostre parti mi avrebbero sbattuto un cappuccino sul tavolo guardandomi storto.

    Poi la cosa continua. Un tizio che si assicura di non avermi sopravanzato alla cassa. L'autista dell'autobus che mi accoglie sorridendo come fosse la più bella giornata dell'anno. Scendo dall'autobus e una cortese signora mi augura buona giornata. Sono estasiato. Dove devo firmare per avere giornate come questa tutti i giorni?

    Ora sono seduto in attesa che Pablo Castro inizi una sessione su ........... ... (sorry ma non posso dire cosa). Tutti contenti, ci si sorride a vicenda. Sarà pure una stupidaggine ma vivere in questo modo è sicuramente più leggere e semplice.

    Mi sa che da noi c'è molto da imparare.