<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Blog .NET de Thomas Levesque</title>
	<atom:link href="http://tomlev.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://tomlev.wordpress.com</link>
	<description>Nouveautés, découvertes, réflexions, trucs, astuces en tous genres sur les technologies .NET</description>
	<lastBuildDate>Tue, 24 Jan 2012 19:01:31 +0000</lastBuildDate>
	<language>fr</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='tomlev.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://1.gravatar.com/blavatar/520c41eaff9c7e1ea22d20ba2d170f53?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title>Blog .NET de Thomas Levesque</title>
		<link>http://tomlev.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://tomlev.wordpress.com/osd.xml" title="Blog .NET de Thomas Levesque" />
	<atom:link rel='hub' href='http://tomlev.wordpress.com/?pushpress=hub'/>
		<item>
		<title>[WPF] Utiliser Linq pour filtrer, trier et grouper les donn&#233;es dans une CollectionView</title>
		<link>http://tomlev.wordpress.com/2011/11/29/wpf-utiliser-linq-pour-filtrer-trier-et-grouper-les-donnes-dans-une-collectionview/</link>
		<comments>http://tomlev.wordpress.com/2011/11/29/wpf-utiliser-linq-pour-filtrer-trier-et-grouper-les-donnes-dans-une-collectionview/#comments</comments>
		<pubDate>Tue, 29 Nov 2011 17:36:14 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[WPF]]></category>
		<category><![CDATA[collectionview]]></category>
		<category><![CDATA[linq]]></category>

		<guid isPermaLink="false">https://tomlev.wordpress.com/?p=489</guid>
		<description><![CDATA[WPF offre un mécanisme assez simple pour la mise en forme de collections de données, via l’interface ICollectionView et ses propriétés Filter, SortDescriptions et GroupDescriptions : Bien que cette technique ne soit pas très difficile à mettre en œuvre, elle présente certains inconvénients : La syntaxe un peu lourde et pas très naturelle : le [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=489&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>WPF offre un mécanisme assez simple pour la mise en forme de collections de données, via l’interface <code>ICollectionView</code> et ses propriétés <code>Filter</code>, <code>SortDescriptions</code> et <code>GroupDescriptions</code> :</p>
<p><pre class="brush: csharp;">
// Collection à laquelle la vue est liée
public ObservableCollection People { get; private set; }
...

// Vue par défaut de la collection People
ICollectionView view = CollectionViewSource.GetDefaultView(People);

// Uniquement les adultes
view.Filter = o =&gt; ((Person)o).Age &gt;= 18;

// Tri par nom et prénom
view.SortDescriptions.Add(new SortDescription(&quot;LastName&quot;, ListSortDirection.Ascending));
view.SortDescriptions.Add(new SortDescription(&quot;FirstName&quot;, ListSortDirection.Ascending));

// Groupement par pays
view.GroupDescriptions.Add(new PropertyGroupDescription(&quot;Country&quot;));
</pre></p>
<p>Bien que cette technique ne soit pas très difficile à mettre en œuvre, elle présente certains inconvénients :</p>
<ul>
<li>La syntaxe un peu lourde et pas très naturelle : le fait que le paramètre du filtre soit un <code>object</code> alors qu’on sait que les éléments sont de type <code>Person</code> réduit la lisibilité, et l’ajout des tris et descriptions comporte beaucoup de répétitions</li>
<li>Le fait de spécifier les noms des propriétés sous forme de chaine introduit des risques d’erreur, puisqu’ils ne sont pas vérifiés par le compilateur.</li>
</ul>
<p>Depuis quelques années, on a pris l&#8217;habitude d&#8217;utiliser Linq pour faire ce genre de choses… il serait donc pratique de pouvoir le faire aussi pour définir le filtre, le tri et le groupement d&#8217;une <code>ICollectionView</code>.</p>
<p>Voyons donc quelle syntaxe on pourrait utiliser pour faire ça avec Linq… quelque chose comme ça, par exemple ?</p>
<p><pre class="brush: csharp;">
People.Where(p =&gt; p.Age &gt;= 18)
      .OrderBy(p =&gt; p.LastName)
      .ThenBy(p =&gt; p.FirstName)
      .GroupBy(p =&gt; p.Country);
</pre></p>
<p>Ou encore, en utilisant la syntaxe de requête Linq :</p>
<p><pre class="brush: csharp;">
from p in People
where p.Age &gt;= 18
orderby p.LastName, p.FirstName
group p by p.Country;
</pre></p>
<p>Bon, évidemment ça ne suffit pas : ce code ne fait rien d’autre que créer une requête Linq sur la collection, il ne modifie pas la <code>CollectionView</code>… mais avec un tout petit peu de travail supplémentaire on peut obtenir le résultat voulu :</p>
<p><pre class="brush: csharp;">
var query =
    from p in People.ShapeView()
    where p.Age &gt;= 18
    orderby p.LastName, p.FirstName
    group p by p.Country;

query.Apply();
</pre></p>
<p>La méthode <code>ShapeView</code> renvoie un wrapper qui encapsule la vue par défaut de la collection, et expose des méthodes <code>Where</code>, <code>OrderBy</code> et <code>GroupBy</code> avec les signatures appropriées pour définir la mise en forme. Créer la requête n’a pas d’effet direct, c’est la méthode <code>Apply</code> qui permet d’appliquer les changements à la vue : en effet, il vaut mieux tous les appliquer en même temps à l’aide de <code>ICollectionView.DeferRefresh</code>, pour ne provoquer un rafraichissement de la vue à chaque nouvelle clause de la requête. Lors de l’appel à <code>Apply</code>, on observe que la vue est bien mise à jour pour refléter les clauses de la requête.</p>
<p>Cette solution permet de conserver le typage fort pour le filtrage, le tri et le groupement, avec pour bénéfice immédiat la vérification par le compilateur. C’est également plus concis et plus lisible que le code d’origine… Attention quand même à une chose : certaines requêtes qui seront correctes du point de vue de C# ne seront en fait pas applicables à une <code>CollectionView</code>. Par exemple, si vous essayez de grouper par la première lettre du nom (<code>p.LastName.Substring(0, 1)</code>), la méthode <code>GroupBy</code> échouera, car seules les propriétés sont supportées par <code>PropertyGroupDescription</code>.</p>
<p>Notez que le wrapper n’écrase pas les propriétés courantes de la <code>CollectionView</code> si vous ne spécifiez pas la clause Linq correspondante, il est donc possible de modifier une vue existante sans devoir tout spécifier à nouveau. Si nécessaire, des méthodes <code>ClearFilter</code>, <code>ClearSort</code> et <code>ClearGrouping</code> permettent de réinitialiser le filtre, le tri et le regroupement :</p>
<p><pre class="brush: csharp;">
// Suppression du regroupement et ajout d'un tri :
People.ShapeView()
      .ClearGrouping()
      .OrderBy(p =&gt; p.LastName);
      .Apply();
</pre></p>
<p>Notez que comme pour une requête Linq “normale”, on peut au choix utiliser la syntaxe de requête ou appeler directement les méthodes, puisqu’il s’agit simplement d’une transformation syntaxique effectuée par le compilateur.</p>
<p>Pour finir, voici le code complet du wrapper et les méthodes d’extension associées :</p>
<p><pre class="brush: csharp;">
    public static class CollectionViewShaper
    {
        public static CollectionViewShaper&lt;TSource&gt; ShapeView&lt;TSource&gt;(this IEnumerable&lt;TSource&gt; source)
        {
            var view = CollectionViewSource.GetDefaultView(source);
            return new CollectionViewShaper&lt;TSource&gt;(view);
        }

        public static CollectionViewShaper&lt;TSource&gt; Shape&lt;TSource&gt;(this ICollectionView view)
        {
            return new CollectionViewShaper&lt;TSource&gt;(view);
        }
    }

    public class CollectionViewShaper&lt;TSource&gt;
    {
        private readonly ICollectionView _view;
        private Predicate&lt;object&gt; _filter;
        private readonly List&lt;SortDescription&gt; _sortDescriptions = new List&lt;SortDescription&gt;();
        private readonly List&lt;GroupDescription&gt; _groupDescriptions = new List&lt;GroupDescription&gt;();

        public CollectionViewShaper(ICollectionView view)
        {
            if (view == null)
                throw new ArgumentNullException(&quot;view&quot;);
            _view = view;
            _filter = view.Filter;
            _sortDescriptions = view.SortDescriptions.ToList();
            _groupDescriptions = view.GroupDescriptions.ToList();
        }

        public void Apply()
        {
            using (_view.DeferRefresh())
            {
                _view.Filter = _filter;
                _view.SortDescriptions.Clear();
                foreach (var s in _sortDescriptions)
                {
                    _view.SortDescriptions.Add(s);
                }
                _view.GroupDescriptions.Clear();
                foreach (var g in _groupDescriptions)
                {
                    _view.GroupDescriptions.Add(g);
                }
            }
        }
            
        public CollectionViewShaper&lt;TSource&gt; ClearGrouping()
        {
            _groupDescriptions.Clear();
            return this;
        }

        public CollectionViewShaper&lt;TSource&gt; ClearSort()
        {
            _sortDescriptions.Clear();
            return this;
        }

        public CollectionViewShaper&lt;TSource&gt; ClearFilter()
        {
            _filter = null;
            return this;
        }

        public CollectionViewShaper&lt;TSource&gt; ClearAll()
        {
            _filter = null;
            _sortDescriptions.Clear();
            _groupDescriptions.Clear();
            return this;
        }

        public CollectionViewShaper&lt;TSource&gt; Where(Func&lt;TSource, bool&gt; predicate)
        {
            _filter = o =&gt; predicate((TSource)o);
            return this;
        }

        public CollectionViewShaper&lt;TSource&gt; OrderBy&lt;TKey&gt;(Expression&lt;Func&lt;TSource, TKey&gt;&gt; keySelector)
        {
            return OrderBy(keySelector, true, ListSortDirection.Ascending);
        }

        public CollectionViewShaper&lt;TSource&gt; OrderByDescending&lt;TKey&gt;(Expression&lt;Func&lt;TSource, TKey&gt;&gt; keySelector)
        {
            return OrderBy(keySelector, true, ListSortDirection.Descending);
        }

        public CollectionViewShaper&lt;TSource&gt; ThenBy&lt;TKey&gt;(Expression&lt;Func&lt;TSource, TKey&gt;&gt; keySelector)
        {
            return OrderBy(keySelector, false, ListSortDirection.Ascending);
        }

        public CollectionViewShaper&lt;TSource&gt; ThenByDescending&lt;TKey&gt;(Expression&lt;Func&lt;TSource, TKey&gt;&gt; keySelector)
        {
            return OrderBy(keySelector, false, ListSortDirection.Descending);
        }

        private CollectionViewShaper&lt;TSource&gt; OrderBy&lt;TKey&gt;(Expression&lt;Func&lt;TSource, TKey&gt;&gt; keySelector, bool clear, ListSortDirection direction)
        {
            string path = GetPropertyPath(keySelector.Body);
            if (clear)
                _sortDescriptions.Clear();
            _sortDescriptions.Add(new SortDescription(path, direction));
            return this;
        }

        public CollectionViewShaper&lt;TSource&gt; GroupBy&lt;TKey&gt;(Expression&lt;Func&lt;TSource, TKey&gt;&gt; keySelector)
        {
            string path = GetPropertyPath(keySelector.Body);
            _groupDescriptions.Add(new PropertyGroupDescription(path));
            return this;
        }

        private static string GetPropertyPath(Expression expression)
        {
            var names = new Stack&lt;string&gt;();
            var expr = expression;
            while (expr != null &amp;&amp; !(expr is ParameterExpression) &amp;&amp; !(expr is ConstantExpression))
            {
                var memberExpr = expr as MemberExpression;
                if (memberExpr == null)
                    throw new ArgumentException(&quot;The selector body must contain only property or field access expressions&quot;);
                names.Push(memberExpr.Member.Name);
                expr = memberExpr.Expression;
            }
            return String.Join(&quot;.&quot;, names.ToArray());
        }
    }
</pre></p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/collectionview/'>collectionview</a>, <a href='http://tomlev.wordpress.com/tag/linq/'>linq</a>, <a href='http://tomlev.wordpress.com/tag/wpf/'>WPF</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/489/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=489&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2011/11/29/wpf-utiliser-linq-pour-filtrer-trier-et-grouper-les-donnes-dans-une-collectionview/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Créer des styles paramétrables à l&#8217;aide des propriétés attachées</title>
		<link>http://tomlev.wordpress.com/2011/09/30/wpf-creer-des-styles-parametrables-a-laide-des-proprietes-attachees/</link>
		<comments>http://tomlev.wordpress.com/2011/09/30/wpf-creer-des-styles-parametrables-a-laide-des-proprietes-attachees/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 22:08:37 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Astuces]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[propriété attachée]]></category>
		<category><![CDATA[style]]></category>
		<category><![CDATA[template]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=459</guid>
		<description><![CDATA[Je voudrais aujourd&#8217;hui partager avec vous une petite astuce que j&#8217;utilise souvent depuis quelques mois. Supposons que pour améliorer l&#8217;apparence de votre application, vous ayez créé des styles personnalisés pour les contrôles standards : Bon, je ne suis pas designer, hein&#8230; mais ça fera parfaitement l&#8217;affaire pour illustrer mon propos . Ces styles sont très [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=459&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Je voudrais aujourd&#8217;hui partager avec vous une petite astuce que j&#8217;utilise souvent depuis quelques mois. Supposons que pour améliorer l&#8217;apparence de votre application, vous ayez créé des styles personnalisés pour les contrôles standards :</p>
<p><a href="http://tomlev.files.wordpress.com/2011/09/parameterized_styles11.png"><img src="http://tomlev.files.wordpress.com/2011/09/parameterized_styles11.png?w=780" alt="" title=""   class="aligncenter size-full wp-image-468" /></a></p>
<p>Bon, je ne suis pas designer, hein&#8230; mais ça fera parfaitement l&#8217;affaire pour illustrer mon propos <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> . Ces styles sont très simples, ce sont les styles par défaut des <code>CheckBox</code> et <code>RadioButton</code> dans lesquels j&#8217;ai seulement modifié les templates pour remplacer les <code>BulletChrome</code> par ces superbes marques bleues. Voilà le code :</p>
<p><pre class="brush: xml;">
        &lt;Style x:Key=&quot;{x:Type CheckBox}&quot; TargetType=&quot;{x:Type CheckBox}&quot;&gt;
            &lt;Setter Property=&quot;Foreground&quot; Value=&quot;{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}&quot;/&gt;
            &lt;Setter Property=&quot;Background&quot; Value=&quot;{StaticResource CheckBoxFillNormal}&quot;/&gt;
            &lt;Setter Property=&quot;BorderBrush&quot; Value=&quot;{StaticResource CheckBoxStroke}&quot;/&gt;
            &lt;Setter Property=&quot;BorderThickness&quot; Value=&quot;1&quot;/&gt;
            &lt;Setter Property=&quot;FocusVisualStyle&quot; Value=&quot;{StaticResource EmptyCheckBoxFocusVisual}&quot;/&gt;
            &lt;Setter Property=&quot;Template&quot;&gt;
                &lt;Setter.Value&gt;
                    &lt;ControlTemplate TargetType=&quot;{x:Type CheckBox}&quot;&gt;
                        &lt;BulletDecorator Background=&quot;Transparent&quot;
                                         SnapsToDevicePixels=&quot;true&quot;&gt;
                            &lt;BulletDecorator.Bullet&gt;
                                &lt;Border BorderBrush=&quot;{TemplateBinding BorderBrush}&quot;
                                        Background=&quot;{TemplateBinding Background}&quot;
                                        BorderThickness=&quot;1&quot;
                                        Width=&quot;11&quot; Height=&quot;11&quot; Margin=&quot;0,1,0,0&quot;&gt;
                                    &lt;Grid&gt;
                                        &lt;Path Name=&quot;TickMark&quot;
                                              Fill=&quot;Blue&quot;
                                              Data=&quot;M0,4 5,9 9,0 4,5&quot;
                                              Visibility=&quot;Hidden&quot; /&gt;
                                        &lt;Rectangle Name=&quot;IndeterminateMark&quot;
                                                   Fill=&quot;Blue&quot;
                                                   Width=&quot;7&quot; Height=&quot;7&quot;
                                                   HorizontalAlignment=&quot;Center&quot;
                                                   VerticalAlignment=&quot;Center&quot;
                                                   Visibility=&quot;Hidden&quot; /&gt;
                                    &lt;/Grid&gt;
                                &lt;/Border&gt;
                            &lt;/BulletDecorator.Bullet&gt;
                            &lt;ContentPresenter HorizontalAlignment=&quot;{TemplateBinding HorizontalContentAlignment}&quot;
                                              Margin=&quot;{TemplateBinding Padding}&quot;
                                              RecognizesAccessKey=&quot;True&quot;
                                              SnapsToDevicePixels=&quot;{TemplateBinding SnapsToDevicePixels}&quot;
                                              VerticalAlignment=&quot;{TemplateBinding VerticalContentAlignment}&quot;/&gt;
                        &lt;/BulletDecorator&gt;
                        &lt;ControlTemplate.Triggers&gt;
                            &lt;Trigger Property=&quot;HasContent&quot; Value=&quot;true&quot;&gt;
                                &lt;Setter Property=&quot;FocusVisualStyle&quot; Value=&quot;{StaticResource CheckRadioFocusVisual}&quot;/&gt;
                                &lt;Setter Property=&quot;Padding&quot; Value=&quot;4,0,0,0&quot;/&gt;
                            &lt;/Trigger&gt;
                            &lt;Trigger Property=&quot;IsEnabled&quot; Value=&quot;false&quot;&gt;
                                &lt;Setter Property=&quot;Foreground&quot; Value=&quot;{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}&quot;/&gt;
                            &lt;/Trigger&gt;
                            &lt;Trigger Property=&quot;IsChecked&quot; Value=&quot;True&quot;&gt;
                                &lt;Setter TargetName=&quot;TickMark&quot; Property=&quot;Visibility&quot; Value=&quot;Visible&quot; /&gt;
                            &lt;/Trigger&gt;
                            &lt;Trigger Property=&quot;IsChecked&quot; Value=&quot;{x:Null}&quot;&gt;
                                &lt;Setter TargetName=&quot;IndeterminateMark&quot; Property=&quot;Visibility&quot; Value=&quot;Visible&quot; /&gt;
                            &lt;/Trigger&gt;
                        &lt;/ControlTemplate.Triggers&gt;
                    &lt;/ControlTemplate&gt;
                &lt;/Setter.Value&gt;
            &lt;/Setter&gt;
        &lt;/Style&gt;
        &lt;Style x:Key=&quot;{x:Type RadioButton}&quot; TargetType=&quot;{x:Type RadioButton}&quot;&gt;
            &lt;Setter Property=&quot;Foreground&quot; Value=&quot;{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}&quot;/&gt;
            &lt;Setter Property=&quot;Background&quot; Value=&quot;#F4F4F4&quot;/&gt;
            &lt;Setter Property=&quot;BorderBrush&quot; Value=&quot;{StaticResource CheckBoxStroke}&quot;/&gt;
            &lt;Setter Property=&quot;BorderThickness&quot; Value=&quot;1&quot;/&gt;
            &lt;Setter Property=&quot;Template&quot;&gt;
                &lt;Setter.Value&gt;
                    &lt;ControlTemplate TargetType=&quot;{x:Type RadioButton}&quot;&gt;
                        &lt;BulletDecorator Background=&quot;Transparent&quot;&gt;
                            &lt;BulletDecorator.Bullet&gt;
                                &lt;Grid VerticalAlignment=&quot;Center&quot; Margin=&quot;0,1,0,0&quot;&gt;
                                    &lt;Ellipse Width=&quot;11&quot; Height=&quot;11&quot;
                                             Stroke=&quot;{TemplateBinding BorderBrush}&quot;
                                             StrokeThickness=&quot;1&quot;
                                             Fill=&quot;{TemplateBinding Background}&quot; /&gt;
                                    &lt;Ellipse Name=&quot;TickMark&quot;
                                             Width=&quot;7&quot; Height=&quot;7&quot;
                                             Fill=&quot;Blue&quot;
                                             Visibility=&quot;Hidden&quot; /&gt;
                                    &lt;Ellipse Name=&quot;IndeterminateMark&quot;
                                             Width=&quot;3&quot; Height=&quot;3&quot;
                                             Fill=&quot;Blue&quot;
                                             Visibility=&quot;Hidden&quot; /&gt;
                                &lt;/Grid&gt;
                            &lt;/BulletDecorator.Bullet&gt;
                            &lt;ContentPresenter HorizontalAlignment=&quot;{TemplateBinding HorizontalContentAlignment}&quot;
                                              Margin=&quot;{TemplateBinding Padding}&quot;
                                              RecognizesAccessKey=&quot;True&quot;
                                              VerticalAlignment=&quot;{TemplateBinding VerticalContentAlignment}&quot;/&gt;
                        &lt;/BulletDecorator&gt;
                        &lt;ControlTemplate.Triggers&gt;
                            &lt;Trigger Property=&quot;HasContent&quot; Value=&quot;true&quot;&gt;
                                &lt;Setter Property=&quot;FocusVisualStyle&quot; Value=&quot;{StaticResource CheckRadioFocusVisual}&quot;/&gt;
                                &lt;Setter Property=&quot;Padding&quot; Value=&quot;4,0,0,0&quot;/&gt;
                            &lt;/Trigger&gt;
                            &lt;Trigger Property=&quot;IsEnabled&quot; Value=&quot;false&quot;&gt;
                                &lt;Setter Property=&quot;Foreground&quot; Value=&quot;{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}&quot;/&gt;
                            &lt;/Trigger&gt;
                            &lt;Trigger Property=&quot;IsChecked&quot; Value=&quot;True&quot;&gt;
                                &lt;Setter TargetName=&quot;TickMark&quot; Property=&quot;Visibility&quot; Value=&quot;Visible&quot; /&gt;
                            &lt;/Trigger&gt;
                            &lt;Trigger Property=&quot;IsChecked&quot; Value=&quot;{x:Null}&quot;&gt;
                                &lt;Setter TargetName=&quot;IndeterminateMark&quot; Property=&quot;Visibility&quot; Value=&quot;Visible&quot; /&gt;
                            &lt;/Trigger&gt;
                        &lt;/ControlTemplate.Triggers&gt;
                    &lt;/ControlTemplate&gt;
                &lt;/Setter.Value&gt;
            &lt;/Setter&gt;
        &lt;/Style&gt;
</pre></p>
<p>Vous avez donc maintenant de magnifiques contrôles qui vont faire de l&#8217;application un grand succès, le management est content, tout va pour le mieux dans le meilleur des mondes&#8230; Et là, vous réalisez que dans un autre écran de l&#8217;application, les contrôles doivent avoir le même style, mais en vert !</p>
<p>La première solution qui vient à l&#8217;esprit est de dupliquer le style en mettant du vert à la place du bleu. Mais comme vous êtes un bon développeur soucieux des bonnes pratiques, vous savez que la duplication de code, c&#8217;est mal : si vous devez un jour modifier le style de la <code>CheckBox</code> bleue, il faudra aussi modifier celui de la verte&#8230; et peut-être aussi la rouge, la noire, etc. Bref, ça deviendrait vite ingérable. Il faut donc refactoriser, mais comment ? Il faudrait pouvoir passer la couleur en paramètre du style, mais un style n&#8217;est pas une méthode à laquelle on peut passer des paramètres&#8230;</p>
<p>Il faudrait donc avoir une propriété supplémentaire pour indiquer la couleur des &#8220;ticks&#8221;, et se binder sur cette propriété dans le template. Une approche possible est de créer des contrôles personnalisés hérités de <code>CheckBox</code> et <code>RadioButton</code>, avec une propriété supplémentaire <code>TickBrush</code>&#8230; mais personnellement je n&#8217;aime pas beaucoup cette approche : je préfère éviter de créer de nouveaux contrôles quand on peut s&#8217;en sortir avec les contrôles standard.</p>
<p>En fait, il y a une solution plus simple : il suffit de créer une classe, qu&#8217;on appelle par exemple <code>ThemeProperties</code>, et de déclarer dedans une propriété attachée de type <code>Brush</code>:</p>
<p><pre class="brush: csharp;">
    public static class ThemeProperties
    {
        public static Brush GetTickBrush(DependencyObject obj)
        {
            return (Brush)obj.GetValue(TickBrushProperty);
        }

        public static void SetTickBrush(DependencyObject obj, Brush value)
        {
            obj.SetValue(TickBrushProperty, value);
        }

        public static readonly DependencyProperty TickBrushProperty =
            DependencyProperty.RegisterAttached(
                &quot;TickBrush&quot;,
                typeof(Brush),
                typeof(ThemeProperties),
                new FrameworkPropertyMetadata(Brushes.Black));
    }
</pre></p>
<p>On modifie un peu nos templates pour remplacer la couleur en dur par un binding sur cette propriété :</p>
<p><pre class="brush: xml;">
                                ...

                                &lt;!-- CheckBox --&gt;
                                        &lt;Path Name=&quot;TickMark&quot;
                                              Fill=&quot;{TemplateBinding my:ThemeProperties.TickBrush}&quot;
                                              Data=&quot;M0,4 5,9 9,0 4,5&quot;
                                              Visibility=&quot;Hidden&quot; /&gt;
                                        &lt;Rectangle Name=&quot;IndeterminateMark&quot;
                                                   Fill=&quot;{TemplateBinding my:ThemeProperties.TickBrush}&quot;
                                                   Width=&quot;7&quot; Height=&quot;7&quot;
                                                   HorizontalAlignment=&quot;Center&quot;
                                                   VerticalAlignment=&quot;Center&quot;
                                                   Visibility=&quot;Hidden&quot; /&gt;

                                ...

                                &lt;!-- RadioButton --&gt;
                                    &lt;Ellipse Name=&quot;TickMark&quot;
                                             Width=&quot;7&quot; Height=&quot;7&quot;
                                             Fill=&quot;{TemplateBinding my:ThemeProperties.TickBrush}&quot;
                                             Visibility=&quot;Hidden&quot; /&gt;
                                    &lt;Ellipse Name=&quot;IndeterminateMark&quot;
                                             Width=&quot;3&quot; Height=&quot;3&quot;
                                             Fill=&quot;{TemplateBinding my:ThemeProperties.TickBrush}&quot;
                                             Visibility=&quot;Hidden&quot; /&gt;

</pre></p>
<p>Et quand on utilise les contrôles, on précise la couleur qu&#8217;on veut pour le tick :</p>
<p><pre class="brush: xml;">
&lt;CheckBox Content=&quot;Checked&quot; IsChecked=&quot;True&quot; my:ThemeProperties.TickBrush=&quot;Blue&quot; /&gt;
</pre></p>
<p>On peut donc maintenant avoir des contrôles qui partagent le même style, mais en changeant la couleur d&#8217;un élément du template :</p>
<p><a href="http://tomlev.files.wordpress.com/2011/09/parameterized_styles42.png"><img src="http://tomlev.files.wordpress.com/2011/09/parameterized_styles42.png?w=780" alt="" title=""   class="aligncenter size-full wp-image-475" /></a></p>
<p>On a donc effectivement rendu les styles paramétrables ! Il reste cependant un petit souci : étant donné que tous les contrôles d&#8217;un même éran utilisent tous la même couleur, il n&#8217;est pas très pratique de devoir la répéter sur chaque contrôle. L&#8217;idéal serait de pouvoir indiquer à la racine de la vue la couleur à utiliser pour tous les contrôles&#8230; et justement, les dependency properties (et donc les propriétés attachées) offrent une fonctionnalité qui permet de faire exactement ça : l&#8217;héritage de valeur. Il suffit d&#8217;indiquer le flag <code>Inherits</code> lors de la déclaration de la propriété <code>TickBrush</code> :</p>
<p><pre class="brush: csharp;">
        public static readonly DependencyProperty TickBrushProperty =
            DependencyProperty.RegisterAttached(
                &quot;TickBrush&quot;,
                typeof(Brush),
                typeof(ThemeProperties),
                new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.Inherits));
</pre></p>
<p>Avec cette modification, la propriété devient &#8220;ambiante&#8221; : il suffit d&#8217;indiquer sa valeur sur un contrôle parent (par exemple la racine de la vue) pour que tous les descendants prennent en compte cette valeur. On peut donc très facilement faire des écrans avec des contrôles qui partagent le même style, mais en appliquant des couleurs différentes.</p>
<p>Le concept peut bien sûr être étendu à d&#8217;autres cas : en fait, dès qu&#8217;un élément du template doit varier selon un critère arbitraire, cette technique peut s&#8217;appliquer. Cela évite bien souvent de devoir dupliquer le template pour ne changer qu&#8217;un petit détail.</p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/propriete-attachee/'>propriété attachée</a>, <a href='http://tomlev.wordpress.com/tag/style/'>style</a>, <a href='http://tomlev.wordpress.com/tag/template/'>template</a>, <a href='http://tomlev.wordpress.com/tag/wpf/'>WPF</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/459/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/459/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/459/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/459/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/459/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/459/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/459/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/459/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/459/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/459/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/459/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/459/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/459/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/459/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=459&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2011/09/30/wpf-creer-des-styles-parametrables-a-laide-des-proprietes-attachees/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2011/09/parameterized_styles11.png" medium="image" />

		<media:content url="http://tomlev.files.wordpress.com/2011/09/parameterized_styles42.png" medium="image" />
	</item>
		<item>
		<title>[WPF 4.5] Abonnement à un évènement à l’aide d’une markup extension</title>
		<link>http://tomlev.wordpress.com/2011/09/23/wpf-4-5-abonnement-a-un-evenement-a-l%e2%80%99aide-d%e2%80%99une-markup-extension/</link>
		<comments>http://tomlev.wordpress.com/2011/09/23/wpf-4-5-abonnement-a-un-evenement-a-l%e2%80%99aide-d%e2%80%99une-markup-extension/#comments</comments>
		<pubDate>Thu, 22 Sep 2011 22:37:56 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[WPF]]></category>
		<category><![CDATA[.NET 4.5]]></category>
		<category><![CDATA[évènements]]></category>
		<category><![CDATA[markup extension]]></category>
		<category><![CDATA[XAML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=447</guid>
		<description><![CDATA[Voilà un certain temps que je n&#8217;avais plus parlé des markup extensions&#8230; J&#8217;y reviens à l&#8217;occasion de la sortie de Visual Studio 11 Developer Preview, qui introduit un certain nombre de nouveautés dans WPF. La nouveauté dont je vais parler n&#8217;est sans doute pas la plus spectaculaire, mais elle vient combler un manque des versions [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=447&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Voilà un certain temps que je n&#8217;avais plus parlé des markup extensions&#8230; J&#8217;y reviens à l&#8217;occasion de la sortie de <a href="http://msdn.microsoft.com/en-us/vstudio/hh127353">Visual Studio 11 Developer Preview</a>, qui introduit un certain nombre de <a href="http://msdn.microsoft.com/en-us/library/bb613588%28v=VS.110%29.aspx">nouveautés</a> dans WPF. La nouveauté dont je vais parler n&#8217;est sans doute pas la plus spectaculaire, mais elle vient combler un manque des versions précédentes : le support des markup extensions pour les évènements.</p>
<p>Jusqu&#8217;ici, il était possible d&#8217;utiliser une markup extension en XAML pour affecter une valeur à une propriété, mais on ne pouvait pas faire la même chose pour s&#8217;abonner à un évènement. Dans WPF 4.5, c&#8217;est désormais possible. Voilà donc un petit exemple de ce que cela permet de faire&#8230;</p>
<p>Quand on utilise le pattern MVVM, on associe souvent des commandes du ViewModel à des contrôles de la vue, via le mécanisme de binding. Cette approche fonctionne généralement assez bien, mais elle présente certains inconvénients :</p>
<ul>
<li>cela introduit beaucoup de code de &#8220;plomberie&#8221; dans le ViewModel</li>
<li>tous les contrôles n&#8217;ont pas une propriété <code>Command</code> (en fait, la plupart ne l&#8217;ont pas), et quand cette propriété existe, elle ne correspond qu&#8217;à un seul évènement du contrôle (par exemple le clic sur un bouton). Il n&#8217;y a pas de moyen vraiment simple de relier les autres évènements à des commandes du ViewModel.</li>
</ul>
<p>Il serait plus pratique de pouvoir lier directement l&#8217;évènement à une méthode du ViewModel de la façon suivante:<br />
<pre class="brush: xml;">
        &lt;Button Content=&quot;Click me&quot;
                Click=&quot;{my:EventBinding OnClick}&quot; /&gt;
</pre><br />
Avec la méthode <code>OnClick</code> définie dans le ViewModel:<br />
<pre class="brush: csharp;">
        public void OnClick(object sender, EventArgs e)
        {
            MessageBox.Show(&quot;Hello world!&quot;);
        }
</pre></p>
<p>Eh bien cela est désormais possible ! Voilà donc une petite preuve de concept&#8230; La classe <code>EventBindingExtension</code> présentée ci-dessous obtient d&#8217;abord le <code>DataContext</code> du contrôle, puis recherche la méthode spécifiée dans le <code>DataContext</code>, et renvoie un delegate pour cette méthode:</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Markup;


    public class EventBindingExtension : MarkupExtension
    {
        public EventBindingExtension() { }

        public EventBindingExtension(string eventHandlerName)
        {
            this.EventHandlerName = eventHandlerName;
        }

        [ConstructorArgument(&quot;eventHandlerName&quot;)]
        public string EventHandlerName { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (string.IsNullOrEmpty(EventHandlerName))
                throw new ArgumentException(&quot;The EventHandlerName property is not set&quot;, &quot;EventHandlerName&quot;);

            var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));

            EventInfo eventInfo = target.TargetProperty as EventInfo;
            if (eventInfo == null)
                throw new InvalidOperationException(&quot;The target property must be an event&quot;);
            
            object dataContext = GetDataContext(target.TargetObject);
            if (dataContext == null)
                throw new InvalidOperationException(&quot;No DataContext found&quot;);

            var handler = GetHandler(dataContext, eventInfo, EventHandlerName);
            if (handler == null)
                throw new ArgumentException(&quot;No valid event handler was found&quot;, &quot;EventHandlerName&quot;);

            return handler;
        }

        #region Helper methods

        static object GetHandler(object dataContext, EventInfo eventInfo, string eventHandlerName)
        {
            Type dcType = dataContext.GetType();

            var method = dcType.GetMethod(
                eventHandlerName,
                GetParameterTypes(eventInfo));
            if (method != null)
            {
                if (method.IsStatic)
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, method);
                else
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, dataContext, method);
            }

            return null;
        }

        static Type[] GetParameterTypes(EventInfo eventInfo)
        {
            var invokeMethod = eventInfo.EventHandlerType.GetMethod(&quot;Invoke&quot;);
            return invokeMethod.GetParameters().Select(p =&gt; p.ParameterType).ToArray();
        }

        static object GetDataContext(object target)
        {
            var depObj = target as DependencyObject;
            if (depObj == null)
                return null;

            return depObj.GetValue(FrameworkElement.DataContextProperty)
                ?? depObj.GetValue(FrameworkContentElement.DataContextProperty);
        }

        #endregion
    }
</pre></p>
<p>Cette classe est utilisable comme dans l&#8217;exemple présenté plus haut.</p>
<p>En l&#8217;état, cette markup extension présente une limitation un peu gênante : le <code>DataContext</code> doit être défini avant l&#8217;appel à <code>ProvideValue</code>, sinon il n&#8217;est pas possible de trouver la méthode qui gère l&#8217;évènement. Une solution pourrait être de s&#8217;abonner à l&#8217;évènement <code>DataContextChanged</code> pour s&#8217;abonner à l&#8217;évènement plus tard, mais en attendant il faut quand même renvoyer une valeur&#8230; et si on renvoie <code>null</code>, on obtient une exception car on ne peut pas s&#8217;abonner à un évènement avec un handler <code>null</code>. Il faudrait donc renvoyer un handler &#8220;bidon&#8221; généré dynamiquement en fonction de la signature de l&#8217;évènement. Voilà qui complique un peu les choses&#8230; mais ça reste faisable.</p>
<p>Voici une deuxième version qui implémente cette amélioration :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Windows;
using System.Windows.Markup;

    public class EventBindingExtension : MarkupExtension
    {
        private EventInfo _eventInfo;

        public EventBindingExtension() { }

        public EventBindingExtension(string eventHandlerName)
        {
            this.EventHandlerName = eventHandlerName;
        }

        [ConstructorArgument(&quot;eventHandlerName&quot;)]
        public string EventHandlerName { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (string.IsNullOrEmpty(EventHandlerName))
                throw new ArgumentException(&quot;The EventHandlerName property is not set&quot;, &quot;EventHandlerName&quot;);

            var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));

            var targetObj = target.TargetObject as DependencyObject;
            if (targetObj == null)
                throw new InvalidOperationException(&quot;The target object must be a DependencyObject&quot;);

            _eventInfo = target.TargetProperty as EventInfo;
            if (_eventInfo == null)
                throw new InvalidOperationException(&quot;The target property must be an event&quot;);

            object dataContext = GetDataContext(targetObj);
            if (dataContext == null)
            {
                SubscribeToDataContextChanged(targetObj);
                return GetDummyHandler(_eventInfo.EventHandlerType);
            }

            var handler = GetHandler(dataContext, _eventInfo, EventHandlerName);
            if (handler == null)
            {
                Trace.TraceError(
                    &quot;EventBinding: no suitable method named '{0}' found in type '{1}' to handle event '{2'}&quot;,
                    EventHandlerName,
                    dataContext.GetType(),
                    _eventInfo);
                return GetDummyHandler(_eventInfo.EventHandlerType);
            }

            return handler;
            
        }

        #region Helper methods

        static Delegate GetHandler(object dataContext, EventInfo eventInfo, string eventHandlerName)
        {
            Type dcType = dataContext.GetType();

            var method = dcType.GetMethod(
                eventHandlerName,
                GetParameterTypes(eventInfo.EventHandlerType));
            if (method != null)
            {
                if (method.IsStatic)
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, method);
                else
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, dataContext, method);
            }

            return null;
        }

        static Type[] GetParameterTypes(Type delegateType)
        {
            var invokeMethod = delegateType.GetMethod(&quot;Invoke&quot;);
            return invokeMethod.GetParameters().Select(p =&gt; p.ParameterType).ToArray();
        }

        static object GetDataContext(DependencyObject target)
        {
            return target.GetValue(FrameworkElement.DataContextProperty)
                ?? target.GetValue(FrameworkContentElement.DataContextProperty);
        }

        static readonly Dictionary&lt;Type, Delegate&gt; _dummyHandlers = new Dictionary&lt;Type, Delegate&gt;();

        static Delegate GetDummyHandler(Type eventHandlerType)
        {
            Delegate handler;
            if (!_dummyHandlers.TryGetValue(eventHandlerType, out handler))
            {
                handler = CreateDummyHandler(eventHandlerType);
                _dummyHandlers[eventHandlerType] = handler;
            }
            return handler;
        }

        static Delegate CreateDummyHandler(Type eventHandlerType)
        {
            var parameterTypes = GetParameterTypes(eventHandlerType);
            var returnType = eventHandlerType.GetMethod(&quot;Invoke&quot;).ReturnType;
            var dm = new DynamicMethod(&quot;DummyHandler&quot;, returnType, parameterTypes);
            var il = dm.GetILGenerator();
            if (returnType != typeof(void))
            {
                if (returnType.IsValueType)
                {
                    var local = il.DeclareLocal(returnType);
                    il.Emit(OpCodes.Ldloca_S, local);
                    il.Emit(OpCodes.Initobj, returnType);
                    il.Emit(OpCodes.Ldloc_0);
                }
                else
                {
                    il.Emit(OpCodes.Ldnull);
                }
            }
            il.Emit(OpCodes.Ret);
            return dm.CreateDelegate(eventHandlerType);
        }

        private void SubscribeToDataContextChanged(DependencyObject targetObj)
        {
            DependencyPropertyDescriptor
                .FromProperty(FrameworkElement.DataContextProperty, targetObj.GetType())
                .AddValueChanged(targetObj, TargetObject_DataContextChanged);
        }

        private void UnsubscribeFromDataContextChanged(DependencyObject targetObj)
        {
            DependencyPropertyDescriptor
                .FromProperty(FrameworkElement.DataContextProperty, targetObj.GetType())
                .RemoveValueChanged(targetObj, TargetObject_DataContextChanged);
        }

        private void TargetObject_DataContextChanged(object sender, EventArgs e)
        {
            DependencyObject targetObj = sender as DependencyObject;
            if (targetObj == null)
                return;

            object dataContext = GetDataContext(targetObj);
            if (dataContext == null)
                return;

            var handler = GetHandler(dataContext, _eventInfo, EventHandlerName);
            if (handler != null)
            {
                _eventInfo.AddEventHandler(targetObj, handler);
            }
            UnsubscribeFromDataContextChanged(targetObj);
        }

        #endregion
    }
</pre></p>
<p>Voilà donc un exemple du genre de choses qu&#8217;on peut faire grâce à cette nouvelle fonctionnalité de WPF. On pourrait aussi imaginer un système de &#8220;behavior&#8221; similaire à ce qu&#8217;on peut faire avec des propriétés attachées, par exemple pour réaliser une action standard lorsqu&#8217;un évènement se produit. Les possiblités sont sans doute nombreuses, je vous laisse le soin de les trouver <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/net-4-5/'>.NET 4.5</a>, <a href='http://tomlev.wordpress.com/tag/evenements/'>évènements</a>, <a href='http://tomlev.wordpress.com/tag/markup-extension/'>markup extension</a>, <a href='http://tomlev.wordpress.com/tag/wpf/'>WPF</a>, <a href='http://tomlev.wordpress.com/tag/xaml/'>XAML</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/447/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/447/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/447/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/447/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/447/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/447/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/447/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/447/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/447/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/447/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/447/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/447/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/447/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/447/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=447&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2011/09/23/wpf-4-5-abonnement-a-un-evenement-a-l%e2%80%99aide-d%e2%80%99une-markup-extension/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>Récursion terminale en C#</title>
		<link>http://tomlev.wordpress.com/2011/08/30/rcursion-terminale-en-c/</link>
		<comments>http://tomlev.wordpress.com/2011/08/30/rcursion-terminale-en-c/#comments</comments>
		<pubDate>Tue, 30 Aug 2011 09:45:36 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[récursion terminale]]></category>
		<category><![CDATA[tail recursion]]></category>
		<category><![CDATA[trampoline]]></category>

		<guid isPermaLink="false">https://tomlev.wordpress.com/?p=418</guid>
		<description><![CDATA[Quel que soit le langage de programmation utilisé, certains traitements s’implémentent naturellement sous forme d’un algorithme récursif (même si ce n’est pas toujours la solution la plus optimale). Le problème de l’approche récursive, c’est qu’elle consomme potentiellement beaucoup d’espace sur la pile : à partir d’un certain niveau de “profondeur” de la récursion, l’espace alloué [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=418&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Quel que soit le langage de programmation utilisé, certains traitements s’implémentent naturellement sous forme d’un algorithme récursif (même si ce n’est pas toujours la solution la plus optimale). Le problème de l’approche récursive, c’est qu’elle consomme potentiellement beaucoup d’espace sur la pile : à partir d’un certain niveau de “profondeur” de la récursion, l’espace alloué pour la pile d’exécution du thread est épuisé, et on obtient une erreur de type “débordement de la pile” (<code>StackOverflowException</code> en .NET).</p>
<p><span id="more-418"></span></p>
<h6><strong>Récursion terminale ? Mais qu’est-ce que c’est ?</strong></h6>
<p>Certains langages, notamment les langages fonctionnels, proposent nativement une optimisation appelée “<a href="http://fr.wikipedia.org/wiki/R%C3%A9cursion_terminale" target="_blank">récursion terminale</a>” (<em>tail recursion</em> en anglais). Le principe est que si l’appel récursif est la dernière instruction d’une fonction, il n’est pas nécessaire de conserver sur la pile le contexte de l’appel courant, puisqu’on aura pas à y revenir : il suffit de remplacer les paramètres par leurs nouvelles valeurs, et de reprendre l’exécution au début de la fonction. La récursion est donc transformée en itération, si bien qu’on ne risque plus de provoquer un débordement de la pile. Le concept étant assez nouveau pour moi, je ne vais pas faire un cours complet sur la récursion terminale… des personnes beaucoup plus compétentes s’en sont déjà chargées ! Je vous suggère de suivre le lien Wikipedia ci-dessus, qui est un bon point de départ pour comprendre cette notion.</p>
<p>En C#, le compilateur n’implémente malheureusement pas la récursion terminale, ce qui est un peu dommage dans la mesure où <a href="http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.tailcall.aspx" target="_blank">le CLR le supporte</a>… Pourtant, tout n’est pas perdu ! Certaines personnes ont eu une idée très astucieuse pour résoudre le problème : une technique appelée “trampoline” (parce qu’elle fait “rebondir” la fonction) qui permet de transformer très facilement un algorithme récursif en algorithme itératif. Samuel Jack explique très bien le concept <a href="http://blog.functionalfun.net/2008/04/bouncing-on-your-tail.html" target="_blank">sur son blog</a> (en anglais). Dans la suite de cet article, on verra comment appliquer cette technique à un algorithme simple, en utilisant la classe présentée dans l’article de Samuel Jack ; et pour finir, je présenterai une autre implémentation d’un trampoline, qui me semble un peu plus souple.</p>
<h6><strong>Un cas d’utilisation en C#</strong></h6>
<p>Voyons donc comment on peut transformer un algorithme récursif simple, comme le calcul de la factorielle d’un nombre, en un algorithme qui utilise la récursion terminale (soit dit en passant, on peut calculer la factorielle beaucoup plus efficacement avec un algorithme non récursif, mais pour l’exemple on fera comme si on ne le savait pas…). L’algorithme qui découle directement de la définition est le suivant :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:edbd1c2f-fc41-42fb-9e47-ac267557a1ac" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: csharp;">
BigInteger Factorial(int n)
{
    if (n &lt; 2)
        return 1;
    return n * Factorial(n - 1);
}
</pre>
</pre>
</div>
<p>(Notez l’utilisation de <code>BigInteger</code> : si on veut pousser la récursion assez loin pour observer les effets de la récursion terminale, le résultat dépassera largement la capacité d’un <code>long</code>)</p>
<p>Si on appelle cette méthode avec une grande valeur (aux alentours de 20000 sur ma machine), on obtient l’exception à laquelle on pouvait s’attendre : <code>StackOverflowException</code>. On a “empilé” tellement d’appels imbriqués à la méthode <code>Factorial</code> qu’on a épuisé la capacité de la pile. On va donc essayer de modifier le code pour pouvoir utiliser la récursion terminale.</p>
<p>Comme mentionné plus haut, il faut que la dernière instruction de la méthode soit l’appel récursif à elle-même. Cela <em>semble</em> être le cas ici… mais en fait non : la dernière opération exécutée sera la multiplication, qui ne peut pas être effectuée avant de connaitre le résultat de <code>Factorial(n-1)</code>. Il faut donc repenser un peu la méthode, pour qu’elle se termine par un appel à elle-même avec des paramètres différents. Pour arriver à ce résultat, on va ajouter un paramètre <code>product</code>, qui va jouer le rôle d’accumulateur :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:5986a601-3c8c-4da9-8eaf-bc7887e0b638" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: csharp;">
BigInteger Factorial(int n, BigInteger product)
{
    if (n &lt; 2)
        return product;
    return Factorial(n - 1, n * product);
}
</pre>
</pre>
</div>
<p>Pour le premier appel, il suffira de passer 1 comme valeur initiale de l’accumulateur.</p>
<p>On a donc maintenant une méthode qui remplit les critères pour la récursion terminale : l’appel récursif à <code>Factorial</code> est bien la dernière opération effectuée par la méthode. Une fois qu’on a mis l’algorithme sous cette forme, la transformation finale pour appliquer la récursion terminale à l’aide du trampoline de Samuel Jack est triviale :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:56bd5252-dc4f-44bb-8471-19d60e3ea817" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: csharp;">
Bounce&lt;int, BigInteger, BigInteger&gt; Factorial(int n, BigInteger product)
{
    if (n &lt; 2)
        return Trampoline.ReturnResult&lt;int, BigInteger, BigInteger&gt;(product);
    return Trampoline.Recurse&lt;int, BigInteger, BigInteger&gt;(n - 1, n * product);
}
</pre>
</pre>
</div>
<ul>
<li>Le renvoi de la valeur finale est remplacé par un appel à <code>Trampoline.ReturnResult</code>, qui indique au trampoline que le calcul est terminé avec le résultat indiqué </li>
<li>L’appel récursif à <code>Factorial</code> est remplacé par un appel à <code>Trampoline.Recurse</code>, qui indique les paramètres à utiliser pour le prochain appel de la méthode </li>
</ul>
<p>Cette méthode n’est pas directement utilisable, puisqu’elle renvoie un objet <code>Bounce</code> dont on ne sait pas trop quoi faire… Pour l’exécuter, on utilise la méthode <code>Trampoline.MakeTrampoline</code>, qui renvoie une nouvelle fonction qui applique la récursion terminale. On peut ensuite exécuter cette fonction directement :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:f2e7878b-cc05-4b99-b6c9-e1804297db44" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: csharp;">
    Func&lt;int, BigInteger, BigInteger&gt; fact = Trampoline.MakeTrampoline&lt;int, BigInteger, BigInteger&gt;(Factorial);
    BigInteger result = fact(50000, 1);
</pre>
</pre>
</div>
<p>On peut maintenant calculer la factorielle d’un grand nombre sans risque de “faire sauter la pile”… bien sûr, ça reste assez lent : comme mentionné plus haut, ce n’est pas du tout la façon optimale de calculer une factorielle, et de plus les opérations avec <code>BigInteger</code> sont assez lourdes. </p>
<h6><strong>Peut-on faire mieux ?</strong></h6>
<p>Vous vous doutez bien que si je pose la question, c’est que la réponse est oui… L’implémentation du trampoline présentée ci-dessus remplit parfaitement son rôle, mais personnellement je trouve qu’elle manque de souplesse :</p>
<ul>
<li>Elle ne fonctionne qu’avec 2 paramètres (on peut bien sûr l’adapter pour un nombre différent de paramètres, mais il faut à chaque fois créer de nouvelles méthodes avec les signatures adéquates) </li>
<li>L’écriture est assez lourde : il y a 3 arguments de type, qu’il faut préciser à chaque fois parce que l’inférence de type n’a pas tous les éléments nécessaires pour les déterminer automatiquement </li>
<li>Le fait que <code>MakeTrampoline</code> renvoie une nouvelle fonction ne me semble pas très utile, il serait plus intuitif d’avoir une méthode <code>Execute</code> qui exécute directement la fonction </li>
</ul>
<p>Et pour finir, je trouve que la terminologie, bien qu’amusante, n’est pas très explicite…</p>
<p>J’ai donc cherché à améliorer ce système pour le rendre plus pratique, en utilisant des méthodes anonymes sous forme d’expressions lambda. Il n’y a plus qu’un seul argument de type (le type de retour), et on passe les paramètres par closure dans une expression lambda. Voilà ce que donne la nouvelle version de la méthode <code>Factorial</code> avec mon implémentation :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:10d88ccc-bed2-4273-a74f-1754e13618ed" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: csharp; pad-line-numbers: true;">
RecursionResult&lt;BigInteger&gt; Factorial(int n, BigInteger product)
{
    if (n &lt; 2)
        return TailRecursion.Return(product);
    return TailRecursion.Next(() =&gt; Factorial(n - 1, n * product));
}
</pre>
</pre>
</div>
<p>Et on l’utilise comme ceci :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:51676e03-ebad-4a0f-98fb-c76546986b50" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: csharp;">
BigInteger result = TailRecursion.Execute(() =&gt; Factorial(50000, 1));
</pre>
</pre>
</div>
<p>C’est plus souple, plus concis, et plus lisible… enfin il me semble en tous cas<img style="border-style:none;" class="wlEmoticon wlEmoticon-smile" alt="Sourire" src="http://tomlev.files.wordpress.com/2011/08/wlemoticon-smile.png?w=780" />. Le revers de la médaille, c’est que performances sont légèrement moins bonnes (environ 20% plus long pour calculer la factorielle de 50000), probablement à cause du delegate qui est créé à chaque niveau de récursion.</p>
<p>Voici le code complet de la classe <code>TailRecursion</code> :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:90ac2501-1d9c-455b-9154-c79e986d0eb0" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: csharp;">
public static class TailRecursion
{
    public static T Execute&lt;T&gt;(Func&lt;RecursionResult&lt;T&gt;&gt; func)
    {
        do
        {
            var recursionResult = func();
            if (recursionResult.IsFinalResult)
                return recursionResult.Result;
            func = recursionResult.NextStep;
        } while (true);
    }
    
    public static RecursionResult&lt;T&gt; Return&lt;T&gt;(T result)
    {
        return new RecursionResult&lt;T&gt;(true, result, null);
    }
    
    public static RecursionResult&lt;T&gt; Next&lt;T&gt;(Func&lt;RecursionResult&lt;T&gt;&gt; nextStep)
    {
        return new RecursionResult&lt;T&gt;(false, default(T), nextStep);
    }

}

public class RecursionResult&lt;T&gt;
{
    private readonly bool _isFinalResult;
    private readonly T _result;
    private readonly Func&lt;RecursionResult&lt;T&gt;&gt; _nextStep;
    internal RecursionResult(bool isFinalResult, T result, Func&lt;RecursionResult&lt;T&gt;&gt; nextStep)
    {
        _isFinalResult = isFinalResult;
        _result = result;
        _nextStep = nextStep;
    }
    
    public bool IsFinalResult { get { return _isFinalResult; } }
    public T Result { get { return _result; } }
    public Func&lt;RecursionResult&lt;T&gt;&gt; NextStep { get { return _nextStep; } }
}
</pre>
</pre>
</div>
<h6><strong>Peut-on aller plus loin ?</strong></h6>
<p>Certainement… mais ça devient de la haute voltige ! Comme je le disais plus haut, le CLR supporte la récursion terminale, via l’instruction IL <code>tail</code>. L’idéal serait que le compilateur C# sache générer cette instruction quand une méthode est éligible à la récursion terminale, mais ce n’est malheureusement pas le cas, et je ne pense pas qu’il faille s’attendre à ce que ce soit géré dans une prochaine version, car la demande pour cette fonctionnalité semble assez faible.</p>
<p>En attendant, on peut tricher un peu, en aidant le compilateur à faire son travail : le .NET framework fournit des outils nommés <a href="http://msdn.microsoft.com/en-us/library/f7dy01k1.aspx">ildasm</a> (désassembleur IL) et <a href="http://msdn.microsoft.com/en-us/library/496e4ekx.aspx">ilasm</a> (assembleur IL), avec lesquels on peut s’amuser un peu… Reprenons notre implémentation récursive classique qui n’utilise pas encore la récursion terminale :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:bca6538c-f9d2-43e5-8d95-7b85e3b5459e" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: csharp;">
	static BigInteger Factorial(int n, BigInteger product)
	{
		if (n &lt; 2)
			return product;
		return Factorial(n - 1, n * product);
	}
</pre>
</pre>
</div>
<p>Si on compile ce code, et qu’on le désassemble avec idlasm, on obtient le code IL suivant :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:23c6c216-9f5b-44a5-aa1d-dd05ab7a1cf0" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: plain; wrap-lines: false;">
  .method private hidebysig static valuetype [System.Numerics]System.Numerics.BigInteger 
          Factorial(int32 n,
                    valuetype [System.Numerics]System.Numerics.BigInteger product) cil managed
  {
    // Code size       41 (0x29)
    .maxstack  3
    .locals init (valuetype [System.Numerics]System.Numerics.BigInteger V_0,
             bool V_1)
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldc.i4.2
    IL_0003:  clt
    IL_0005:  ldc.i4.0
    IL_0006:  ceq
    IL_0008:  stloc.1
    IL_0009:  ldloc.1
    IL_000a:  brtrue.s   IL_0010

    IL_000c:  ldarg.1
    IL_000d:  stloc.0
    IL_000e:  br.s       IL_0027

    IL_0010:  ldarg.0
    IL_0011:  ldc.i4.1
    IL_0012:  sub
    IL_0013:  ldarg.0
    IL_0014:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Implicit(int32)
    IL_0019:  ldarg.1
    IL_001a:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Multiply(valuetype [System.Numerics]System.Numerics.BigInteger,
                                                                                                                                        valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_001f:  call       valuetype [System.Numerics]System.Numerics.BigInteger Program::Factorial(int32,
                                                                                                  valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_0024:  stloc.0
    IL_0025:  br.s       IL_0027

    IL_0027:  ldloc.0
    IL_0028:  ret
  } // end of method Program::Factorial
</pre>
</pre>
</div>
<p>Ca pique un peu les yeux si on a pas l’habitude de lire du code IL, mais on arrive à peu près à voir ce qui se passe… L’appel récursif se situe à l’adresse <code>IL_001f</code>, c’est à ce niveau là qu’on va pouvoir intervenir. Si on lit la documentation de l’instruction <code>tail</code>, on apprend qu’elle doit précéder immédiatement une instruction <code>call</code>, et que l’instruction suivant le <code>call</code> doit être <code>ret</code> (retour de la méthode). Pour l’instant, il y a plusieurs instructions après le <code>call</code>, parce que le compilateur a généré une variable locale pour stocker la valeur de retour. Il faut juste modifier légèrement le code pour ne plus utiliser cette variable, et ajouter l’instruction <code>tail</code> au bon endroit :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:bb6192b4-efc9-4317-8809-ad6b58872a3e" class="wlWriterEditableSmartContent">
<pre style="white-space:normal;">
<pre class="brush: plain; wrap-lines: false;">
  .method private hidebysig static valuetype [System.Numerics]System.Numerics.BigInteger 
          Factorial(int32 n,
                    valuetype [System.Numerics]System.Numerics.BigInteger product) cil managed
  {
    // Code size       41 (0x29)
    .maxstack  3
    .locals init (valuetype [System.Numerics]System.Numerics.BigInteger V_0,
             bool V_1)
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldc.i4.2
    IL_0003:  clt
    IL_0005:  ldc.i4.0
    IL_0006:  ceq
    IL_0008:  stloc.1
    IL_0009:  ldloc.1
    IL_000a:  brtrue.s   IL_0010

    IL_000c:  ldarg.1
    IL_000d:  ret		// Return directly instead of storing the result in V_0
    IL_000e:  nop

    IL_0010:  ldarg.0
    IL_0011:  ldc.i4.1
    IL_0012:  sub
    IL_0013:  ldarg.0
    IL_0014:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Implicit(int32)
    IL_0019:  ldarg.1
    IL_001a:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Multiply(valuetype [System.Numerics]System.Numerics.BigInteger,
                                                                                                                                        valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_001f:  tail.
    IL_0020:  call       valuetype [System.Numerics]System.Numerics.BigInteger Program::Factorial(int32,
                                                                                                  valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_0025:  ret		// Return directly instead of storing the result in V_0

  } // end of method Program::Factorial
</pre>
</pre>
</div>
<p>On réassemble ce code avec ilasm, et on obtient un nouvel exécutable, qui s’exécute sans problème même avec des valeurs pour lesquelles la version classique aurait planté depuis longtemps <img style="border-style:none;" class="wlEmoticon wlEmoticon-smile" alt="Sourire" src="http://tomlev.files.wordpress.com/2011/08/wlemoticon-smile.png?w=780" />. Les performances sont également très bonnes : environ 3 fois plus rapide qu’avec la classe Trampoline. Si on compare les performances avec des valeurs plus petites, on remarque que c’est également 3 fois plus rapide que la version classique sans récursion terminale. </p>
<p>Bien sûr, c’est n’est qu’une preuve de concept… il ne semble pas très réaliste d&#8217;effectuer cette opération manuellement dans un “vrai” projet. En revanche, il serait probablement possible de créer un outil qui modifie l’assembly automatiquement après la compilation pour ajouter la récursion terminale.</p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/c/'>C#</a>, <a href='http://tomlev.wordpress.com/tag/recursion-terminale/'>récursion terminale</a>, <a href='http://tomlev.wordpress.com/tag/tail-recursion/'>tail recursion</a>, <a href='http://tomlev.wordpress.com/tag/trampoline/'>trampoline</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/418/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/418/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/418/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/418/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/418/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/418/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/418/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/418/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/418/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/418/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/418/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/418/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/418/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/418/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=418&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2011/08/30/rcursion-terminale-en-c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2011/08/wlemoticon-smile.png" medium="image">
			<media:title type="html">Sourire</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2011/08/wlemoticon-smile.png" medium="image">
			<media:title type="html">Sourire</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Afficher une image GIF animée</title>
		<link>http://tomlev.wordpress.com/2011/03/27/wpf-afficher-une-image-gif-animee/</link>
		<comments>http://tomlev.wordpress.com/2011/03/27/wpf-afficher-une-image-gif-animee/#comments</comments>
		<pubDate>Sun, 27 Mar 2011 20:28:32 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[WPF]]></category>
		<category><![CDATA[animé]]></category>
		<category><![CDATA[gif]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=406</guid>
		<description><![CDATA[WPF est une technologie géniale, mais parfois on a l&#8217;impression qu&#8217;il lui manque certaines fonctionnalités assez basiques&#8230; Un exemple souvent cité est l&#8217;absence de support pour les images GIF animées. En fait, le format GIF proprement dit est supporté, mais le contrôle Image n&#8217;affiche que la première image de l&#8217;animation. De nombreuses solutions à ce [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=406&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>WPF est une technologie géniale, mais parfois on a l&#8217;impression qu&#8217;il lui manque certaines fonctionnalités assez basiques&#8230; Un exemple souvent cité est l&#8217;absence de support pour les images GIF animées. En fait, le format GIF proprement dit est supporté, mais le contrôle <code>Image</code> n&#8217;affiche que la première image de l&#8217;animation.</p>
<p>De nombreuses solutions à ce problème ont été proposées sur les forums et blogs techniques, généralement des variantes autour des approches suivantes :</p>
<ul>
<li>Utiliser le contrôle <code>MediaElement</code> : malheureusement ce contrôle ne supporte que les URI de type <code>file://</code> ou <code>http://</code>, et non le schéma d&#8217;URI <code>pack://</code> utilisé pour les ressources WPF ; l&#8217;image ne peut donc pas être inclue dans les ressources, elle doit être dans un fichier à part. De plus, la transparence n&#8217;est pas supportée, si bien que le résultat final est assez laid</li>
<li>Utiliser le contrôle <code>PictureBox</code> de Windows Forms, via un <code>WindowsFormsHost</code> : personnellement j&#8217;ai horreur d&#8217;utiliser des contrôles Windows Forms en WPF, ça me donne l&#8217;impression de faire quelque chose de mal <img src='http://s2.wp.com/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </li>
<li>Créer un contrôle dérivé de <code>Image</code> qui gère l&#8217;animation. Pour l&#8217;implémentation, certaines solutions tirent partie de la classe <code>ImageAnimator</code> de <code>System.Drawing</code> (GDI), d&#8217;autres utilisent une animation WPF pour changer de frame. C&#8217;est une approche assez &#8220;propre&#8221;, mais qui oblige à utiliser un contrôle spécifique pour les GIF. De plus la solution utilisant <code>ImageAnimator</code> se révèle assez peu fluide.</li>
</ul>
<p>Comme vous l&#8217;aurez deviné, aucune de ces solutions ne me satisfait vraiment&#8230; De plus, aucune ne gère proprement la durée de chaque frame, et suppose simplement que toutes les frames durent 100ms (c&#8217;est presque toujours le cas, mais le <em>presque</em> fait toute la différence&#8230;). Je n&#8217;ai donc gardé que les meilleures idées dans les approches ci-dessus pour créer ma propre solution. Les objectifs que je souhaitais atteindre sont les suivants :</p>
<ul>
<li>Ne pas dépendre de Windows Forms ou de GDI</li>
<li>Afficher l&#8217;image animée dans un contrôle <code>Image</code> standard</li>
<li>Pouvoir utiliser le même code XAML pour une image fixe ou animée</li>
<li>Supporter la transparence</li>
<li>Tenir compte de la durée réelle de chaque frame de l&#8217;image</li>
</ul>
<p>Pour arriver à ce résultat, je suis parti d&#8217;une idée simple, voire évidente : pour animer l&#8217;image, il suffit d&#8217;appliquer une animation à la propriété <code>Source</code> du contrôle <code>Image</code>. Or WPF fournit tous les outils nécessaires pour réaliser ce type d&#8217;animation ; en l&#8217;occurrence la classe <a href="http://msdn.microsoft.com/fr-fr/library/system.windows.media.animation.objectanimationusingkeyframes.aspx"><code>ObjectAnimationUsingKeyFrames</code></a> répond parfaitement au besoin : on peut spécifier à quel instant exact affecter une valeur donnée à la propriété, ce qui permet de tenir compte de la durée des frames.</p>
<p>Le problème suivant est d&#8217;extraire les différentes frames de l&#8217;image : heureusement ce scénario est prévu dans WPF, et la classe <a href="http://msdn.microsoft.com/fr-fr/library/system.windows.media.imaging.bitmapdecoder.aspx"><code>BitmapDecoder</code></a> fournit une propriété <code>Frames</code> qui sert à ça. Donc, pas de difficulté majeure à ce niveau&#8230;</p>
<p>Enfin, dernier obstacle : extraire la durée de chaque frame. C&#8217;est finalement la partie qui m&#8217;a demandé le plus de recherche&#8230; J&#8217;ai d&#8217;abord cru qu&#8217;il faudrait lire manuellement le fichier pour trouver cette information, en décodant directement les données binaires. Mais la solution est finalement assez simple, et tire partie de la classe <a href="http://msdn.microsoft.com/fr-fr/library/system.windows.media.imaging.bitmapmetadata.aspx"><code>BitmapMetadata</code></a>. La seule difficulté a été de localiser le &#8220;chemin&#8221; de la métadonnée qui contient cette information, mais après quelques tâtonnements, la voilà : <code>/grctlext/Delay</code>.</p>
<p>La solution finale est implémentée sous forme d&#8217;une propriété attachée <code>AnimatedSource</code> applicable au contrôle <code>Image</code>, qui s&#8217;utilise en lieu et place de <code>Source</code> :</p>
<p><pre class="brush: xml;">&lt;Image Stretch=&quot;None&quot; my:ImageBehavior.AnimatedSource=&quot;/Images/animation.gif&quot; /&gt;</pre></p>
<p>On peut également affecter une image fixe à cette propriété, elle s&#8217;affichera normalement ; on peut donc utiliser cette propriété sans se soucier de savoir si l&#8217;image à afficher sera fixe ou animée.</p>
<p>Au final, tous les objectifs fixés au départ sont donc atteints, et il y a même une cerise sur le gâteau : cette solution fonctionne également dans le designer (du moins dans Visual Studio 2010), on voit donc directement l&#8217;animation quand on affecte la propriété <code>AnimatedSource</code> <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Sans plus attendre, voilà le code complet :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    public static class ImageBehavior
    {
        #region AnimatedSource

        [AttachedPropertyBrowsableForType(typeof(Image))]
        public static ImageSource GetAnimatedSource(Image obj)
        {
            return (ImageSource)obj.GetValue(AnimatedSourceProperty);
        }

        public static void SetAnimatedSource(Image obj, ImageSource value)
        {
            obj.SetValue(AnimatedSourceProperty, value);
        }

        public static readonly DependencyProperty AnimatedSourceProperty =
            DependencyProperty.RegisterAttached(
              &quot;AnimatedSource&quot;,
              typeof(ImageSource),
              typeof(ImageBehavior),
              new UIPropertyMetadata(
                null,
                AnimatedSourceChanged));

        private static void AnimatedSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            Image imageControl = o as Image;
            if (imageControl == null)
                return;

            var oldValue = e.OldValue as ImageSource;
            var newValue = e.NewValue as ImageSource;
            if (oldValue != null)
            {
                imageControl.BeginAnimation(Image.SourceProperty, null);
            }
            if (newValue != null)
            {
                imageControl.DoWhenLoaded(InitAnimationOrImage);
            }
        }

        private static void InitAnimationOrImage(Image imageControl)
        {
            BitmapSource source = GetAnimatedSource(imageControl) as BitmapSource;
            if (source != null)
            {
                var decoder = GetDecoder(source) as GifBitmapDecoder;
                if (decoder != null &amp;&amp; decoder.Frames.Count &gt; 1)
                {
                    var animation = new ObjectAnimationUsingKeyFrames();
                    var totalDuration = TimeSpan.Zero;
                    BitmapSource prevFrame = null;
                    FrameInfo prevInfo = null;
                    foreach (var rawFrame in decoder.Frames)
                    {
                        var info = GetFrameInfo(rawFrame);
                        var frame = MakeFrame(
                            source,
                            rawFrame, info,
                            prevFrame, prevInfo);

                        var keyFrame = new DiscreteObjectKeyFrame(frame, totalDuration);
                        animation.KeyFrames.Add(keyFrame);
                        
                        totalDuration += info.Delay;
                        prevFrame = frame;
                        prevInfo = info;
                    }
                    animation.Duration = totalDuration;
                    animation.RepeatBehavior = RepeatBehavior.Forever;
                    if (animation.KeyFrames.Count &gt; 0)
                        imageControl.Source = (ImageSource)animation.KeyFrames[0].Value;
                    else
                        imageControl.Source = decoder.Frames[0];
                    imageControl.BeginAnimation(Image.SourceProperty, animation);
                    return;
                }
            }
            imageControl.Source = source;
            return;
        }

        private static BitmapDecoder GetDecoder(BitmapSource image)
        {
            BitmapDecoder decoder = null;
            var frame = image as BitmapFrame;
            if (frame != null)
                decoder = frame.Decoder;

            if (decoder == null)
            {
                var bmp = image as BitmapImage;
                if (bmp != null)
                {
                    if (bmp.StreamSource != null)
                    {
                        decoder = BitmapDecoder.Create(bmp.StreamSource, bmp.CreateOptions, bmp.CacheOption);
                    }
                    else if (bmp.UriSource != null)
                    {
                        Uri uri = bmp.UriSource;
                        if (bmp.BaseUri != null &amp;&amp; !uri.IsAbsoluteUri)
                            uri = new Uri(bmp.BaseUri, uri);
                        decoder = BitmapDecoder.Create(uri, bmp.CreateOptions, bmp.CacheOption);
                    }
                }
            }

            return decoder;
        }

        private static BitmapSource MakeFrame(
            BitmapSource fullImage,
            BitmapSource rawFrame, FrameInfo frameInfo,
            BitmapSource previousFrame, FrameInfo previousFrameInfo)
        {
            DrawingVisual visual = new DrawingVisual();
            using (var context = visual.RenderOpen())
            {
                if (previousFrameInfo != null &amp;&amp; previousFrame != null &amp;&amp;
                    previousFrameInfo.DisposalMethod == FrameDisposalMethod.Combine)
                {
                    var fullRect = new Rect(0, 0, fullImage.PixelWidth, fullImage.PixelHeight);
                    context.DrawImage(previousFrame, fullRect);
                }

                context.DrawImage(rawFrame, frameInfo.Rect);
            }
            var bitmap = new RenderTargetBitmap(
                fullImage.PixelWidth, fullImage.PixelHeight,
                fullImage.DpiX, fullImage.DpiY,
                PixelFormats.Pbgra32);
            bitmap.Render(visual);
            return bitmap;
        }

        private class FrameInfo
        {
            public TimeSpan Delay { get; set; }
            public FrameDisposalMethod DisposalMethod { get; set; }
            public double Width { get; set; }
            public double Height { get; set; }
            public double Left { get; set; }
            public double Top { get; set; }

            public Rect Rect
            {
                get { return new Rect(Left, Top, Width, Height); }
            }
        }

        private enum FrameDisposalMethod
        {
            Replace = 0,
            Combine = 1,
            RestoreBackground = 2,
            RestorePrevious = 3
        }

        private static FrameInfo GetFrameInfo(BitmapFrame frame)
        {
            var frameInfo = new FrameInfo
            {
                Delay = TimeSpan.FromMilliseconds(100),
                DisposalMethod = FrameDisposalMethod.Replace,
                Width = frame.PixelWidth,
                Height = frame.PixelHeight,
                Left = 0,
                Top = 0
            };

            BitmapMetadata metadata;
            try
            {
                metadata = frame.Metadata as BitmapMetadata;
                if (metadata != null)
                {
                    const string delayQuery = &quot;/grctlext/Delay&quot;;
                    const string disposalQuery = &quot;/grctlext/Disposal&quot;;
                    const string widthQuery = &quot;/imgdesc/Width&quot;;
                    const string heightQuery = &quot;/imgdesc/Height&quot;;
                    const string leftQuery = &quot;/imgdesc/Left&quot;;
                    const string topQuery = &quot;/imgdesc/Top&quot;;

                    var delay = metadata.GetQueryOrNull&lt;ushort&gt;(delayQuery);
                    if (delay.HasValue)
                        frameInfo.Delay = TimeSpan.FromMilliseconds(10 * delay.Value);

                    var disposal = metadata.GetQueryOrNull&lt;byte&gt;(disposalQuery);
                    if (disposal.HasValue)
                        frameInfo.DisposalMethod = (FrameDisposalMethod) disposal.Value;

                    var width = metadata.GetQueryOrNull&lt;ushort&gt;(widthQuery);
                    if (width.HasValue)
                        frameInfo.Width = width.Value;

                    var height = metadata.GetQueryOrNull&lt;ushort&gt;(heightQuery);
                    if (height.HasValue)
                        frameInfo.Height = height.Value;

                    var left = metadata.GetQueryOrNull&lt;ushort&gt;(leftQuery);
                    if (left.HasValue)
                        frameInfo.Left = left.Value;

                    var top = metadata.GetQueryOrNull&lt;ushort&gt;(topQuery);
                    if (top.HasValue)
                        frameInfo.Top = top.Value;
                }
            }
            catch (NotSupportedException)
            {
            }

            return frameInfo;
        }

        private static T? GetQueryOrNull&lt;T&gt;(this BitmapMetadata metadata, string query)
            where T : struct
        {
            if (metadata.ContainsQuery(query))
            {
                object value = metadata.GetQuery(query);
                if (value != null)
                    return (T) value;
            }
            return null;
        }

        #endregion
    }
</pre></p>
<p>Et voici la méthode d&#8217;extension <code>DoWhenLoaded</code> utilisée dans le code ci-dessus :</p>
<p><pre class="brush: csharp;">public static void DoWhenLoaded&lt;T&gt;(this T element, Action&lt;T&gt; action)
    where T : FrameworkElement
{
    if (element.IsLoaded)
    {
        action(element);
    }
    else
    {
        RoutedEventHandler handler = null;
        handler = (sender, e) =&gt;
        {
            element.Loaded -= handler;
            action(element);
        };
        element.Loaded += handler;
    }
}</pre></p>
<p>Cette classe sera inclue dans la prochaine version de la librairie <a href="http://dvp-net.developpez.com/">Dvp.NET</a>, dont j&#8217;avais <a href="http://tomlev.wordpress.com/2010/03/18/dvp-net-premiere-version-de-la-librairie-dvp-net/">déjà parlé il y quelque temps</a>.</p>
<p><strong>Mise à jour</strong> : le code qui récupère la durée d&#8217;une frame ne fonctionne que sous Windows Seven, et sous Windows Vista si la <a href="http://support.microsoft.com/kb/971644/fr-fr">Platform Update</a> est installée (non testé). La durée par défaut (100ms) sera utilisée sur les autres versions de Windows. Je mettrai à jour l&#8217;article si je trouve une solution qui fonctionne sur tous les systèmes (je sais que je pourrais utiliser <code>System.Drawing.Bitmap</code>, mais je préfèrerais éviter&#8230;)</p>
<p><strong>Mise à jour 2</strong> : comme Klaus l&#8217;a signalé dans un commentaire sur la version anglaise de mon blog, la classe <code>ImageBehavior</code> ne gérait pas certains attributs importants des frames : la méthode de destruction (est-ce qu&#8217;une frame doit simplement remplacer la frame précédente, ou être combinée avec elle), et la position des frames (Left/Top/Width/Height). J&#8217;ai mis à jour le code pour gérer ces attributs correctement. Merci Klaus !</p>
<p><strong>Mise à jour 3</strong> : encore un petit bug corrigé, la récupération du décodeur à partir d&#8217;une URI relative ne fonctionnait pas. Merci à l&#8217;anonyme qui l&#8217;a signalé!</p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/anime/'>animé</a>, <a href='http://tomlev.wordpress.com/tag/gif/'>gif</a>, <a href='http://tomlev.wordpress.com/tag/wpf/'>WPF</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/406/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/406/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/406/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/406/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/406/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/406/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/406/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/406/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/406/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/406/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/406/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/406/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/406/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/406/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=406&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2011/03/27/wpf-afficher-une-image-gif-animee/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Comment faire un binding dans les cas o&#249; on n&#8217;h&#233;rite pas du DataContext</title>
		<link>http://tomlev.wordpress.com/2011/03/21/wpf-comment-faire-un-binding-dans-les-cas-o-on-nhrite-pas-du-datacontext/</link>
		<comments>http://tomlev.wordpress.com/2011/03/21/wpf-comment-faire-un-binding-dans-les-cas-o-on-nhrite-pas-du-datacontext/#comments</comments>
		<pubDate>Sun, 20 Mar 2011 23:03:54 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Astuces]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[binding]]></category>
		<category><![CDATA[datacontext]]></category>
		<category><![CDATA[freezable]]></category>

		<guid isPermaLink="false">https://tomlev.wordpress.com/?p=394</guid>
		<description><![CDATA[La propriété DataContext de WPF est extrêmement pratique, car elle est automatiquement héritée par tous les enfants de l’élément où elle est définie ; il n’est donc pas nécessaire de la redéfinir pour chaque élément qu’on veut lier aux données. Cependant, il arrive que le DataContext ne soit pas accessible pour certains éléments : c’est [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=394&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>La propriété <code>DataContext</code> de WPF est extrêmement pratique, car elle est automatiquement héritée par tous les enfants de l’élément où elle est définie ; il n’est donc pas nécessaire de la redéfinir pour chaque élément qu’on veut lier aux données. Cependant, il arrive que le <code>DataContext</code> ne soit pas accessible pour certains éléments : c’est le cas des éléments qui n’appartiennent pas à l’arbre visuel ni à l’arbre logique. Il devient alors très difficile de définir une propriété ce ces éléments par un binding&#8230;</p>
<p>Prenons un exemple simple : on veut afficher une liste de produits dans un <code>DataGrid</code>. Dans la grille, on veut pouvoir afficher où masquer la colonne du prix, en fonction d’une propriété <code>ShowPrice</code> exposée par le ViewModel. L’approche évidente consiste à binder la propriété <code>Visibility</code> de la colonne à la propriété <code>ShowPrice</code> :</p>
<p><pre class="brush: xml;">
&lt;DataGridTextColumn Header=&quot;Price&quot; Binding=&quot;{Binding Price}&quot; IsReadOnly=&quot;False&quot;
                    Visibility=&quot;{Binding ShowPrice,
                        Converter={StaticResource visibilityConverter}}&quot;/&gt;
</pre></p>
<p>Malheureusement, changer la valeur de la propriété <code>ShowPrice</code> n&#8217;a aucun effet, et la colonne reste toujours affichée&#8230; pourquoi ? Si on regarde la fenêtre de sortie de Visual Studio, on remarque la ligne suivante :</p>
<blockquote><p>System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ShowPrice; DataItem=null; target element is &#8216;DataGridTextColumn&#8217; (HashCode=32685253); target property is &#8216;Visibility&#8217; (type &#8216;Visibility&#8217;)</p></blockquote>
<p>Derrière cet obscur charabia se cache une explication toute simple : WPF ne sait pas quel <code>FrameworkElement</code> utiliser pour récupérer le <code>DataContext</code>, car la colonne n&#8217;appartient pas à l&#8217;arbre visuel ni à l&#8217;arbre logique du <code>DataGrid</code>.</p>
<p>On peut toujours essayer de &#8220;triturer&#8221; le binding pour obtenir le résultat voulu, par exemple en essayant de binder par rapport au <code>DataGrid</code> lui-même :</p>
<p><pre class="brush: xml;">
&lt;DataGridTextColumn Header=&quot;Price&quot; Binding=&quot;{Binding Price}&quot; IsReadOnly=&quot;False&quot;
                    Visibility=&quot;{Binding DataContext.ShowPrice,
                        Converter={StaticResource visibilityConverter},
                        RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}&quot;/&gt;
</pre></p>
<p>Ou encore, en ajoutant une <code>CheckBox</code> bindée sur <code>ShowPrice</code> et en essayant de binder la visibilité de la colonne sur la propriété <code>IsChecked</code>, en spécifiant le nom de l&#8217;élément :</p>
<p><pre class="brush: xml;">
&lt;DataGridTextColumn Header=&quot;Price&quot; Binding=&quot;{Binding Price}&quot; IsReadOnly=&quot;False&quot;
                    Visibility=&quot;{Binding IsChecked,
                        Converter={StaticResource visibilityConverter},
                        ElementName=chkShowPrice}&quot;/&gt;
</pre></p>
<p>Mais rien à faire, on obtient toujours le même résultat&#8230;</p>
<p>A ce stade, il semble que la seule approche qui pourrait marcher est de passer par le code-behind, ce qu&#8217;on préfère généralement éviter quand on suit le pattern MVVM&#8230; mais ce serait dommage d&#8217;abandonner aussi vite <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>La solution est en fait assez simple, et se base sur la classe <a href="http://msdn.microsoft.com/fr-fr/library/system.windows.freezable.aspx"><code>Freezable</code></a>. La vocation première de cette classe est de définir des objets qui ont un état modifiable et un état non modifiable. Mais en l&#8217;occurrence, la caractéristique qui nous intéresse est qu&#8217;un objet qui hérite de <code>Freezable</code> peut hériter du <code>DataContext</code>, bien qu&#8217;il ne s&#8217;agisse pas d&#8217;un élément visuel. Je ne connais pas le mécanisme exact qui permet d&#8217;obtenir ce comportement, mais toujours est-il que cela va nous permettre d&#8217;arriver au résultat voulu&#8230;</p>
<p>L&#8217;idée est de créer une classe, que j&#8217;ai appelée <code>BindingProxy</code>, qui hérite de <code>Freezable</code> et dans laquelle on va déclarer une dependency property <code>Data</code> :</p>
<p><pre class="brush: csharp;">
    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable

        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register(&quot;Data&quot;, typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }
</pre></p>
<p>On va ensuite déclarer une instance de cette classe dans les ressources du <code>DataGrid</code>, et binder la propriété <code>Data</code> sur le <code>DataContext</code> courant :</p>
<p><pre class="brush: xml;">
&lt;DataGrid.Resources&gt;
    &lt;local:BindingProxy x:Key=&quot;proxy&quot; Data=&quot;{Binding}&quot; /&gt;
&lt;/DataGrid.Resources&gt;
</pre></p>
<p>Il suffit ensuite de spécifier que la source de notre binding est cet objet <code>BindingProxy</code>, facilement accessible puisqu&#8217;il est déclaré comme ressource :</p>
<p><pre class="brush: xml;">
&lt;DataGridTextColumn Header=&quot;Price&quot; Binding=&quot;{Binding Price}&quot; IsReadOnly=&quot;False&quot;
                    Visibility=&quot;{Binding Data.ShowPrice,
                        Converter={StaticResource visibilityConverter},
                        Source={StaticResource proxy}}&quot;/&gt;
</pre></p>
<p>Remarquez qu&#8217;on a aussi préfixé le chemin du binding par &#8220;Data&#8221;, puisque le chemin est maintenant relatif à l&#8217;objet <code>BindingProxy</code>.</p>
<p>Le binding fonctionne maintenant comme prévu, moyennant une solution relativement simple à mettre en oeuvre&#8230;</p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/binding/'>binding</a>, <a href='http://tomlev.wordpress.com/tag/datacontext/'>datacontext</a>, <a href='http://tomlev.wordpress.com/tag/freezable/'>freezable</a>, <a href='http://tomlev.wordpress.com/tag/wpf/'>WPF</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/394/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/394/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/394/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/394/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/394/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/394/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/394/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/394/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/394/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/394/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/394/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/394/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/394/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/394/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=394&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2011/03/21/wpf-comment-faire-un-binding-dans-les-cas-o-on-nhrite-pas-du-datacontext/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[C# 5] Programmation asynchrone avec C# 5</title>
		<link>http://tomlev.wordpress.com/2010/10/30/c-5-programmation-asynchrone-avec-c-5/</link>
		<comments>http://tomlev.wordpress.com/2010/10/30/c-5-programmation-asynchrone-avec-c-5/#comments</comments>
		<pubDate>Sat, 30 Oct 2010 02:07:18 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[C# 5.0]]></category>
		<category><![CDATA[async]]></category>
		<category><![CDATA[await]]></category>

		<guid isPermaLink="false">https://tomlev.wordpress.com/?p=377</guid>
		<description><![CDATA[Depuis quelque temps, les spéculations allaient bon train sur les fonctionnalités de la future version 5 du langage C#… Très peu d’informations officielles avaient filtré à ce sujet, la seule chose plus ou moins certaine était l’introduction du concept de “compilateur en temps que service”, qui permettrait de tirer parti du compilateur à partir du [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=377&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Depuis quelque temps, les spéculations allaient bon train sur les fonctionnalités de la future version 5 du langage C#… Très peu d’informations officielles avaient filtré à ce sujet, la seule chose plus ou moins certaine était l’introduction du concept de “compilateur en temps que service”, qui permettrait de tirer parti du compilateur à partir du code. A part ça, silence radio de la part de Microsoft…</p>
<p>Lors de la PDC jeudi dernier, un coin du voile a enfin été levé, mais pas du tout sur ce qu’on attendait ! Anders Hejlsberg, le créateur de C#, a bien consacré quelques minutes à la notion de “<em>compiler as a service</em>”, mais l’essentiel de sa présentation portait sur quelque chose de complètement différent : la programmation asynchrone en C#.</p>
<p>Il est bien sûr déjà possible d’effectuer des traitements asynchrones en C#, mais c’est généralement assez pénible et peu intuitif… On est souvent obligé de passer par des callbacks pour indiquer ce qui doit être exécuté à la fin du traitement asynchrone, et on se retrouve rapidement avec un code difficile à relire et à comprendre, et donc à maintenir. Pour une démonstration de ce problème, je vous invite à lire l’<a href="http://blogs.msdn.com/b/ericlippert/archive/2010/10/27/continuation-passing-style-revisited-part-five-cps-and-asynchrony.aspx">excellent article d’Eric Lippert</a> à ce sujet, il explique ça beaucoup mieux que moi…</p>
<p>Cet article (et <a href="http://blogs.msdn.com/b/ericlippert/archive/tags/continuation+passing+style/">la série</a> qu’il conclut) était en fait un prélude à l’annonce faite à la PDC : <strong>C# 5 intègrera une nouvelle syntaxe permettant d’écrire du code asynchrone</strong> de façon beaucoup plus naturelle, avec l’introduction de deux nouveaux mots-clés : <code>async</code> et <code>await</code>. Le code à écrire pour réaliser un traitement asynchrone sera quasiment identique à celui d’un traitement synchrone : toute la complexité sera masquée par cette nouvelle fonctionnalité du langage.</p>
<p>Puisqu’un exemple vaut mieux qu’un long discours, je vais reprendre l’exemple utilisé par Anders Hejlsberg pendant sa présentation, en le simplifiant un peu. Supposons qu’on veuille rechercher des titres de films par leur année de sortie. Pour simplifier, on utilisera le service OData de <a href="http://www.netflix.com/">Netflix</a>. Le code suivant effectue la recherche de façon synchrone, en récupérant les résultats 10 par 10 :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:b8d0b669-152d-4477-8a6e-10cecaf4655f" class="wlWriterEditableSmartContent">
<pre>
<pre class="brush: csharp; pad-line-numbers: true;">
        private void btnSearch_Click(object sender, RoutedEventArgs e)
        {
            int year;
            if (!int.TryParse(txtYear.Text, out year))
            {
                MessageBox.Show(&quot;L'année saisie est incorrecte&quot;);
                return;
            }
            SearchMovies(year);
        }

        private void SearchMovies(int year)
        {
            var netflixUri = new Uri(&quot;http://odata.netflix.com/Catalog/&quot;);
            var catalog = new Netflix.NetflixCatalog(netflixUri);
            lstTitles.Items.Clear();
            int count = 0;
            int pageSize = 10;
            while (true)
            {
                var movies = SearchMoviesBatch(catalog, year, count, pageSize);
                if (movies.Length == 0)
                    break;
                foreach (var title in movies)
                {
                    lstTitles.Items.Add(title.Name);
                }
                count += movies.Length;
            }
        }

        private Title[] SearchMoviesBatch(NetflixCatalog catalog, int year, int count, int pageSize)
        {
            var query = from title in catalog.Titles
                            where title.ReleaseYear == year
                            orderby title.Name
                            select title;
            return query.Skip(count).Take(pageSize).ToArray();
        }
</pre>
</pre>
</div>
<p>Ce code a le mérite d’être assez simple, mais il suffit de l’exécuter pour se rendre compte qu’il y a un problème : la récupération des résultats peut prendre un certain temps, pendant lequel l’interface reste figée. Il faut donc effectuer la recherche de façon asynchrone, pour que l’interface reste réactive. Voici une approche possible, avec la version actuelle de C# :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:60f0c0ae-1e78-4bfe-8c79-4c1ac5dfb4f0" class="wlWriterEditableSmartContent">
<pre>
<pre class="brush: csharp;">
        private void SearchMoviesAsync(int year)
        {
            lstTitles.Items.Clear();
            Thread t = new Thread(() =&gt;
            {
                var netflixUri = new Uri(&quot;http://odata.netflix.com/Catalog/&quot;);
                var catalog = new Netflix.NetflixCatalog(netflixUri);
                int count = 0;
                int pageSize = 10;
                while (true)
                {
                    var movies = SearchMoviesBatch(catalog, year, count, pageSize);
                    if (movies.Length == 0)
                        break;
                    foreach (var title in movies)
                    {
                        Dispatcher.Invoke(new Action(() =&gt; lstTitles.Items.Add(title.Name)));
                    }
                    count += movies.Length;
                }
            });
            t.Start();
        }
</pre>
</pre>
</div>
<p>(Les deux autres méthodes sont inchangées)</p>
<p>On voit que le code commence déjà à être moins clair, à cause de l’expression lambda passée au constructeur du thread, et de l’utilisation de <code>Dispatcher.Invoke</code> pour mettre à jour l’interface graphique. Imaginez un peu ce que ça donnerait dans un scénario plus complexe, avec plusieurs tâches asynchrones interdépendantes (comme dans l’article d’Eric Lippert mentionné plus haut).</p>
<p>Avec la nouvelle syntaxe introduite par C# 5, voici comment on pourrait écrire ce code :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:04abbc87-d5a8-4417-8488-b9496f63ff9e" class="wlWriterEditableSmartContent">
<pre>
<pre class="brush: csharp;">
        private async void SearchMoviesAsync(int year)
        {
            var netflixUri = new Uri(&quot;http://odata.netflix.com/Catalog/&quot;);
            var catalog = new Netflix.NetflixCatalog(netflixUri);
            lstTitles.Items.Clear();
            int count = 0;
            int pageSize = 10;
            while (true)
            {
                var movies = await SearchMoviesBatchAsync(catalog, year, count, pageSize);
                if (movies.Length == 0)
                    break;
                foreach (var title in movies)
                {
                    lstTitles.Items.Add(title.Name);
                }
                count += movies.Length;
            }
        }

        private async Task&lt;Title[]&gt; SearchMoviesBatchAsync(NetflixCatalog catalog, int year, int count, int pageSize)
        {
            var query = from title in catalog.Titles
                        where title.ReleaseYear == year
                        orderby title.Name
                        select title;
            return await query.Skip(count).Take(pageSize).ToArrayAsync();
        }
</pre>
</pre>
</div>
<p>Remarquez que les deux méthodes ont un nouveau modificateur <code>async</code>, qui indique qu’elles s’exécutent de façon asynchrone. Lors de l’appel à une autre méthode asynchrone, l’appel est précédé du mot-clé <code>await</code>. Lorsque la méthode <code>SearchMoviesAsync</code> est appelée, elle commence à s’exécuter normalement, jusqu’au mot-clé <code>await</code>. A partir de là, deux scénarios sont possibles</p>
<ul>
<li>soit l’appel à <code>SearchMoviesBatchAsync</code> se termine de façon synchrone, auquel cas l’exécution continue normalement </li>
<li>soit il s’exécute de façon asynchrone, dans ce cas le contrôle est rendu à la méthode qui appelle <code>SearchMoviesAsync</code> (en l’occurrence <code>btnSearch_Click</code>). Quand l’appel à <code>SearchMoviesBatchAsync</code> se termine, l’exécution de <code>SearchMoviesAsync</code> reprend là où elle en était (de ce point de vue, <code>await</code> fonctionne un peu comme <code>yield return</code>)</li>
</ul>
<p>Un peu comme pour les blocs itérateurs, le compilateur réécrit le code de la méthode en créant un delegate avec le code qui suit l’appel asynchrone, et appelle ce delegate quand la tâche asynchrone se termine. Remarquez d’ailleurs que ce delegate est appelé sur le même thread, celui du dispatcher en l’occurrence : on n’a donc pas besoin de <code>Dispatcher.Invoke</code> pour mettre à jour l’interface graphique.</p>
<p>En pratique, tout ce système se base sur la classe <code>Task</code> introduite dans .NET 4. Remarquez d’ailleurs que le type de retour de la méthode <code>SearchMoviesBatchAsync</code> est <code>Task&lt;Title[]&gt;</code>. Pourtant, quand on appelle cette méthode à partir de <code>SearchMoviesAsync</code>, on récupère bien un objet de type <font face="Courier New">Title[]</font>, et non <code>Task&lt;Title[]&gt;</code>. C’est l’autre effet du mot-clé <code>await</code> : il récupère le <em>résultat</em> d’une tâche une fois qu’elle est terminée.</p>
<p>Encore une chose : j’ai utilisé dans le code une méthode d’extension <code style="margin-top:6px;margin-bottom:6px;">ToArrayAsync</code>, voici son code :</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:ea1c4d66-022f-404a-9b70-fb4e540e681c" class="wlWriterEditableSmartContent">
<pre>
<pre class="brush: csharp;">
        public static Task&lt;T[]&gt; ToArrayAsync&lt;T&gt;(this IQueryable&lt;T&gt; source)
        {
            return TaskEx.Run(() =&gt; source.ToArray());
        }
</pre>
</pre>
</div>
<p>Voilà pour l’introduction à cette future nouvelle fonctionnalité de C#. J’espère que c’était à peu près compréhensible et que je n’ai pas dit trop de bêtises… tout n’est pas encore complètement clair dans ma tête<img style="border-style:none;" class="wlEmoticon wlEmoticon-winkingsmile" alt="Clignement d&#039;œil" src="http://tomlev.files.wordpress.com/2010/10/wlemoticon-winkingsmile.png?w=780" />. Pour en savoir plus, voici quelques liens utiles :</p>
<ul>
<li>Eric Lippert, encore lui, a commencé une série d’articles sur la programmation asynchrone en C# 5, voici les deux premiers :
<ul>
<li><a href="http://blogs.msdn.com/b/ericlippert/archive/2010/10/28/asynchrony-in-c-5-part-one.aspx">Asynchrony in C# 5, Part One</a> </li>
<li><a href="http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx">Asynchronous Programming in C# 5.0 part two: Whence await?</a> </li>
</ul>
</li>
<li>
<div align="left"><a href="http://player.microsoftpdc.com/Session/1b127a7d-300e-4385-af8e-ac747fee677a">La vidéo de la présentation d’Anders Hejlsberg à la PDC</a></div>
</li>
<li>
<p><a href="http://msdn.microsoft.com/fr-fr/vstudio/async.aspx">Asynchronous Programming for C# and Visual Basic</a>, la page officielle où vous pourrez trouver des vidéos et articles sur le sujet, ainsi que le Visual Studio Async CTP que vous pouvez télécharger pour essayer vous-même.</p>
</li>
</ul>
<p>Pour les adeptes de VB.NET, sachez que cette fonctionnalité sera aussi inclue dans la prochaine version de Visual Basic, ainsi que les itérateurs, qui n’existaient qu’en C# jusqu’à maintenant.</p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/async/'>async</a>, <a href='http://tomlev.wordpress.com/tag/await/'>await</a>, <a href='http://tomlev.wordpress.com/tag/c-5-0/'>C# 5.0</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/377/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/377/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/377/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/377/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/377/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/377/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/377/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/377/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/377/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/377/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/377/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/377/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/377/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/377/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=377&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2010/10/30/c-5-programmation-asynchrone-avec-c-5/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2010/10/wlemoticon-winkingsmile.png" medium="image">
			<media:title type="html">Clignement d&#039;œil</media:title>
		</media:content>
	</item>
		<item>
		<title>[Entity Framework] Utiliser Include avec des expressions lambda</title>
		<link>http://tomlev.wordpress.com/2010/10/03/entity-framework-utiliser-include-avec-des-expressions-lambda/</link>
		<comments>http://tomlev.wordpress.com/2010/10/03/entity-framework-utiliser-include-avec-des-expressions-lambda/#comments</comments>
		<pubDate>Sun, 03 Oct 2010 17:44:15 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[C# 4.0]]></category>
		<category><![CDATA[Code sample]]></category>
		<category><![CDATA[.NET 4.0]]></category>
		<category><![CDATA[Entity Framework]]></category>
		<category><![CDATA[expression]]></category>
		<category><![CDATA[include]]></category>
		<category><![CDATA[lambda]]></category>
		<category><![CDATA[linq]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=360</guid>
		<description><![CDATA[Je travaille en ce moment sur un projet qui utilise Entity Framework 4. Bien que le lazy loading soit activé, j&#8217;utilise généralement la méthode ObjectQuery.Include pour charger les entités associées en une seule fois, de façon à éviter des appels supplémentaires à la base de données lors de l&#8217;accès à ces entités : Ou encore, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=360&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Je travaille en ce moment sur un projet qui utilise Entity Framework 4. Bien que le lazy loading soit activé, j&#8217;utilise généralement la méthode <a href="http://msdn.microsoft.com/en-us/library/bb738708.aspx"><code>ObjectQuery.Include</code></a> pour charger les entités associées en une seule fois, de façon à éviter des appels supplémentaires à la base de données lors de l&#8217;accès à ces entités :</p>
<p><pre class="brush: csharp;">
var query =
    from ord in db.Orders.Include(&quot;OrderDetails&quot;)
    where ord.Date &gt;= DateTime.Today
    select ord;
</pre></p>
<p>Ou encore, pour inclure aussi le produit :</p>
<p><pre class="brush: csharp;">
var query =
    from ord in db.Orders.Include(&quot;OrderDetails.Product&quot;)
    where ord.Date &gt;= DateTime.Today
    select ord;
</pre></p>
<p>Il y a quelque chose qui m&#8217;ennuie avec cette méthode <code>Include</code> : le fait de devoir spécifier le chemin de la propriété sous forme de chaine de caractères. En effet cette approche présente deux inconvénients majeurs :</p>
<ul>
<li>Elle comporte un risque d&#8217;erreur important : on a vite fait de faire une faute de frappe dans le chemin de la propriété, et puisque c&#8217;est une chaine de caractères, le compilateur ne remarque rien. On a donc une erreur à l&#8217;exécution alors que ça aurait pu être vérifié dès la compilation.</li>
<li>On ne profite plus de l&#8217;assistance de l&#8217;IDE : pas d&#8217;intellisense ni de refactoring. Si on renomme une propriété du modèle, le refactoring automatique ne prend pas en compte le contenu des chaines de caractères. Il faut donc aller modifier manuellement les appels à <code>Include</code> qui font référence à cette propriété, avec le risque non négligeable d&#8217;en oublier au passage&#8230;</li>
</ul>
<p>Il serait donc plus pratique d&#8217;utiliser une expression lambda pour spécifier le chemin de la propriété à inclure. Le principe est connu, et fréquemment utilisé pour éviter de passer une chaine quand on veut spécifier le nom d&#8217;une propriété.</p>
<p>Le cas &#8220;de base&#8221;, dans lequel on ne charge qu&#8217;une propriété directement liée à la source, est assez simple à gérer, et on trouve des implémentations un peu partout sur le net. Il suffit d&#8217;utiliser une méthode qui extrait le nom de la propriété à partir de l&#8217;expression :</p>
<p><pre class="brush: csharp;">
    public static class ObjectQueryExtensions
    {
        public static ObjectQuery&lt;T&gt; Include&lt;T&gt;(this ObjectQuery&lt;T&gt; query, Expression&lt;Func&lt;T, object&gt;&gt; selector)
        {
            string propertyName = GetPropertyName(selector);
            return query.Include(propertyName);
        }

        private static string GetPropertyName&lt;T&gt;(Expression&lt;Func&lt;T, object&gt;&gt; expression)
        {
            MemberExpression memberExpr = expression.Body as MemberExpression;
            if (memberExpr == null)
                throw new ArgumentException(&quot;Expression body must be a member expression&quot;);
            return memberExpr.Member.Name;
        }
    }
</pre></p>
<p>En utilisant cette méthode d&#8217;extension, on peut réécrire le code du premier exemple de la façon suivante :</p>
<p><pre class="brush: csharp;">
var query =
    from ord in db.Orders.Include(o =&gt; o.OrderDetails)
    where ord.Date &gt;= DateTime.Today
    select ord;
</pre></p>
<p>Ce code fonctionne, mais seulement pour les cas simples&#8230; dans le deuxième exemple, on veut aussi inclure la propriété <code>OrderDetail.Product</code>, et le code ci-dessus ne permet pas de gérer ce cas. En effet, l&#8217;expression qu&#8217;il faudrait écrire pour inclure la propriété <code>Product</code> serait du type <code>o.OrderDetails.Select(od =&gt; od.Product)</code>, or la méthode <code>GetPropertyName</code> ne sait gérer que les propriétés, pas les appels de méthode&#8230;</p>
<p>Pour obtenir le chemin complet de la propriété à inclure, il faut parcourir tout l&#8217;arbre d&#8217;expression pour en extraire les propriétés. Bien que cela puisse paraitre assez complexe, il existe une classe qui peut nous y aider : <a href="http://msdn.microsoft.com/en-us/library/system.linq.expressions.expressionvisitor.aspx"><code>ExpressionVisitor</code></a>. Cette classe, introduite en .NET 4.0, implémente le design pattern Visiteur pour parcourir tous les noeuds de l&#8217;arbre. L&#8217;implémentation de base ne fait rien de particulier, elle se contente de visiter chaque noeud. Tout ce que nous avons à faire, c&#8217;est en hériter pour spécialiser certaines méthodes de façon à extraire les propriétés utilisées dans l&#8217;expression. On va donc redéfinir les méthodes suivantes :</p>
<ul>
<li><code>VisitMember</code> : c&#8217;est la méthode appelée pour visiter l&#8217;accès à une propriété ou à un champ</li>
<li><code>VisitMethodCall</code> : la méthode appelée pour visiter les appels de méthode. Bien que ce cas ne nous intéresse pas directement a priori, on doit modifier son comportement dans le cas des opérateurs Linq : l&#8217;implémentation par défaut visite les paramètres dans l&#8217;ordre normal, mais pour les méthodes d&#8217;extension comme <code>Select</code> ou <code>SelectMany</code>, on doit visiter le premier paramètre (le paramètre <code>this</code>) en dernier, de façon à conserver l&#8217;ordre voulu pour le chemin de la propriété</li>
</ul>
<p>Voici donc la nouvelle implémentation de la méthode d&#8217;extension <code>Include</code> :</p>
<p><pre class="brush: csharp;">
    public static class ObjectQueryExtensions
    {
        public static ObjectQuery&lt;T&gt; Include&lt;T&gt;(this ObjectQuery&lt;T&gt; query, Expression&lt;Func&lt;T, object&gt;&gt; selector)
        {
            string path = new PropertyPathVisitor().GetPropertyPath(expression);
            return query.Include(path);
        }

        class PropertyPathVisitor : ExpressionVisitor
        {
            private Stack&lt;string&gt; _stack;

            public string GetPropertyPath(Expression expression)
            {
                _stack = new Stack&lt;string&gt;();
                Visit(expression);
                return _stack
                    .Aggregate(
                        new StringBuilder(),
                        (sb, name) =&gt;
                            (sb.Length &gt; 0 ? sb.Append(&quot;.&quot;) : sb).Append(name))
                    .ToString();
            }

            protected override Expression VisitMember(MemberExpression expression)
            {
                if (_stack != null)
                    _stack.Push(expression.Member.Name);
                return base.VisitMember(expression);
            }

            protected override Expression VisitMethodCall(MethodCallExpression expression)
            {
                if (IsLinqOperator(expression.Method))
                {
                    for (int i = 1; i &lt; expression.Arguments.Count; i++)
                    {
                        Visit(expression.Arguments[i]);
                    }
                    Visit(expression.Arguments[0]);
                    return expression;
                }
                return base.VisitMethodCall(expression);
            }

            private static bool IsLinqOperator(MethodInfo method)
            {
                if (method.DeclaringType != typeof(Queryable) &amp;&amp; method.DeclaringType != typeof(Enumerable))
                    return false;
                return Attribute.GetCustomAttribute(method, typeof(ExtensionAttribute)) != null;
            }
        }
    }
</pre></p>
<p>J&#8217;ai déjà parlé plus haut de la méthode <code>VisitMethodCall</code>, je ne reviens donc pas dessus. L&#8217;implémentation de <code>VisitMember</code> est très simple : on se contente d&#8217;ajouter le nom de la propriété sur une pile. Au fait, pourquoi une pile ? Parce que la visite de l&#8217;expression ne se déroule pas dans l&#8217;ordre auquel on pense intuitivement. Par exemple, dans une expression du type <code>o.OrderDetails.Select(od =&gt; od.Product)</code>, le premier noeud examiné n&#8217;est pas <code>o</code>, mais l&#8217;appel à <code>Select</code>, car ce qui précède (<code>o.OrderDetails</code>) est en fait un paramètre de la méthode statique <code>Select</code>&#8230; Pour obtenir les propriétés dans l&#8217;ordre voulu, on les place donc sur une pile de façon à les relire ensuite dans l&#8217;ordre inverse.</p>
<p>La méthode <code>GetPropertyPath</code> est elle aussi assez facile à comprendre : elle initialise la pile, visite l&#8217;expression, et reconstitue le chemin à partir de la pile.</p>
<p>On peut donc maintenant réécrire le code du deuxième exemple de la façon suivante :</p>
<p><pre class="brush: csharp;">
var query =
    from ord in db.Orders.Include(o =&gt; OrderDetails.Select(od =&gt; od.Product))
    where ord.Date &gt;= DateTime.Today
    select ord;
</pre></p>
<p>Cette méthode fonctionne aussi pour des cas plus complexes. Ajoutons un peu de piment à notre exemple : une ou plusieurs remises peuvent être appliquées à chaque article commandé, et chaque remise est associée à une campagne promotionnelle. Si on veut inclure les remises et les campagnes associées dans les résultats de la requête, on peut écrire quelque chose comme ça :</p>
<p><pre class="brush: csharp;">
var query =
    from ord in db.Orders.Include(o =&gt; OrderDetails.Select(od =&gt; od.Discounts.Select(d =&gt; d.Campaign)))
    where ord.Date &gt;= DateTime.Today
    select ord;
</pre></p>
<p>Le résultat est le même que si on avait passé à <code>Include</code> le chemin &#8220;OrderDetails.Discounts.Campaign&#8221;.  Comme les <code>Select</code> imbriqués réduisent la lisibilité du code, on peut écrire l&#8217;expression un peu différemment :</p>
<p><pre class="brush: csharp;">
var query =
    from ord in db.Orders.Include(o =&gt; o.OrderDetails
                                        .SelectMany(od =&gt; od.Discounts)
                                        .Select(d =&gt; d.Campaign))
    where ord.Date &gt;= DateTime.Today
    select ord;
</pre></p>
<p>Pour finir, deux remarques sur cette solution :</p>
<ul>
<li>Une méthode d&#8217;extension similaire est inclue dans le <a href="http://blogs.msdn.com/b/adonet/archive/2010/07/14/ctp4announcement.aspx">Entity Framework Feature CTP4</a> (voir <a href="http://romiller.com/2010/07/14/ef-ctp4-tips-tricks-include-with-lambda/">cet article</a> pour plus de détails). Il est donc probable qu&#8217;elle finisse par être intégrée dans le Framework (peut-être dans un service pack pour .NET 4.0 ?)</li>
<li>Bien que cette solution cible Entity Framework 4.0, il est a priori possible de l&#8217;adapter à EF 3.5. La classe <code>ExpressionVisitor</code> n&#8217;est pas disponible en 3.5, mais le <a href="http://www.albahari.com/nutshell/linqkit.aspx">LINQKit</a> de Joseph Albahari en fournit une implémentation. Je n&#8217;ai pas essayé, mais ça devrait fonctionner de la même façon&#8230;</li>
</ul>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/net-4-0/'>.NET 4.0</a>, <a href='http://tomlev.wordpress.com/tag/entity-framework/'>Entity Framework</a>, <a href='http://tomlev.wordpress.com/tag/expression/'>expression</a>, <a href='http://tomlev.wordpress.com/tag/include/'>include</a>, <a href='http://tomlev.wordpress.com/tag/lambda/'>lambda</a>, <a href='http://tomlev.wordpress.com/tag/linq/'>linq</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/360/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=360&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2010/10/03/entity-framework-utiliser-include-avec-des-expressions-lambda/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Une grille simplifiée utilisant des attributs XAML</title>
		<link>http://tomlev.wordpress.com/2010/07/20/wpf-une-grille-simplifiee-utilisant-des-attributs-xaml/</link>
		<comments>http://tomlev.wordpress.com/2010/07/20/wpf-une-grille-simplifiee-utilisant-des-attributs-xaml/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 00:13:46 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[grid]]></category>
		<category><![CDATA[XAML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=352</guid>
		<description><![CDATA[Le composant Grid est l&#8217;un des contrôles les plus utilisés en WPF. Il permet de disposer facilement des éléments selon des lignes et des colonnes. Malheureusement le code pour l&#8217;utiliser, bien que simple à écrire, est relativement lourd : Dans cet exemple, plus de la moitié du code est constitué de la définition de la [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=352&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Le composant <code>Grid</code> est l&#8217;un des contrôles les plus utilisés en WPF. Il permet de disposer facilement des éléments selon des lignes et des colonnes. Malheureusement le code pour l&#8217;utiliser, bien que simple à écrire, est relativement lourd :</p>
<p><pre class="brush: xml;">
&lt;Grid&gt;
    &lt;Grid.RowDefinitions&gt;
        &lt;RowDefinition Height=&quot;Auto&quot;/&gt;
        &lt;RowDefinition Height=&quot;5&quot;/&gt;
        &lt;RowDefinition Height=&quot;*&quot;/&gt;
    &lt;/Grid.RowDefinitions&gt;
    &lt;Grid.ColumnDefinitions&gt;
        &lt;ColumnDefinition Width=&quot;60&quot; /&gt;
        &lt;ColumnDefinition Width=&quot;*&quot; /&gt;
    &lt;/Grid.ColumnDefinitions&gt;
    
    &lt;Label Content=&quot;Name&quot; Grid.Row=&quot;0&quot; Grid.Column=&quot;0&quot; /&gt;
    &lt;TextBox Text=&quot;Hello world&quot; Grid.Row=&quot;0&quot; Grid.Column=&quot;1&quot;/&gt;
    &lt;Rectangle Fill=&quot;Black&quot; Grid.Row=&quot;1&quot; Grid.ColumnSpan=&quot;2&quot;/&gt;
    &lt;Label Content=&quot;Image&quot; Grid.Row=&quot;2&quot; Grid.Column=&quot;0&quot; /&gt;
    &lt;Image Source=&quot;Resources/Desert.jpg&quot; Grid.Row=&quot;2&quot; Grid.Column=&quot;1&quot; /&gt;
&lt;/Grid&gt;
</pre></p>
<p>Dans cet exemple, plus de la moitié du code est constitué de la définition de la grille ! Bien que cette syntaxe offre une certaine souplesse et permette de contrôler assez finement la disposition, dans la plupart des cas on a seulement besoin de définir la hauteur des lignes ou la largeur des colonnes&#8230; il serait donc beaucoup plus simple de pouvoir déclarer la grille de cette façon :</p>
<p><pre class="brush: xml;">
&lt;Grid Rows=&quot;Auto,5,*&quot; Columns=&quot;60,*&quot;&gt;
    ...
&lt;/Grid&gt;
</pre></p>
<p>La suite de cet article démontre comment atteindre précisément ce résultat, en créant une classe <code>SimpleGrid</code> héritée de <code>Grid</code>.</p>
<p>Pour commencer, notre classe aura besoin de deux nouvelles propriétés : <code>Rows</code> et <code>Columns</code>. Ces propriétés définissent respectivement les hauteurs et largeurs des lignes et des colonnes. Ces dimensions ne sont pas de simples nombres : en effet, des valeurs comme <code>"*"</code>, <code>"2*"</code> ou <code>"Auto"</code> sont des dimensions valides. Il existe en WPF un type dédié pour représenter ces dimensions : la structure <code>GridLength</code>. Nos deux propriétés seront donc des collections de <code>GridLength</code>. Voilà donc la signature de la classe <code>SimpleGrid</code> :</p>
<p><pre class="brush: csharp;">
public class SimpleGrid : Grid
{
    public IList&lt;GridLength&gt; Rows { get; set; }
    public IList&lt;GridLength&gt; Columns { get; set; }
}
</pre></p>
<p>Puisque ce sont ces propriétés qui vont contrôler les lignes et colonnes de la grille, il faut qu&#8217;elles modifient les <code>RowDefinitions</code> et <code>ColumnDefinitions</code> de la classe de base. Voilà donc comment les implémenter pour obtenir le résultat voulu :<br />
<pre class="brush: csharp;">
        private IList&lt;GridLength&gt; _rows;
        public IList&lt;GridLength&gt; Rows
        {
            get { return _rows; }
            set
            {
                _rows = value;
                RowDefinitions.Clear();
                if (_rows == null)
                    return;
                foreach (var length in _rows)
                {
                    RowDefinitions.Add(new RowDefinition { Height = length });
                }
            }
        }

        private IList&lt;GridLength&gt; _columns;
        public IList&lt;GridLength&gt; Columns
        {
            get { return _columns; }
            set
            {
                _columns = value;
                ColumnDefinitions.Clear();
                if (_columns == null)
                    return;
                foreach (var length in _columns)
                {
                    ColumnDefinitions.Add(new ColumnDefinition { Width = length });
                }
            }
        }
</pre></p>
<p>Notre classe <code>SimpleGrid</code> est d&#8217;ores et déjà utilisable&#8230; à partir du code C#, ce qui ne nous aide pas beaucoup quand il s&#8217;agit de simplifier le code XAML. Il nous faut donc trouver un moyen de déclarer dans un attribut les valeurs de ces propriétés, ce qui n&#8217;est pas évident dans la mesure où ce sont des collections&#8230;</p>
<p>En XAML, tous les attributs sont écrits sous forme de chaines de caractères. Pour convertir ces chaines en valeurs du type voulu, WPF fait appel à des convertisseurs, qui sont des classes héritées de <code>TypeConverter</code>, associées à chaque type qui supporte les conversions de et vers d&#8217;autres types. Par exemple, le type <code>GridLength</code> a pour convertisseur le type <code>GridLengthConverter</code>, qui permet de convertir des nombres ou des chaines de caractères en <code>GridLength</code>, et inversement. Le mécanisme de conversion est décrit dans <a href="http://msdn.microsoft.com/fr-fr/library/aa970913.aspx">cet article</a> sur MSDN.</p>
<p>Nous allons donc devoir créer un convertisseur et l&#8217;associer au type de nos propriétés. Comme nous n&#8217;avons pas la main sur le type <code>IList&lt;T&gt;</code>, nous allons d&#8217;abord créer un type spécifique <code>GridLengthCollection</code> qu&#8217;on utilisera à la place de <code>IList&lt;GridLength&gt;</code>, et lui associer un convertisseur <code>GridLengthCollectionConverter</code> :</p>
<p><pre class="brush: csharp;">
    [TypeConverter(typeof(GridLengthCollectionConverter))]
    public class GridLengthCollection : ReadOnlyCollection&lt;GridLength&gt;
    {
        public GridLengthCollection(IList&lt;GridLength&gt; lengths)
            : base(lengths)
        {
        }
    }
</pre></p>
<p>Pourquoi une collection en lecture seule ? Tout bêtement parce que permettre d&#8217;ajouter ou de supprimer des lignes ou des colonnes compliquerait l&#8217;implémentation, et n&#8217;apporterait rien pour l&#8217;objectif qui nous intéresse, à savoir simplifier la déclaration de la grille en XAML. Donc, restons dans la simplicité&#8230; Pour éviter de réinventer la roue, on hérite de la classe <code>ReadOnlyCollection&lt;T&gt;</code>, qui correspond parfaitement à notre besoin.</p>
<p>Notez aussi l&#8217;utilisation de l&#8217;attribut <code>TypeConverter</code> : il sert à indiquer le convertisseur associé à un type. Il nous reste donc simplement à implémenter ce convertisseur :</p>
<p><pre class="brush: csharp;">
    public class GridLengthCollectionConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            return base.CanConvertFrom(context, sourceType);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(string))
                return true;
            return base.CanConvertTo(context, destinationType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            string s = value as string;
            if (s != null)
                return ParseString(s, culture);
            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string) &amp;&amp; value is GridLengthCollection)
                return ToString((GridLengthCollection)value, culture);
            return base.ConvertTo(context, culture, value, destinationType);
        }

        private string ToString(GridLengthCollection value, CultureInfo culture)
        {
            var converter = new GridLengthConverter();
            return string.Join(&quot;,&quot;, value.Select(v =&gt; converter.ConvertToString(v)));
        }

        private GridLengthCollection ParseString(string s, CultureInfo culture)
        {
            var converter = new GridLengthConverter();
            var lengths = s.Split(',').Select(p =&gt; (GridLength)converter.ConvertFromString(p.Trim()));
            return new GridLengthCollection(lengths.ToArray());
        }
    }
</pre></p>
<p>Ce convertisseur est capable de convertir une <code>GridLengthCollection</code> de et vers une chaine de caractères, dans laquelle les dimensions sont séparées par des virgules. Notez l&#8217;utilisation du convertisseur <code>GridLengthConverter</code> : puisqu&#8217;il existe déjà un convertisseur pour les éléments de notre collection, autant s&#8217;en servir&#8230;</p>
<p>Toutes les pièces du puzzle étant en place, il ne nous reste plus qu&#8217;à utiliser notre nouvelle grille simplifiée :</p>
<p><pre class="brush: xml;">
&lt;my:SimpleGrid Rows=&quot;Auto,5,*&quot; Columns=&quot;60,*&quot;&gt;
    &lt;Label Content=&quot;Name&quot; Grid.Row=&quot;0&quot; Grid.Column=&quot;0&quot; /&gt;
    &lt;TextBox Text=&quot;Hello world&quot; Grid.Row=&quot;0&quot; Grid.Column=&quot;1&quot;/&gt;
    &lt;Rectangle Fill=&quot;Black&quot; Grid.Row=&quot;1&quot; Grid.ColumnSpan=&quot;2&quot;/&gt;
    &lt;Label Content=&quot;Image&quot; Grid.Row=&quot;2&quot; Grid.Column=&quot;0&quot; /&gt;
    &lt;Image Source=&quot;Resources/Desert.jpg&quot; Grid.Row=&quot;2&quot; Grid.Column=&quot;1&quot; /&gt;
&lt;/my:SimpleGrid&gt;
</pre></p>
<p>On obtient donc un résultat beaucoup plus concis et lisible qu&#8217;en utilisant une <code>Grid</code> normale, l&#8217;objectif est donc atteint <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>On pourrait bien sûr envisager des améliorations, par exemple déclarer les propriétés <code>Rows</code> et <code>Columns</code> comme des <code>DependencyProperty</code> afin de permettre le binding, ou encore gérer l&#8217;ajout et la suppression de colonnes. Cependant, cette grille s&#8217;adresse à des scénarios simples où la grille est définie une fois pour toutes et n&#8217;est pas modifiée à l&#8217;exécution (ce qui correspond a priori au cas d&#8217;utilisation le plus fréquent), il semble donc plus judicieux de la garder la plus simple possible. Pour des besoins plus spécifiques, par exemple si l&#8217;on veut utiliser les propriétés <code>MinWidth</code>, <code>MaxWidth</code> ou encore <code>SharedSizeGroup</code>, il faudra donc revenir à la <code>Grid</code> standard.</p>
<p>Pour référence, voici le code final de la classe <code>SimpleGrid</code> :</p>
<p><pre class="brush: csharp;">
    public class SimpleGrid : Grid
    {
        private GridLengthCollection _rows;
        public GridLengthCollection Rows
        {
            get { return _rows; }
            set
            {
                _rows = value;
                RowDefinitions.Clear();
                if (_rows == null)
                    return;
                foreach (var length in _rows)
                {
                    RowDefinitions.Add(new RowDefinition { Height = length });
                }
            }
        }

        private GridLengthCollection _columns;
        public GridLengthCollection Columns
        {
            get { return _columns; }
            set
            {
                _columns = value;
                if (_columns == null)
                    return;
                ColumnDefinitions.Clear();
                foreach (var length in _columns)
                {
                    ColumnDefinitions.Add(new ColumnDefinition { Width = length });
                }
            }
        }
    }
</pre></p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/grid/'>grid</a>, <a href='http://tomlev.wordpress.com/tag/wpf/'>WPF</a>, <a href='http://tomlev.wordpress.com/tag/xaml/'>XAML</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/352/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/352/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/352/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/352/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/352/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/352/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/352/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/352/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/352/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/352/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/352/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/352/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/352/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/352/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=352&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2010/07/20/wpf-une-grille-simplifiee-utilisant-des-attributs-xaml/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[C#] Une implémentation du pattern WeakEvent</title>
		<link>http://tomlev.wordpress.com/2010/05/16/c-une-implementation-du-pattern-weakevent/</link>
		<comments>http://tomlev.wordpress.com/2010/05/16/c-une-implementation-du-pattern-weakevent/#comments</comments>
		<pubDate>Sun, 16 May 2010 21:35:14 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[code snippet]]></category>
		<category><![CDATA[weak event]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=339</guid>
		<description><![CDATA[Comme vous le savez peut-être, la mauvaise utilisation des évènements est l&#8217;une des principales causes de fuites mémoires dans une application .NET : en effet, un évènement garde des références aux objets qui y sont abonnés (via le delegate), ce qui empêche le garbage collector de collecter ces objets quand ils ne sont plus utilisés. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=339&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Comme vous le savez peut-être, la mauvaise utilisation des évènements est l&#8217;une des principales causes de fuites mémoires dans une application .NET : en effet, un évènement garde des références aux objets qui y sont abonnés (via le delegate), ce qui empêche le garbage collector de collecter ces objets quand ils ne sont plus utilisés. Le problème est particulièrement vrai pour un évènement statique, puisque les références sont conservées pendant toute l&#8217;exécution de l&#8217;application. Si on crée de nombreux objets qui s&#8217;abonnent à un évènement statique et qu&#8217;on ne les désabonne pas, ils restent indéfiniment en mémoire, même si on n&#8217;en a plus besoin depuis longtemps, ce qui peut finir par saturer la mémoire.</p>
<p>La solution &#8220;évidente&#8221; au problème est bien sûr de désabonner les objets qui ne sont plus utilisés. Malheureusement, il n&#8217;y a pas toujours de moyen simple de savoir à quel moment on peut désabonner un objet. Une autre approche est d&#8217;implémenter le pattern <a href="http://msdn.microsoft.com/en-us/library/aa970850.aspx">WeakEvent</a>, qui permet de ne garder qu&#8217;une référence faible vers les objets abonnés à l&#8217;évènement, de façon à ne pas empêcher le garbage collector de les collecter. Microsoft inclut dans WPF des éléments pour implémenter le pattern WeakEvent, et explique comment créer ses propres évènements selon ce pattern, à l&#8217;aide de la classe <code>WeakEventManager</code> et de l&#8217;interface <code>IWeakEventListener</code>. Cependant, cette technique est assez lourde à mettre en œuvre, aussi bien pour exposer un tel évènement (il faut créer une nouvelle classe dédiée) que pour s&#8217;abonner à l&#8217;évènement (implémentation de <code>IWeakEventListener</code>).</p>
<p>J&#8217;ai donc réfléchi à une autre solution, permettant d&#8217;implémenter plus facilement le pattern WeakEvent. Ma première idée était d&#8217;utiliser une liste de <code>WeakReference</code> pour stocker la liste des delegates abonnés à l&#8217;évènement. Malheureusement, lorsqu&#8217;on s&#8217;abonne à un évènement, on écrit généralement quelque chose comme ça :</p>
<p><pre class="brush: csharp;">
myObject.MyEvent += new EventHandler(myObject_MyEvent);
</pre></p>
<p>On crée donc un delegate, mais on ne garde aucune référence dessus. Puisque l&#8217;évènement ne référence ce delegate que via une WeakReference, rien n&#8217;empêche le garbage collector de le collecter&#8230; et c&#8217;est effectivement ce qui arrive. Au bout d&#8217;un temps variable (pas plus de quelques secondes d&#8217;après mes observations), le delegate est collecté et n&#8217;est donc plus appelé quand l&#8217;évènement est déclenché.</p>
<p>Plutôt que de conserver une référence faible vers le delegate lui même, une meilleure solution serait de faire une référence faible sur l&#8217;objet qui implémente la méthode (<code>Delegate.Target</code>). J&#8217;ai donc créé une classe <code>WeakDelegate&lt;TDelegate&gt;</code> pour gérer cela :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    public class WeakDelegate&lt;TDelegate&gt; : IEquatable&lt;TDelegate&gt;
    {
        private WeakReference _targetReference;
        private MethodInfo _method;

        public WeakDelegate(Delegate realDelegate)
        {
            if (realDelegate.Target != null)
                _targetReference = new WeakReference(realDelegate.Target);
            else
                _targetReference = null;
            _method = realDelegate.Method;
        }

        public TDelegate GetDelegate()
        {
            return (TDelegate)(object)GetDelegateInternal();
        }

        private Delegate GetDelegateInternal()
        {
            if (_targetReference != null)
            {
                return Delegate.CreateDelegate(typeof(TDelegate), _targetReference.Target, _method);
            }
            else
            {
                return Delegate.CreateDelegate(typeof(TDelegate), _method);
            }
        }

        public bool IsAlive
        {
            get { return _targetReference == null || _targetReference.IsAlive; }
        }


        #region IEquatable&lt;TDelegate&gt; Members

        public bool Equals(TDelegate other)
        {
            Delegate d = (Delegate)(object)other;
            return d != null
                &amp;&amp; d.Target == _targetReference.Target
                &amp;&amp; d.Method.Equals(_method);
        }

        #endregion

        internal void Invoke(params object[] args)
        {
            Delegate handler = (Delegate)(object)GetDelegateInternal();
            handler.DynamicInvoke(args);
        }
    }
</pre></p>
<p>Il ne reste plus qu&#8217;à gérer une liste de <code>WeakDelegate&lt;TDelegate&gt;</code>, ce que fait la classe <code>WeakEvent&lt;TDelegate&gt;</code> :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    public class WeakEvent&lt;TEventHandler&gt;
    {
        private List&lt;WeakDelegate&lt;TEventHandler&gt;&gt; _handlers;

        public WeakEvent()
        {
            _handlers = new List&lt;WeakDelegate&lt;TEventHandler&gt;&gt;();
        }

        public virtual void AddHandler(TEventHandler handler)
        {
            Delegate d = (Delegate)(object)handler;
            _handlers.Add(new WeakDelegate&lt;TEventHandler&gt;(d));
        }

        public virtual void RemoveHandler(TEventHandler handler)
        {
            // also remove &quot;dead&quot; (garbage collected) handlers
            _handlers.RemoveAll(wd =&gt; !wd.IsAlive || wd.Equals(handler));
        }

        public virtual void Raise(object sender, EventArgs e)
        {
            var handlers = _handlers.ToArray();
            foreach (var weakDelegate in handlers)
            {
                if (weakDelegate.IsAlive)
                {
                    weakDelegate.Invoke(sender, e);
                }
                else
                {
                    _handlers.Remove(weakDelegate);
                }
            }
        }

        protected List&lt;WeakDelegate&lt;TEventHandler&gt;&gt; Handlers
        {
            get { return _handlers; }
        }
    }
</pre></p>
<p>Cette classe gère automatiquement la suppression des handlers &#8220;morts&#8221; (collectés), et fournit une méthode <code>Raise</code> pour faciliter le déclenchement de l&#8217;évènement. Elle peut s&#8217;utiliser de la façon suivante :</p>
<p><pre class="brush: csharp;">
        private WeakEvent&lt;EventHandler&gt; _myEvent = new WeakEvent&lt;EventHandler&gt;();
        public event EventHandler MyEvent
        {
            add { _myEvent.AddHandler(value); }
            remove { _myEvent.RemoveHandler(value); }
        }

        protected virtual void OnMyEvent()
        {
            _myEvent.Raise(this, EventArgs.Empty);
        }
</pre></p>
<p>C&#8217;est un peu plus long à écrire qu&#8217;un évènement &#8220;classique&#8221;, mais ce n&#8217;est finalement pas grand chose par rapport aux avantages que ça apporte&#8230; D&#8217;ailleurs, on peut facilement créer un &#8220;code snippet&#8221; pour Visual Studio, qui permet de créer un &#8220;évènement faible&#8221; en un rien de temps, avec seulement 3 informations à renseigner :</p>
<p><pre class="brush: xml; collapse: true; light: false; toolbar: true;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
&lt;CodeSnippets  xmlns=&quot;http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet&quot;&gt;
  &lt;CodeSnippet Format=&quot;1.0.0&quot;&gt;
    &lt;Header&gt;
      &lt;Title&gt;wevt&lt;/Title&gt;
      &lt;Shortcut&gt;wevt&lt;/Shortcut&gt;
      &lt;Description&gt;Code snippet for a weak event&lt;/Description&gt;
      &lt;Author&gt;Thomas Levesque&lt;/Author&gt;
      &lt;SnippetTypes&gt;
        &lt;SnippetType&gt;Expansion&lt;/SnippetType&gt;
      &lt;/SnippetTypes&gt;
    &lt;/Header&gt;
    &lt;Snippet&gt;
      &lt;Declarations&gt;
        &lt;Literal&gt;
          &lt;ID&gt;type&lt;/ID&gt;
          &lt;ToolTip&gt;Event type&lt;/ToolTip&gt;
          &lt;Default&gt;EventHandler&lt;/Default&gt;
        &lt;/Literal&gt;
        &lt;Literal&gt;
          &lt;ID&gt;event&lt;/ID&gt;
          &lt;ToolTip&gt;Event name&lt;/ToolTip&gt;
          &lt;Default&gt;MyEvent&lt;/Default&gt;
        &lt;/Literal&gt;
        &lt;Literal&gt;
          &lt;ID&gt;field&lt;/ID&gt;
          &lt;ToolTip&gt;Name of the field holding the registered handlers&lt;/ToolTip&gt;
          &lt;Default&gt;_myEvent&lt;/Default&gt;
        &lt;/Literal&gt;
      &lt;/Declarations&gt;
      &lt;Code Language=&quot;csharp&quot;&gt;
        &lt;![CDATA[private WeakEvent&lt;$type$&gt; $field$ = new WeakEvent&lt;EventHandler&gt;();
        public event $type$ $event$
        {
            add { $field$.AddHandler(value); }
            remove { $field$.RemoveHandler(value); }
        }

        protected virtual void On$event$()
        {
            $field$.Raise(this, EventArgs.Empty);
        }
	$end$]]&gt;
      &lt;/Code&gt;
    &lt;/Snippet&gt;
  &lt;/CodeSnippet&gt;
&lt;/CodeSnippets&gt;
</pre></p>
<p>Ce qui donne dans Visual Studio le résultat suivant :</p>
<p><a href="http://tomlev.files.wordpress.com/2010/05/screenshot_snippet_wevt.png"><img src="http://tomlev.files.wordpress.com/2010/05/screenshot_snippet_wevt.png?w=780" alt="Code snippet pour implémenter un WeakEvent" title="Code snippet pour implémenter un WeakEvent"   class="aligncenter size-full wp-image-343" /></a></p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/c/'>C#</a>, <a href='http://tomlev.wordpress.com/tag/code-snippet/'>code snippet</a>, <a href='http://tomlev.wordpress.com/tag/weak-event/'>weak event</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/339/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=339&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2010/05/16/c-une-implementation-du-pattern-weakevent/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2010/05/screenshot_snippet_wevt.png" medium="image">
			<media:title type="html">Code snippet pour implémenter un WeakEvent</media:title>
		</media:content>
	</item>
		<item>
		<title>[Dvp.NET] Première version de la librairie Dvp.NET !</title>
		<link>http://tomlev.wordpress.com/2010/03/18/dvp-net-premiere-version-de-la-librairie-dvp-net/</link>
		<comments>http://tomlev.wordpress.com/2010/03/18/dvp-net-premiere-version-de-la-librairie-dvp-net/#comments</comments>
		<pubDate>Thu, 18 Mar 2010 14:25:07 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Dvp.NET]]></category>
		<category><![CDATA[librairie]]></category>
		<category><![CDATA[open-source]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=302</guid>
		<description><![CDATA[Depuis quelques mois, je travaille avec d&#8217;autres membres de l&#8217;équipe .NET de Developpez.com sur un petit projet open-source nommé Dvp.NET. Il s&#8217;agit d&#8217;une librairie de classes issues des contributions des membres de Developpez.com. Les fonctionnalités de cette librairie sont très diverses : Des méthodes d&#8217;extension pour faciliter les tâches courantes concernant : Les chaines de [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=302&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Depuis quelques mois, je travaille avec d&#8217;autres membres de l&#8217;équipe .NET de Developpez.com sur un petit projet open-source nommé <a href="http://dvp-net.developpez.com/">Dvp.NET</a>. Il s&#8217;agit d&#8217;une librairie de classes issues des contributions des membres de Developpez.com. Les fonctionnalités de cette librairie sont très diverses :</p>
<ul>
<li>Des méthodes d&#8217;extension pour faciliter les tâches courantes concernant :
<ul>
<li>Les chaines de caractères</li>
<li>Les collections</li>
<li>L&#8217;accès aux données</li>
<li>Les dates</li>
<li>Les énumérations</li>
<li>&#8230;</li>
</ul>
</li>
<li>Des algorithmes métier fréquemment utilisés (vérification de numéros Siret, Siren, IBAN, RIB, carte de crédit&#8230;)</li>
<li>Des classes de conversion de nombres en toutes lettres ou en chiffres romains</li>
<li>Une classe de formatage de texte avancé (StringTemplate)</li>
<li>Des contrôles, composants, et méthodes d&#8217;extension pour Windows Forms</li>
<li>Des markup extensions et propriétés attachées pour WPF (tri automatique d&#8217;une ListView, bordure de fenêtre &#8220;Glass&#8221;, binding sur les paramètres d&#8217;application&#8230;), ainsi que des classes utiles pour développer selon le pattern MVVM (ViewModelBase, DelegateCommand&#8230;)</li>
<li>Et encore beaucoup d&#8217;autres choses, mais je ne vais pas vous en faire une liste exhaustive ici <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </li>
</ul>
<p>Hier, nous avons publié la première version beta de cette librairie : n&#8217;hésitez pas à <a href="http://projets.developpez.com/attachments/download/437/dvp.net-0.1-beta.zip">la télécharger</a> et à l&#8217;essayer ! Vous pouvez nous faire part des bugs rencontrés et de vos suggestions en postant sur <a href="http://www.developpez.net/forums/f1239/applications/projets/projets-heberges/dvp-net/">le forum du projet</a>.</p>
<p>Une <a href="http://dvp-net.developpez.com/doc/">documentation en ligne</a> est disponible, ainsi qu&#8217;un <a href="http://projets.developpez.com/projects/dvp-net/wiki">wiki</a> (encore un peu incomplet) qui présente succinctement les différentes fonctionnalités de la librairie, avec des exemples d&#8217;utilisation.</p>
<p>N&#8217;hésitez pas à me faire part de vos retours !</p>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/dvp-net/'>Dvp.NET</a>, <a href='http://tomlev.wordpress.com/tag/librairie/'>librairie</a>, <a href='http://tomlev.wordpress.com/tag/open-source/'>open-source</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/302/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/302/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/302/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/302/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/302/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/302/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/302/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/302/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/302/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/302/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/302/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/302/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/302/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/302/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=302&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2010/03/18/dvp-net-premiere-version-de-la-librairie-dvp-net/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>Automatiser la vérification des null avec les expressions Linq</title>
		<link>http://tomlev.wordpress.com/2010/02/20/automatiser-la-verification-des-null-avec-les-expressions-linq/</link>
		<comments>http://tomlev.wordpress.com/2010/02/20/automatiser-la-verification-des-null-avec-les-expressions-linq/#comments</comments>
		<pubDate>Sat, 20 Feb 2010 00:42:59 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[expression]]></category>
		<category><![CDATA[linq]]></category>
		<category><![CDATA[null check]]></category>
		<category><![CDATA[proof of concept]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=294</guid>
		<description><![CDATA[Le problème Je suis sûr qu&#8217;il vous est déjà arrivé d&#8217;écrire ce genre de code : On veut juste obtenir name = xx.Foo.Bar.Baz.Name, mais on est obligé de tester chaque objet intermédiaire pour vérifier qu&#8217;il n&#8217;est pas nul, ce qui peut vite s&#8217;avérer pénible si la propriété voulue est profondément enfouie dans un graphe d&#8217;objets&#8230; [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=294&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>Le problème</strong></p>
<p>Je suis sûr qu&#8217;il vous est déjà arrivé d&#8217;écrire ce genre de code :</p>
<p><pre class="brush: csharp;">
X x = GetX();
string name = &quot;Default&quot;;
if (xx != null &amp;&amp; xx.Foo != null &amp;&amp; xx.Foo.Bar != null &amp;&amp; xx.Foo.Bar.Baz != null)
{
    name = xx.Foo.Bar.Baz.Name;
}
</pre></p>
<p>On veut juste obtenir <code>name = xx.Foo.Bar.Baz.Name</code>, mais on est obligé de tester chaque objet intermédiaire pour vérifier qu&#8217;il n&#8217;est pas nul, ce qui peut vite s&#8217;avérer pénible si la propriété voulue est profondément enfouie dans un graphe d&#8217;objets&#8230;</p>
<p><strong>Une solution</strong></p>
<p>Linq offre une fonctionnalité qui permet (entre autres) de régler ce problème : les expressions. Il est possible, à partir d&#8217;une expression lambda, d&#8217;obtenir son arbre syntaxique (ou AST : Abstract Syntax Tree), et de faire toutes sortes de manipulations sur cet arbre. On peut également générer dynamiquement un arbre syntaxique, et le compiler pour obtenir un delegate qu&#8217;on pourra ensuite exécuter.</p>
<p>Mais quel rapport avec le problème qui nous intéresse ? Eh bien tout simplement, nous allons pouvoir utiliser les expressions Linq pour analyser l&#8217;arbre syntaxique correspondant à l&#8217;accès à la propriété <code>xx.Foo.Bar.Baz.Name</code>, et réécrire cet arbre de façon à y ajouter des tests de nullité pour chaque objet intermédiaire.</p>
<p>Nous allons donc créer une méthode d&#8217;extension <code>NullSafeEval</code>, qui prendra en premier paramètre une expression lambda définissant comment accéder à la propriété voulue, et en deuxième paramètre la valeur par défaut à renvoyer si un objet nul est rencontré en cours de route.</p>
<p>Cette méthode va transformer l&#8217;expression <code>xx.Foo.Bar.Baz.Name</code> en ceci :</p>
<p><pre class="brush: csharp;">
    (xx == null)
    ? defaultValue
    : (xx.Foo == null)
      ? defaultValue
      : (xx.Foo.Bar == null)
        ? defaultValue
        : (xx.Foo.Bar.Baz == null)
          ? defaultValue
          : xx.Foo.Bar.Baz.Name;
</pre></p>
<p>Voici l&#8217;implémentation de la méthode <code>NullSafeEval</code> :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
        public static TResult NullSafeEval&lt;TSource, TResult&gt;(this TSource source, Expression&lt;Func&lt;TSource, TResult&gt;&gt; expression, TResult defaultValue)
        {
            var safeExp = Expression.Lambda&lt;Func&lt;TSource, TResult&gt;&gt;(
                NullSafeEvalWrapper(expression.Body, Expression.Constant(defaultValue)),
                expression.Parameters[0]);

            var safeDelegate = safeExp.Compile();
            return safeDelegate(source);
        }

        private static Expression NullSafeEvalWrapper(Expression expr, Expression defaultValue)
        {
            Expression obj;
            Expression safe = expr;

            while (!IsNullSafe(expr, out obj))
            {
                var isNull = Expression.Equal(obj, Expression.Constant(null));

                safe =
                    Expression.Condition
                    (
                        isNull,
                        defaultValue,
                        safe
                    );

                expr = obj;
            }
            return safe;
        }

        private static bool IsNullSafe(Expression expr, out Expression nullableObject)
        {
            nullableObject = null;

            if (expr is MemberExpression || expr is MethodCallExpression)
            {
                Expression obj;
                MemberExpression memberExpr = expr as MemberExpression;
                MethodCallExpression callExpr = expr as MethodCallExpression;

                if (memberExpr != null)
                {
                    // Static fields don't require an instance
                    FieldInfo field = memberExpr.Member as FieldInfo;
                    if (field != null &amp;&amp; field.IsStatic)
                        return true;

                    // Static properties don't require an instance
                    PropertyInfo property = memberExpr.Member as PropertyInfo;
                    if (property != null)
                    {
                        MethodInfo getter = property.GetGetMethod();
                        if (getter != null &amp;&amp; getter.IsStatic)
                            return true;
                    }
                    obj = memberExpr.Expression;
                }
                else
                {
                    // Static methods don't require an instance
                    if (callExpr.Method.IsStatic)
                        return true;

                    obj = callExpr.Object;
                }

                // Value types can't be null
                if (obj.Type.IsValueType)
                    return true;

                // Instance member access or instance method call is not safe
                nullableObject = obj;
                return false;
            }
            return true;
        }
</pre></p>
<p>En résumé, ce code remonte l&#8217;arbre de l&#8217;expression lambda, en encadrant chaque appel à une propriété ou méthode d&#8217;instance par une expression conditionnelle <em>(condition ? valeur si vrai : valeur si faux)</em>.</p>
<p>Et voilà comment on utilise cette méthode :</p>
<p><pre class="brush: csharp;">
string name = xx.NullSafeEval(x =&gt; x.Foo.Bar.Baz.Name, &quot;Default&quot;);
</pre></p>
<p>C&#8217;est tout de même plus clair et plus concis que notre code initial <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Notez que l&#8217;implémentation proposée gère non seulement les accès aux propriétés, mais également les appels de méthode, on pourrait donc avoir quelque chose comme ça :</p>
<p><pre class="brush: csharp;">
string name = xx.NullSafeEval(x =&gt; x.Foo.GetBar(42).Baz.Name, &quot;Default&quot;);
</pre></p>
<p>Les indexeurs ne sont pas encore gérés, mais pourraient être ajoutés sans grande difficulté ; je vous laisse le soin de le faire si vous en avez l&#8217;usage <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><strong>Limitations</strong></p>
<p>Même si cette solution peut sembler très intéressante au premier abord, lisez la suite avant de vous précipiter pour intégrer ce code dans des programmes réels&#8230;</p>
<ul>
<li>Tout d&#8217;abord, le code proposé est avant tout un &#8220;proof of concept&#8221; et n&#8217;a pas subi de tests approfondis, sa fiabilité peut donc laisser à désirer.</li>
<li>Ensuite, il ne faut pas perdre de vue que la génération dynamique de code à partir d&#8217;une expression est très pénalisante pour les performances&#8230;
<p>Une piste possible pour limiter ce problème serait de mettre en cache les delegates obtenus pour chaque expression, de façon à ne pas les regénérer inutilement. Malheureusement, il n&#8217;y a pas (à ma connaissance) de moyen simple de comparer deux expressions Linq, ce qui complique sensiblement l&#8217;implémentation de ce cache&#8230;</li>
<li>D&#8217;autre part, vous aurez peut-être remarqué que les propriétés et méthodes intermédiaires de l&#8217;expression sont évaluées plusieurs fois ; non seulement cela peut avoir un impact non négligeable sur les performances, mais surtout, cela peut causer des effets de bord aux conséquences difficilement prévisibles&#8230;
<p>Une solution possible serait de réécrire l&#8217;expression de la façon suivante :</p>
<p><pre class="brush: csharp;">
Foo foo = null;
Bar bar = null;
Baz baz = null;
var name =
    (x == null)
    ? defaultValue
    : ((foo = x.Foo) == null)
      ? defaultValue
      : ((bar = foo.Bar) == null)
        ? defaultValue
        : ((baz = bar.Baz) == null)
          ? defaultValue
          : baz.Name;
</pre></p>
<p>Malheureusement, ce n&#8217;est pas possible en .NET 3.5 : en effet, cette version ne supporte que des expressions simples, il n&#8217;est donc pas possible de déclarer des variables ou de leur affecter une valeur, ni d&#8217;écrire plusieurs instructions distinctes. En revanche, en .NET 4.0, le support des expressions Linq a été grandement amélioré, et il est possible de générer ce type de code. J&#8217;ai commencé à transformer le code de <code>NullSafeEval</code> pour arriver à ce résultat, mais ça s&#8217;avère beaucoup plus complexe que prévu&#8230; je publierai la nouvelle méthode si j&#8217;arrive à quelque chose de concluant <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />
</li>
</ul>
<p>Au final, je ne recommande donc pas d&#8217;utiliser cette technique dans du code réel, en tous cas en l&#8217;état actuel. Cela donne cependant un aperçu intéressant des possibilités des expressions Linq. Sachez qu&#8217;elle sont également utilisées, entre autres :</p>
<ul>
<li>Pour la génération de requêtes SQL dans des ORM comme Linq to SQL ou Entity Framework</li>
<li>Pour construire dynamiquement des prédicats complexes, comme dans la classe <a href="http://www.albahari.com/nutshell/predicatebuilder.aspx">PredicateBuilder</a> de Joseph Albahari</li>
<li>Pour implementer la &#8220;réflexion statique&#8221; dont on a <a href="http://www.google.com/search?tbo=1&amp;tbs=blg:1&amp;q=static+reflection">beaucoup parlé sur les blogs</a> depuis quelques temps</li>
</ul>
<br /> Tagged: <a href='http://tomlev.wordpress.com/tag/c/'>C#</a>, <a href='http://tomlev.wordpress.com/tag/expression/'>expression</a>, <a href='http://tomlev.wordpress.com/tag/linq/'>linq</a>, <a href='http://tomlev.wordpress.com/tag/null-check/'>null check</a>, <a href='http://tomlev.wordpress.com/tag/proof-of-concept/'>proof of concept</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/294/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/294/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/294/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/294/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/294/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/294/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/294/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/294/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/294/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/294/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/294/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/294/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/294/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/294/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=294&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2010/02/20/automatiser-la-verification-des-null-avec-les-expressions-linq/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[VS 2010] Support du binding dans les InputBindings</title>
		<link>http://tomlev.wordpress.com/2009/10/25/vs-2010-support-du-binding-dans-les-inputbindings/</link>
		<comments>http://tomlev.wordpress.com/2009/10/25/vs-2010-support-du-binding-dans-les-inputbindings/#comments</comments>
		<pubDate>Sun, 25 Oct 2009 20:08:40 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[WPF]]></category>
		<category><![CDATA[.NET 4.0]]></category>
		<category><![CDATA[binding]]></category>
		<category><![CDATA[InputBinding]]></category>
		<category><![CDATA[MVVM]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[Visual Studio 2010]]></category>
		<category><![CDATA[wpf 4.0]]></category>
		<category><![CDATA[XAML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=271</guid>
		<description><![CDATA[LA fonctionnalité qui manquait à WPF ! La beta 2 de Visual Studio 2010 est là depuis quelques jours, et apporte à WPF une nouveauté que j&#8217;attendais depuis longtemps : le support du binding dans les InputBindings. Pour rappel, le problème de la version précédente était que la propriété Command de la classe InputBinding n&#8217;était [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=271&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>LA fonctionnalité qui manquait à WPF !</strong></p>
<p>La beta 2 de Visual Studio 2010 est là depuis quelques jours, et apporte à WPF une nouveauté que j&#8217;attendais depuis longtemps : le support du binding dans les <code>InputBindings</code>.</p>
<p>Pour rappel, le problème de la version précédente était que la propriété <code>Command</code> de la classe <code>InputBinding</code> n&#8217;était pas une <code>DependencyProperty</code>, on ne pouvait donc pas la définir par un binding. D&#8217;ailleurs, les <code>InputBindings</code> n&#8217;héritaient pas du <code>DataContext</code>, ce qui compliquait beaucoup les implémentations alternatives de cette fonctionnalité&#8230;</p>
<p>Jusqu&#8217;ici, pour lier la commande d&#8217;un <code>KeyBinding</code> ou <code>MouseBinding</code> à une propriété du <code>DataContext</code>, il fallait donc passer par des détours pas forcément très élégants&#8230; J&#8217;avais fini par trouver une solution acceptable, détaillée dans <a href="http://tomlev.wordpress.com/2009/03/17/wpf-utiliser-les-inputbindings-avec-le-pattern-mvvm/">ce post</a>, mais qui me laissait assez insatisfait (utilisation de la réflexion sur des membres privés, pas mal de limitations&#8230;).</p>
<p>J&#8217;ai découvert plus récemment le <a href="http://www.codeplex.com/wpf/Release/ProjectReleases.aspx?ReleaseId=14962">MVVM toolkit</a>, qui propose une approche un peu plus propre : une classe <code>CommandReference</code>, héritée de <code>Freezable</code>, qui permet de mettre dans les ressources de la page ou du contrôle une référence à la commande, qu&#8217;on peut ensuite utiliser avec <code>StaticResource</code>. Plus propre, mais ça restait peu intuitif&#8230;</p>
<p>WPF 4.0 résout le problème une bonne fois pour toutes : la classe <code>InputBinding</code> hérite maintenant de <code>Freezable</code>, ce qui lui permet d&#8217;hériter du <code>DataContext</code> parent. De plus les propriétés <code>Command</code>, <code>CommandParameter</code> et <code>CommandTarget</code> sont maintenant des <code>DependencyProperty</code>. On peut donc laisser tomber tous les bricolages qu&#8217;il fallait utiliser jusqu&#8217;à maintenant, et aller droit au but :</p>
<p><pre class="brush: xml;">
    &lt;Window.InputBindings&gt;
        &lt;KeyBinding Key=&quot;F5&quot; Command=&quot;{Binding RefreshCommand}&quot; /&gt;
    &lt;/Window.InputBindings&gt;
</pre></p>
<p>Voilà qui devrait faciliter un peu le développement d&#8217;applications MVVM !</p>
<p><strong>Help 3</strong></p>
<p>Je change un peu de sujet pour vous parler du nouveau système de documentation offline de Visual Studio 2010, appelé &#8220;Help 3&#8243;. Il se présente comme une application web installée localement, les pages s&#8217;affichent donc dans le navigateur. Globalement, c&#8217;est beaucoup mieux&#8230; largement plus léger et plus réactif que le vieux Document Explorer livré avec les versions précédentes de Visual Studio.</p>
<p>En revanche, une fonction qui m&#8217;était absolument indispensable a disparu : l&#8217;index ! Il ne reste que la vue en arborescence et la recherche. Plus de trace de l&#8217;index que j&#8217;utilisais en permanence pour accéder directement à une classe ou un membre, sans forcément connaître son namespace. En plus, les résultats de la recherche ne montre pas clairement le namespace : par exemple, si vous tapez &#8220;button class&#8221; dans la recherche, pas moyen de voir la différence entre <code>System.Windows.Forms.Button</code>, <code>System.Windows.Controls.Button</code> et <code>System.Web.UI.WebControls</code> ! Avant, le volet &#8220;Résultats de l&#8217;index&#8221; affichait cette information clairement.</p>
<p>Mon sentiment sur ce nouveau système d&#8217;aide est donc un peu mitigé&#8230; il va falloir que je change ma façon d&#8217;utiliser la doc. Mais à part ce détail agaçant, c&#8217;est objectivement un gros progrès !</p>
<br /> Tagged: .NET 4.0, binding, InputBinding, MVVM, Visual Studio, Visual Studio 2010, WPF, wpf 4.0, XAML <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/271/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=271&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/10/25/vs-2010-support-du-binding-dans-les-inputbindings/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[C# 4.0] Implémenter un objet dynamique personnalisé</title>
		<link>http://tomlev.wordpress.com/2009/10/06/c-4-0-implementer-un-objet-dynamique-personnalise/</link>
		<comments>http://tomlev.wordpress.com/2009/10/06/c-4-0-implementer-un-objet-dynamique-personnalise/#comments</comments>
		<pubDate>Tue, 06 Oct 2009 21:34:41 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[C# 4.0]]></category>
		<category><![CDATA[Code sample]]></category>
		<category><![CDATA[.NET 4.0]]></category>
		<category><![CDATA[DLR]]></category>
		<category><![CDATA[dynamic]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=261</guid>
		<description><![CDATA[Comme vous le savez sans doute déjà si vous vous intéressez à l’actualité de .NET, la future version 4.0 de C#, actuellement en beta, introduit un nouveau type appelé dynamic. Celui ci permet d’accéder à des propriétés ou méthodes d’un objet qui ne sont pas connus statiquement (à la compilation). Ils seront résolus dynamiquement à [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=261&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Comme vous le savez sans doute déjà si vous vous intéressez à l’actualité de .NET, la future version 4.0 de C#, actuellement en beta, introduit un nouveau type appelé <code>dynamic</code>. Celui ci permet d’accéder à des propriétés ou méthodes d’un objet qui ne sont pas connus statiquement (à la compilation). Ils seront résolus dynamiquement à l’exécution grâce au DLR (Dynamic Language Runtime), qui est l’une des grandes nouveautés de .NET 4.0. Cela permet notamment de faciliter la manipulation d’objets COM, ou de tout autre objet dont on ne connait pas statiquement le type. Pour plus d’informations sur le type <code>dynamic</code>, je vous invite à consulter <a href="http://msdn.microsoft.com/en-us/library/dd264736%28VS.100%29.aspx">la documentation MSDN</a>.</p>
<p>En jouant un peu avec la beta de Visual Studio 2010, je me suis rendu compte qu’on pouvait faire des choses très intéressantes avec ce type <code>dynamic</code>… En effet, il est possible de créer ses propres objets dynamiques, en contrôlant comment sont évalués les appels aux membres de l’objet. Il faut pour cela implémenter l’interface <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.idynamicmetaobjectprovider(VS.100).aspx"><code>IDynamicMetaObjectProvider</code></a>. Cette interface semble simple à première vue, dans la mesure où elle ne définit qu’un seul membre : la méthode <code>GetMetaObject</code>. Là où ça se complique un peu, c’est pour implémenter cette méthode… Il faut construire un <code>DynamicMetaObject</code> à partir d’une <code>Expression</code>, et j’avoue que je me suis tout d’abord découragé en voyant la complexité de la tâche.</p>
<p>Heureusement, il y a une technique beaucoup plus simple pour implémenter ses propres objets dynamiques : il suffit d’hériter de la classe <code><a href="http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicmetaobject(VS.100).aspx">DynamicObject</a></code>, qui fournit une implementation de base de <code>IDynamicMetaObjectProvider</code>. Il suffit ensuite de redéfinir les méthodes qui nous intéressent pour obtenir le comportement souhaité.</p>
<p>Pour le premier exemple, on va s’inspirer du langage Javascript, dans lequel il est possible d’ajouter des membres (propriétés ou méthodes) à un objet existant, de la façon suivante :</p>
<p><pre class="brush: jscript;">
var x = new Object();
x.Message = &quot;Hello world !&quot;;
x.ShowMessage = function()
{
  alert(this.Message);
};
x.ShowMessage();
</pre></p>
<p>Ce code crée un objet, lui ajoute une propriété <code>Message</code> en définissant sa valeur, et ajoute également une méthode <code>ShowMessage</code> qui affiche le message.</p>
<p>Dans les précédentes versions de C#, il était impossible de réaliser une telle chose : en effet C# est un langage à typage statique, ce qui implique que l’accès aux membres d’un objet est résolu à la compilation, et non à l’exécution. La classe <code>Object</code> n’ayant pas de propriété <code>Message</code> ou de méthode <code>ShowMessage</code>, on ne peut pas écrire <code>x.Message</code> ou <code>x.ShowMessage()</code>. Et c’est là qu’entre en jeu le type <code>dynamic</code>…</p>
<p>Nous allons donc créer un objet dynamique qui permet d’écrire en C# un code similaire au code Javascript ci-dessus. Pour cela, on va stocker dans un <code>Dictionary&lt;string, object&gt;</code> les valeurs des propriétés définies dynamiquement pour l’objet. La clé du fonctionnement de cette classe est de redéfinir les méthodes <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.trygetmember%28VS.100%29.aspx"><code>TryGetMember</code></a> et <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.trygetmember%28VS.100%29.aspx"><code>TrySetMember</code></a>. Ces méthodes implementent la logique permettant de lire ou d’écrire un membre de l’objet dynamique. Pour fixer les idées, voici le code, je le commenterai plus loin.</p>
<p><pre class="brush: csharp;">
public class MyDynamicObject : DynamicObject
{
    private Dictionary&lt;string, object&gt; _properties = new Dictionary&lt;string, object&gt;();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return _properties.TryGetValue(binder.Name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        _properties[binder.Name] = value;
        return true;
    }
}
</pre></p>
<p>Détaillons maintenant le code ci-dessus… La méthode <code>TryGetMember</code> tente d’obtenir la propriété demandée à partir du dictionnaire. Notez que le nom de la propriété est accessible via la propriété <code>Name</code> du paramètre <code>binder</code>. Si la propriété existe, sa valeur est renvoyée via le paramètre de sortie <code>result</code> et la méthode renvoie <code>true</code>. Dans le cas contraire, la méthode renvoie <code>false</code>, ce qui cause une exception de type <code>RuntimeBinderException</code> à l’endroit où la propriété est accédée en lecture. Cette erreur indique simplement l’échec de la résolution dynamique de la propriété.</p>
<p>La méthode <code>TrySetMember</code> effectue l’opération inverse : elle définit la valeur d’une propriété. Si le membre auquel on veut accéder n’existe pas, il est ajouté au dictionnaire, la méthode renvoie donc toujours <code>true</code>.</p>
<p>Voyons maintenant comment utiliser cet objet :</p>
<p><pre class="brush: csharp;">
dynamic x = new MyDynamicObject();
x.Message = &quot;Hello world !&quot;;
Console.WriteLine(x.Message);
</pre></p>
<p>Ce code fonctionne sans problème et affiche “Hello world !” dans la console… sympa, non ?</p>
<p>Et les méthodes dans tout ça ? Et bien, je pourrais vous dire qu’il faut redéfinir la méthode <code><a href="http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.tryinvokemember%28VS.100%29.aspx">TryInvokeMember</a></code>, qui sert à gérer les appels dynamiques de méthodes… Mais en fait, ce n’est même pas nécessaire ! Notre implémentation gère déjà cette fonctionnalité : il suffit d’affecter un delegate à une propriété de l’objet. Ce ne sera donc pas réellement une méthode membre de l’objet, mais plutôt une propriété qui renvoie un <code>Delegate</code> ; mais puisque le code pour invoquer ce delegate est identique à celui de l’appel d’une méthode, on s’en contentera pour l’instant <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> . Voilà un exemple d’ajout d’une méthode à l’objet :</p>
<p><pre class="brush: csharp;">
dynamic x = new MyDynamicObject();
x.Message = &quot;Hello world !&quot;;
x.ShowMessage = new Action(
    () =&gt;
    {
        Console.WriteLine(x.Message);
    });
x.ShowMessage();
</pre></p>
<p>On a donc, à peu de choses près, un code identique au code Javascript qu’on voulait imiter. Et tout ça avec une classe de moins de 10 lignes de code (sans compter les accolades)…</p>
<p>Cette petite classe peut être assez pratique comme objet à tout faire, par exemple pour regrouper des informations sans avoir à créer une classe spécifique. En cela elle est similaire à un type anonyme (déjà présent en C# 3), mais avec l’avantage qu’on peut utiliser l’objet comme valeur de retour d’une méthode, ce qui n’était pas possible avec un type anonyme.</p>
<p>Il est bien sûr possible de créer des objets dynamiques plus utiles que celui-ci… par exemple, voilà un petit wrapper pour un <code>DataRow</code>, pour faciliter l&#8217;accès aux champs :</p>
<p><pre class="brush: csharp;">
public class DynamicDataRow : DynamicObject
{
    private DataRow _dataRow;

    public DynamicDataRow(DataRow dataRow)
    {
        if (dataRow == null)
            throw new ArgumentNullException(&quot;dataRow&quot;);
        this._dataRow = dataRow;
    }

    public DataRow DataRow
    {
        get { return _dataRow; }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = null;
        if (_dataRow.Table.Columns.Contains(binder.Name))
        {
            result = _dataRow[binder.Name];
            return true;
        }
        return false;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (_dataRow.Table.Columns.Contains(binder.Name))
        {
            _dataRow[binder.Name] = value;
            return true;
        }
        return false;
    }
}
</pre></p>
<p>Ajoutons une petite méthode d’extension pour rendre plus naturelle l&#8217;utilisation de ce wrapper :</p>
<p><pre class="brush: csharp;">
public static class DynamicDataRowExtensions
{
    public static dynamic AsDynamic(this DataRow dataRow)
    {
        return new DynamicDataRow(dataRow);
    }
}
</pre></p>
<p>On peut maintenant écrire un code de ce genre :</p>
<p><pre class="brush: csharp;">
DataTable table = new DataTable();
table.Columns.Add(&quot;FirstName&quot;, typeof(string));
table.Columns.Add(&quot;LastName&quot;, typeof(string));
table.Columns.Add(&quot;DateOfBirth&quot;, typeof(DateTime));

dynamic row = table.NewRow().AsDynamic();
row.FirstName = &quot;John&quot;;
row.LastName = &quot;Doe&quot;;
row.DateOfBirth = new DateTime(1981, 9, 12);
table.Rows.Add(row.DataRow);

// Add more rows...
// ...

var bornInThe20thCentury = from r in table.AsEnumerable()
                           let dr = r.AsDynamic()
                           where dr.DateOfBirth.Year &gt; 1900
                           &amp;&amp; dr.DateOfBirth.Year &lt;= 2000
                           select new { dr.LastName, dr.FirstName };

foreach (var item in bornInThe20thCentury)
{
    Console.WriteLine(&quot;{0} {1}&quot;, item.FirstName, item.LastName);
}
</pre></p>
<p>Voilà, maintenant que vous connaissez le principe, vous pouvez donner libre cours à votre imagination <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><strong>Mise à jour :</strong> Aussitôt après la publication de cet article, voilà que je tombe sur la classe <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28VS.100%29.aspx"><code>ExpandoObject</code></a>, qui fait exactement la même chose que la classe <code>MyDynamicObject</code> ci-dessus&#8230; il semblerait donc que j&#8217;ai encore réinventé la roue ! Cela dit, il est toujours intéressant de voir le fonctionnement d&#8217;un objet dynamique, ne serait-ce qu&#8217;à des fins didactiques <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> . Pour plus d&#8217;infos sur <code>ExpandoObject</code>, voir <a href="http://blogs.msdn.com/csharpfaq/archive/2009/10/01/dynamic-in-c-4-0-introducing-the-expandoobject.aspx">ce billet sur le blog C# FAQ</a></p>
<br /> Tagged: .NET 4.0, C# 4.0, DLR, dynamic <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/261/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/261/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/261/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/261/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/261/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/261/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/261/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/261/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/261/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/261/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/261/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/261/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/261/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/261/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=261&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/10/06/c-4-0-implementer-un-objet-dynamique-personnalise/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Markup extensions et templates</title>
		<link>http://tomlev.wordpress.com/2009/08/22/wpf-markup-extensions-et-templates/</link>
		<comments>http://tomlev.wordpress.com/2009/08/22/wpf-markup-extensions-et-templates/#comments</comments>
		<pubDate>Fri, 21 Aug 2009 23:24:37 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[markup extension]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[XAML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=224</guid>
		<description><![CDATA[Note&#160;: Ce billet est la suite de celui sur une markup extension qui met à jour sa cible, et réutilise le même code de départ. Vous avez peut-être remarqué que l&#8217;utilisation d&#8217;une markup extension personnalisée dans un template donnait parfois des résultats inattendus&#8230; Nous allons voir dans ce billet comment faire une markup extension qui [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=224&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><em>Note&nbsp;: Ce billet est la suite de celui sur <a href="http://tomlev.wordpress.com/2009/07/28/wpf-une-markup-extension-qui-met-a-jour-sa-cible/">une markup extension qui met à jour sa cible</a>, et réutilise le même code de départ.</em></p>
<p>Vous avez peut-être remarqué que l&#8217;utilisation d&#8217;une markup extension personnalisée dans un template donnait parfois des résultats inattendus&#8230; Nous allons voir dans ce billet comment faire une markup extension qui se comporte correctement dans un template.</p>
<p><strong>Illustration du problème</strong></p>
<p>Reprenons l&#8217;exemple du précédent billet&nbsp;: une markup extension qui renvoie l&#8217;état de la connectivité réseau, et met à jour la propriété cible quand le réseau est connecté ou déconnecté&nbsp;:</p>
<p><pre class="brush: xml;">
&lt;CheckBox IsChecked=&quot;{my:NetworkAvailable}&quot; Content=&quot;Network is available&quot; /&gt;
</pre></p>
<p>Mettons maintenant la même <code>CheckBox</code> dans un <code>ControlTemplate</code>&nbsp;:</p>
<p><pre class="brush: xml;">
&lt;ControlTemplate x:Key=&quot;test&quot;&gt;
  &lt;CheckBox IsChecked=&quot;{my:NetworkAvailable}&quot; Content=&quot;Network is available&quot; /&gt;
&lt;/ControlTemplate&gt;
</pre></p>
<p>Et créons un contrôle qui utilise ce template&nbsp;:</p>
<p><pre class="brush: xml;">
&lt;Control Template=&quot;{StaticResource test}&quot; /&gt;
</pre></p>
<p>Si on se déconnecte du réseau, on remarque que <code>la CheckBox</code> n&#8217;est pas automatiquement mise à jour par la <code>NetworkAvailableExtension</code>, alors que ça fonctionnait bien quand on l&#8217;utilisait hors du template&#8230;</p>
<p><strong>Explication et solution</strong></p>
<p>La markup expression est évaluée quand elle est rencontrée par le parser XAML&nbsp;: en l&#8217;occurrence, lors du parsing du template. Or, à cet instant le contrôle <code>CheckBox</code> n&#8217;est pas encore créé, la méthode <code>ProvideValue</code> ne peut donc pas y accéder&#8230; Quand une markup extension est évaluée dans un template, le <code>TargetObject</code> est en fait un objet de type <code>System.Windows.SharedDp</code>, qui est une classe interne de WPF.</p>
<p>Pour que la markup extension puisse accéder à  sa cible, il faut qu&#8217;elle soit évaluée lorsque le template est appliqué&nbsp;: on doit donc retarder son évaluation. Pour y arriver, il suffit en fait de renvoyer l&#8217;extension elle-même comme valeur de retour de <code>ProvideValue</code>&nbsp;: de cette façon, elle sera de nouveau évaluée lors de la création du contrôle cible.</p>
<p>Pour savoir si l&#8217;extension est appelée pour le template ou pour un contrôle &#8220;réel&#8221;, il suffit de tester si le type du <code>TargetObject</code> est <code>System.Windows.SharedDp</code>. Le code de la méthode <code>ProvideValue</code> devient donc&nbsp;:</p>
<p><pre class="brush: csharp;">
        public sealed override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
            if (target != null)
            {
                if (target.TargetObject.GetType().FullName == &quot;System.Windows.SharedDp&quot;)
                    return this;
                _targetObject = target.TargetObject;
                _targetProperty = target.TargetProperty;
            }

            return ProvideValueInternal(serviceProvider);
        }
</pre></p>
<p>Voilà, c&#8217;est réparé, la <code>CheckBox</code> se met à nouveau à jour en cas de changement de connectivité réseau <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><strong>Encore un os</strong></p>
<p>Mais ne crions pas victoire trop vite, on n&#8217;est pas encore tout à fait au bout de nos peines&#8230; Que se passe-t-il si on souhaite maintenant utiliser notre <code>ControlTemplate</code> sur plusieurs contrôles ?</p>
<p><pre class="brush: xml;">
&lt;Control Template=&quot;{StaticResource test}&quot; /&gt;
&lt;Control Template=&quot;{StaticResource test}&quot; /&gt;
</pre></p>
<p>Résultat&nbsp;: la seconde checkbox se met à jour, mais pas la première&#8230;</p>
<p>La raison est simple&nbsp;: il y a deux contrôles <code>CheckBox</code>, mais une seule instance de <code>NetworkAvailableExtension</code>, partagée entre toutes les instances du template. Or <code>NetworkAvailableExtension</code> ne peut référencer qu&#8217;un seul objet cible, c&#8217;est donc le dernier pour lequel <code>ProvideValue</code> a été appelée qui est conservé&#8230;</p>
<p>Il suffit donc de gérer non pas un objet cible, mais une collection d&#8217;objets cibles, qui seront tous mis à jour dans la méthode <code>UpdateValue</code>. Voilà le code final de la classe de base <code>UpdatableMarkupExtension</code>&nbsp;:</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    public abstract class UpdatableMarkupExtension : MarkupExtension
    {
        private List&lt;object&gt; _targetObjects = new List&lt;object&gt;();
        private object _targetProperty;

        protected IEnumerable&lt;object&gt; TargetObjects
        {
            get { return _targetObjects; }
        }

        protected object TargetProperty
        {
            get { return _targetProperty; }
        }

        public sealed override object ProvideValue(IServiceProvider serviceProvider)
        {
            // Retrieve target information
            IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;

            if (target != null &amp;&amp; target.TargetObject != null)
            {
                // In a template the TargetObject is a SharedDp (internal WPF class)
                // In that case, the markup extension itself is returned to be re-evaluated later
                if (target.TargetObject.GetType().FullName == &quot;System.Windows.SharedDp&quot;)
                    return this;

                // Save target information for later updates
                _targetObjects.Add(target.TargetObject);
                _targetProperty = target.TargetProperty;
            }

            // Delegate the work to the derived class
            return ProvideValueInternal(serviceProvider);
        }

        protected virtual void UpdateValue(object value)
        {
            if (_targetObjects.Count &gt; 0)
            {
                // Update the target property of each target object
                foreach (var target in _targetObjects)
                {
                    if (_targetProperty is DependencyProperty)
                    {
                        DependencyObject obj = target as DependencyObject;
                        DependencyProperty prop = _targetProperty as DependencyProperty;

                        Action updateAction = () =&gt; obj.SetValue(prop, value);

                        // Check whether the target object can be accessed from the
                        // current thread, and use Dispatcher.Invoke if it can't

                        if (obj.CheckAccess())
                            updateAction();
                        else
                            obj.Dispatcher.Invoke(updateAction);
                    }
                    else // _targetProperty is PropertyInfo
                    {
                        PropertyInfo prop = _targetProperty as PropertyInfo;
                        prop.SetValue(target, value, null);
                    }
                }
            }
        }

        protected abstract object ProvideValueInternal(IServiceProvider serviceProvider);
    }
</pre></p>
<p>La classe <code>UpdatableMarkupExtension</code> est donc maintenant pleinement opérationnelle&#8230; jusqu&#8217;à preuve du contraire <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> . Cette classe constitue une bonne base pour toute markup extension devant mettre à jour sa cible, sans avoir à se préoccuper des aspects &#8220;bas niveau&#8221; du suivi des objets cibles et de leur mise à jour.</p>
<br /> Tagged: markup extension, template, WPF, XAML <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/224/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=224&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/08/22/wpf-markup-extensions-et-templates/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Tri automatique d’un GridView : suite</title>
		<link>http://tomlev.wordpress.com/2009/08/04/wpf-tri-automatique-d%e2%80%99un-gridview-suite/</link>
		<comments>http://tomlev.wordpress.com/2009/08/04/wpf-tri-automatique-d%e2%80%99un-gridview-suite/#comments</comments>
		<pubDate>Mon, 03 Aug 2009 23:11:20 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[GridView]]></category>
		<category><![CDATA[propriété attachée]]></category>
		<category><![CDATA[tri]]></category>
		<category><![CDATA[XAML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=212</guid>
		<description><![CDATA[Il y a quelques mois, j&#8217;avais publié un billet où j&#8217;expliquais comment trier automatiquement un GridView lors du clic sur un en-tête de colonne. J&#8217;avais laissé un point ouvert : l&#8217;ajout d&#8217;un symbole dans l&#8217;en-tête de colonne pour indiquer visuellement la direction du tri. C&#8217;est maintenant chose faite ! Pour arriver à ce résultat, j&#8217;ai [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=212&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Il y a quelques mois, j&#8217;avais publié <a href="http://tomlev.wordpress.com/2009/03/27/wpf-tri-automatique-dun-gridview-lors-du-clic-sur-une-colonne/">un billet</a> où j&#8217;expliquais comment trier automatiquement un GridView lors du clic sur un en-tête de colonne. J&#8217;avais laissé un point ouvert : l&#8217;ajout d&#8217;un symbole dans l&#8217;en-tête de colonne pour indiquer visuellement la direction du tri. C&#8217;est maintenant chose faite !</p>
<div id="attachment_215" class="wp-caption aligncenter" style="width: 310px"><a href="http://tomlev.files.wordpress.com/2009/08/gridviewsort.png"><img src="http://tomlev.files.wordpress.com/2009/08/gridviewsort.png?w=780" alt="Exemple GridViewSort avec symbole de tri" title="Exemple GridViewSort avec symbole de tri"   class="size-full wp-image-215" /></a><p class="wp-caption-text">Exemple GridViewSort avec symbole de tri</p></div>
<p>Pour arriver à ce résultat, j&#8217;ai utilisé un <code>Adorner</code> : c&#8217;est un composant qui permet de dessiner &#8220;par-dessus&#8221; un élément graphique existant, sur une couche de dessin indépendante.</p>
<p>La nouvelle version de la classe <code>GridViewSort</code> peut s&#8217;utiliser de la même façon qu&#8217;avant, la grille affiche alors des symboles de tri par défaut. Ils ne sont pas particulièrement &#8220;jolis&#8221;, donc si vous vous sentez une âme d&#8217;artiste, vous pouvez fournir vos propres images de la façon suivante :</p>
<p><pre class="brush: xml;">
        &lt;ListView ItemsSource=&quot;{Binding Persons}&quot;
                  IsSynchronizedWithCurrentItem=&quot;True&quot;
                  util:GridViewSort.AutoSort=&quot;True&quot;
                  util:GridViewSort.SortGlyphAscending=&quot;/Images/up.png&quot;
                  util:GridViewSort.SortGlyphDescending=&quot;/Images/down.png&quot;&gt;
</pre></p>
<p>Il est aussi possible de désactiver les symboles de tri, en mettant à <code>false</code> la propriété attachée <code>ShowSortGlyph</code> :</p>
<p><pre class="brush: csharp;">
        &lt;ListView ItemsSource=&quot;{Binding Persons}&quot;
                  IsSynchronizedWithCurrentItem=&quot;True&quot;
                  util:GridViewSort.AutoSort=&quot;True&quot;
                  util:GridViewSort.ShowSortGlyph=&quot;False&quot;&gt;
</pre></p>
<p>Notez qu&#8217;en l&#8217;état actuel, la gestion du symbole de tri ne fonctionne qu&#8217;en mode de tri automatique (<code>AutoSort</code> = <code>true</code>). Le cas d&#8217;un tri personnalisé avec la propriété <code>Command</code> n&#8217;est pas encore géré.</p>
<p>Voici le code complet de la nouvelle classe <code>GridViewSort</code> (un peu plus dense que le précédent&#8230;) :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Documents;

namespace Wpf.Util
{
    public class GridViewSort
    {
        #region Public attached properties

        public static ICommand GetCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(CommandProperty, value);
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached(
                &quot;Command&quot;,
                typeof(ICommand),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    null,
                    (o, e) =&gt;
                    {
                        ItemsControl listView = o as ItemsControl;
                        if (listView != null)
                        {
                            if (!GetAutoSort(listView)) // Don't change click handler if AutoSort enabled
                            {
                                if (e.OldValue != null &amp;&amp; e.NewValue == null)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (e.OldValue == null &amp;&amp; e.NewValue != null)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static bool GetAutoSort(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoSortProperty);
        }

        public static void SetAutoSort(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoSortProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoSort.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoSortProperty =
            DependencyProperty.RegisterAttached(
                &quot;AutoSort&quot;,
                typeof(bool),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    false,
                    (o, e) =&gt;
                    {
                        ListView listView = o as ListView;
                        if (listView != null)
                        {
                            if (GetCommand(listView) == null) // Don't change click handler if a command is set
                            {
                                bool oldValue = (bool)e.OldValue;
                                bool newValue = (bool)e.NewValue;
                                if (oldValue &amp;&amp; !newValue)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (!oldValue &amp;&amp; newValue)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static string GetPropertyName(DependencyObject obj)
        {
            return (string)obj.GetValue(PropertyNameProperty);
        }

        public static void SetPropertyName(DependencyObject obj, string value)
        {
            obj.SetValue(PropertyNameProperty, value);
        }

        // Using a DependencyProperty as the backing store for PropertyName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.RegisterAttached(
                &quot;PropertyName&quot;,
                typeof(string),
                typeof(GridViewSort),
                new UIPropertyMetadata(null)
            );

        public static bool GetShowSortGlyph(DependencyObject obj)
        {
            return (bool)obj.GetValue(ShowSortGlyphProperty);
        }

        public static void SetShowSortGlyph(DependencyObject obj, bool value)
        {
            obj.SetValue(ShowSortGlyphProperty, value);
        }

        // Using a DependencyProperty as the backing store for ShowSortGlyph.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShowSortGlyphProperty =
            DependencyProperty.RegisterAttached(&quot;ShowSortGlyph&quot;, typeof(bool), typeof(GridViewSort), new UIPropertyMetadata(true));

        public static ImageSource GetSortGlyphAscending(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(SortGlyphAscendingProperty);
        }

        public static void SetSortGlyphAscending(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(SortGlyphAscendingProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortGlyphAscending.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SortGlyphAscendingProperty =
            DependencyProperty.RegisterAttached(&quot;SortGlyphAscending&quot;, typeof(ImageSource), typeof(GridViewSort), new UIPropertyMetadata(null));

        public static ImageSource GetSortGlyphDescending(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(SortGlyphDescendingProperty);
        }

        public static void SetSortGlyphDescending(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(SortGlyphDescendingProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortGlyphDescending.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SortGlyphDescendingProperty =
            DependencyProperty.RegisterAttached(&quot;SortGlyphDescending&quot;, typeof(ImageSource), typeof(GridViewSort), new UIPropertyMetadata(null));

        #endregion

        #region Private attached properties

        private static GridViewColumnHeader GetSortedColumnHeader(DependencyObject obj)
        {
            return (GridViewColumnHeader)obj.GetValue(SortedColumnHeaderProperty);
        }

        private static void SetSortedColumnHeader(DependencyObject obj, GridViewColumnHeader value)
        {
            obj.SetValue(SortedColumnHeaderProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortedColumn.  This enables animation, styling, binding, etc...
        private static readonly DependencyProperty SortedColumnHeaderProperty =
            DependencyProperty.RegisterAttached(&quot;SortedColumnHeader&quot;, typeof(GridViewColumnHeader), typeof(GridViewSort), new UIPropertyMetadata(null));

        #endregion

        #region Column header click event handler

        private static void ColumnHeader_Click(object sender, RoutedEventArgs e)
        {
            GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
            if (headerClicked != null &amp;&amp; headerClicked.Column != null)
            {
                string propertyName = GetPropertyName(headerClicked.Column);
                if (!string.IsNullOrEmpty(propertyName))
                {
                    ListView listView = GetAncestor&lt;ListView&gt;(headerClicked);
                    if (listView != null)
                    {
                        ICommand command = GetCommand(listView);
                        if (command != null)
                        {
                            if (command.CanExecute(propertyName))
                            {
                                command.Execute(propertyName);
                            }
                        }
                        else if (GetAutoSort(listView))
                        {
                            ApplySort(listView.Items, propertyName, listView, headerClicked);
                        }
                    }
                }
            }
        }

        #endregion

        #region Helper methods

        public static T GetAncestor&lt;T&gt;(DependencyObject reference) where T : DependencyObject
        {
            DependencyObject parent = VisualTreeHelper.GetParent(reference);
            while (!(parent is T))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }
            if (parent != null)
                return (T)parent;
            else
                return null;
        }

        public static void ApplySort(ICollectionView view, string propertyName, ListView listView, GridViewColumnHeader sortedColumnHeader)
        {
            ListSortDirection direction = ListSortDirection.Ascending;
            if (view.SortDescriptions.Count &gt; 0)
            {
                SortDescription currentSort = view.SortDescriptions[0];
                if (currentSort.PropertyName == propertyName)
                {
                    if (currentSort.Direction == ListSortDirection.Ascending)
                        direction = ListSortDirection.Descending;
                    else
                        direction = ListSortDirection.Ascending;
                }
                view.SortDescriptions.Clear();

                GridViewColumnHeader currentSortedColumnHeader = GetSortedColumnHeader(listView);
                if (currentSortedColumnHeader != null)
                {
                    RemoveSortGlyph(currentSortedColumnHeader);
                }
            }
            if (!string.IsNullOrEmpty(propertyName))
            {
                view.SortDescriptions.Add(new SortDescription(propertyName, direction));
                if (GetShowSortGlyph(listView))
                    AddSortGlyph(
                        sortedColumnHeader,
                        direction,
                        direction == ListSortDirection.Ascending ? GetSortGlyphAscending(listView) : GetSortGlyphDescending(listView));
                SetSortedColumnHeader(listView, sortedColumnHeader);
            }
        }

        private static void AddSortGlyph(GridViewColumnHeader columnHeader, ListSortDirection direction, ImageSource sortGlyph)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(columnHeader);
            adornerLayer.Add(
                new SortGlyphAdorner(
                    columnHeader,
                    direction,
                    sortGlyph
                    ));
        }

        private static void RemoveSortGlyph(GridViewColumnHeader columnHeader)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(columnHeader);
            Adorner[] adorners = adornerLayer.GetAdorners(columnHeader);
            if (adorners != null)
            {
                foreach (Adorner adorner in adorners)
                {
                    if (adorner is SortGlyphAdorner)
                        adornerLayer.Remove(adorner);
                }
            }
        }

        #endregion

        #region SortGlyphAdorner nested class

        private class SortGlyphAdorner : Adorner
        {
            private GridViewColumnHeader _columnHeader;
            private ListSortDirection _direction;
            private ImageSource _sortGlyph;

            public SortGlyphAdorner(GridViewColumnHeader columnHeader, ListSortDirection direction, ImageSource sortGlyph)
                : base(columnHeader)
            {
                _columnHeader = columnHeader;
                _direction = direction;
                _sortGlyph = sortGlyph;
            }

            private Geometry GetDefaultGlyph()
            {
                double x1 = _columnHeader.ActualWidth - 13;
                double x2 = x1 + 10;
                double x3 = x1 + 5;
                double y1 = _columnHeader.ActualHeight / 2 - 3;
                double y2 = y1 + 5;

                if (_direction == ListSortDirection.Ascending)
                {
                    double tmp = y1;
                    y1 = y2;
                    y2 = tmp;
                }

                PathSegmentCollection pathSegmentCollection = new PathSegmentCollection();
                pathSegmentCollection.Add(new LineSegment(new Point(x2, y1), true));
                pathSegmentCollection.Add(new LineSegment(new Point(x3, y2), true));

                PathFigure pathFigure = new PathFigure(
                    new Point(x1, y1),
                    pathSegmentCollection,
                    true);

                PathFigureCollection pathFigureCollection = new PathFigureCollection();
                pathFigureCollection.Add(pathFigure);

                PathGeometry pathGeometry = new PathGeometry(pathFigureCollection);
                return pathGeometry;
            }

            protected override void OnRender(DrawingContext drawingContext)
            {
                base.OnRender(drawingContext);

                if (_sortGlyph != null)
                {
                    double x = _columnHeader.ActualWidth - 13;
                    double y = _columnHeader.ActualHeight / 2 - 5;
                    Rect rect = new Rect(x, y, 10, 10);
                    drawingContext.DrawImage(_sortGlyph, rect);
                }
                else
                {
                    drawingContext.DrawGeometry(Brushes.LightGray, new Pen(Brushes.Gray, 1.0), GetDefaultGlyph());
                }
            }
        }

        #endregion
    }
}
</pre></p>
<p>Voilà, j&#8217;espère que ça vous sera utile <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<br /> Tagged: GridView, propriété attachée, tri, WPF, XAML <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/212/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/212/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/212/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/212/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/212/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/212/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/212/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/212/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/212/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/212/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/212/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/212/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/212/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/212/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=212&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/08/04/wpf-tri-automatique-d%e2%80%99un-gridview-suite/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2009/08/gridviewsort.png" medium="image">
			<media:title type="html">Exemple GridViewSort avec symbole de tri</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Une markup extension qui met à jour sa cible</title>
		<link>http://tomlev.wordpress.com/2009/07/28/wpf-une-markup-extension-qui-met-a-jour-sa-cible/</link>
		<comments>http://tomlev.wordpress.com/2009/07/28/wpf-une-markup-extension-qui-met-a-jour-sa-cible/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 01:50:26 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[markup extension]]></category>
		<category><![CDATA[XAML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=202</guid>
		<description><![CDATA[Si vous avez lu mes précédents billets sur le sujet, vous savez que je suis un grand fan des markup extensions&#8230; Cependant, celles-ci ont une limitation qui peut s&#8217;avérer assez gênante&#160;: elles ne sont évaluées qu&#8217;une seule fois. Il serait pourtant utile de pouvoir les réévaluer pour mettre à jour la propriété cible, comme pour [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=202&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Si vous avez lu mes précédents billets sur le sujet, vous savez que je suis un grand fan des markup extensions&#8230; Cependant, celles-ci ont une limitation qui peut s&#8217;avérer assez gênante&nbsp;: elles ne sont évaluées qu&#8217;une seule fois. Il serait pourtant utile de pouvoir les réévaluer pour mettre à jour la propriété cible, comme pour un binding&#8230; Cela peut être utile dans différents cas, notamment&nbsp;:</p>
<ul>
<li>si la valeur de la markup extension peut changer en réponse à un évènement</li>
<li>si l&#8217;état de l&#8217;objet cible quand la markup extension est évaluée ne permet pas encore de déterminer la valeur à renvoyer, et qu&#8217;il faut différer l&#8217;évaluation (par exemple si l&#8217;on a besoin du DataContext de l&#8217;objet, et que celui-ci n&#8217;est pas encore défini lors de l&#8217;évaluation)</li>
</ul>
<p>Voyons donc comment on peut obtenir le comportement voulu&#8230;</p>
<p>La méthode <code>ProvideValue</code> d&#8217;une markup extension prend un paramètre de type <code>IServiceProvider</code>, qui fournit, entre autres, un service <code>IProvideValueTarget</code>. Cette interface expose des propriétés <code>TargetObject</code> et <code>TargetProperty</code>, qui permettent d&#8217;obtenir l&#8217;objet et la propriété cibles de la markup extension. Il est donc possible, si l&#8217;on sauvegarde cette information, de mettre à jour la propriété concernée alors que la markup extension a déjà été évaluée.</p>
<p>On va donc créer un classe abstraite <code>UpdatableMarkupExtension</code>, qui sauvegarde l&#8217;objet et la propriété cible et fournit une méthode pour mettre à jour la valeur&nbsp;:</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    public abstract class UpdatableMarkupExtension : MarkupExtension
    {
        private object _targetObject;
        private object _targetProperty;

        protected object TargetObject
        {
            get { return _targetObject; }
        }

        protected object TargetProperty
        {
            get { return _targetProperty; }
        }

        public sealed override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
            if (target != null)
            {
                _targetObject = target.TargetObject;
                _targetProperty = target.TargetProperty;
            }

            return ProvideValueInternal(serviceProvider);
        }

        protected void UpdateValue(object value)
        {
            if (_targetObject != null)
            {
                if (_targetProperty is DependencyProperty)
                {
                    DependencyObject obj = _targetObject as DependencyObject;
                    DependencyProperty prop = _targetProperty as DependencyProperty;

                    Action updateAction = () =&gt;  obj.SetValue(prop, value);

                    // Check whether the target object can be accessed from the
                    // current thread, and use Dispatcher.Invoke if it can't

                    if (obj.CheckAccess())
                        updateAction();
                    else
                        obj.Dispatcher.Invoke(updateAction);
                }
                else // _targetProperty is PropertyInfo
                {
                    PropertyInfo prop = _targetProperty as PropertyInfo;
                    prop.SetValue(_targetObject, value, null);
                }
            }
        }

        protected abstract object ProvideValueInternal(IServiceProvider serviceProvider);
    }
</pre></p>
<p>Comme il est indispensable de sauvegarder l&#8217;objet et la propriété cibles, on marque la méthode <code>ProvideValue</code> comme <code>sealed</code> pour qu&#8217;elle ne puisse pas être redéfinie, et on définit à la place une méthode abstraite <code>ProvideValueInternal</code> pour que les classes dérivées puissent fournir leur implémentation.</p>
<p>La méthode <code>UpdateValue</code> gère la mise à jour de la propriété cible, qui peut être soit une propriété de dépendance (<code>DependencyProperty</code>), soit une propriété CLR classique (<code>PropertyInfo</code>). Dans le cas d&#8217;une <code>DependencyProperty</code>, l&#8217;objet cible hérite de <code>DependencyObject</code>, et donc de <code>DispatcherObject</code>&nbsp;: il faut donc s&#8217;assurer qu&#8217;on n&#8217;accède à cet objet qu&#8217;à partir du thread qui le possède, à l&#8217;aide des méthodes <code>CheckAccess</code> et <code>Invoke</code>.</p>
<p>Voyons maintenant comment utiliser cette classe, au travers d&#8217;un exemple simple. Supposons qu&#8217;on souhaite réaliser une markup extension qui indique si le réseau est disponible, et qui s&#8217;utiliserait de la façon suivante&nbsp;:</p>
<p><pre class="brush: xml;">
&lt;CheckBox IsChecked=&quot;{my:NetworkAvailable}&quot; Content=&quot;Network is available&quot; /&gt;
</pre></p>
<p>On souhaite que la checkbox se mette à jour si l&#8217;état de la connexion change (cable branché ou débranché, Wifi hors de portée&#8230;). Il faut donc gérer l&#8217;évènement <code>NetworkChange.NetworkAvailabilityChanged</code>, et mettre à jour la propriété <code>IsChecked</code> en conséquence. Notre extension va donc utiliser les fonctionnalités implémentées dans la classe <code>UpdatableMarkupExtension</code>&nbsp;:</p>
<p><pre class="brush: csharp;">
    public class NetworkAvailableExtension : UpdatableMarkupExtension
    {
        public NetworkAvailableExtension()
        {
            NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
        }

        protected override object ProvideValueInternal(IServiceProvider serviceProvider)
        {
            return NetworkInterface.GetIsNetworkAvailable();
        }

        private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
        {
            UpdateValue(e.IsAvailable);
        }
    }
</pre></p>
<p>Notez qu&#8217;on s&#8217;abonne à l&#8217;évènement <code>NetworkAvailabilityChanged</code> dans le constructeur de la classe. Si on voulait s&#8217;abonner à un évènement de l&#8217;objet cible, il faudrait plutôt le faire dans la méthode <code>ProvideValueInternal</code>,  pour avoir accès à l&#8217;objet cible.</p>
<p>On voit donc qu&#8217;il est très simple d&#8217;implémenter une markup extension capable de mettre à jour sa cible a posteriori, après la première évaluation. Cela permet d&#8217;obtenir un fonctionnement similaire à celui du binding, mais sans être limité aux propriétés de dépendance. J&#8217;ai notamment utilisé cette possibilité pour réaliser un système de localisation qui permet de changer la langue &#8220;à la volée&#8221;, sans redémarrer le programme.</p>
<p><strong>Mise à jour&nbsp;:</strong> En l&#8217;état actuel, cette markup extension ne supporte pas l&#8217;utilisation dans un template. Pour une explication et une solution à ce problème, lisez <a href="http://tomlev.wordpress.com/?p=224">ce billet</a>.</p>
<br /> Tagged: markup extension, WPF, XAML <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/202/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/202/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/202/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/202/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/202/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/202/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/202/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/202/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/202/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/202/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/202/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/202/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/202/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/202/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=202&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/07/28/wpf-une-markup-extension-qui-met-a-jour-sa-cible/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[C#] Relation parent/enfant et sérialisation XML</title>
		<link>http://tomlev.wordpress.com/2009/06/11/c-relation-parentenfant-et-serialisation-xml/</link>
		<comments>http://tomlev.wordpress.com/2009/06/11/c-relation-parentenfant-et-serialisation-xml/#comments</comments>
		<pubDate>Thu, 11 Jun 2009 22:19:15 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Astuces]]></category>
		<category><![CDATA[Code sample]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[collection]]></category>
		<category><![CDATA[parent/enfant]]></category>
		<category><![CDATA[sérialisation XML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=180</guid>
		<description><![CDATA[Me revoilà avec un peu de retard, j&#8217;ai un peu manqué de temps libre ces dernières semaines&#8230; Voilà donc un petit post pour présenter une idée qui m&#8217;est venue récemment. Pour une fois, il ne sera pas question de WPF, c&#8217;est de conception C# qu&#8217;il s&#8217;agit ! Le problème Il est assez courant, dans un [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=180&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Me revoilà avec un peu de retard, j&#8217;ai un peu manqué de temps libre ces dernières semaines&#8230; Voilà donc un petit post pour présenter une idée qui m&#8217;est venue récemment. Pour une fois, il ne sera pas question de WPF, c&#8217;est de conception C# qu&#8217;il s&#8217;agit !</p>
<p><strong>Le problème</strong></p>
<p>Il est assez courant, dans un programme, d&#8217;avoir un objet parent qui possède une collection d&#8217;enfants ayant une référence vers leur parent. C&#8217;est le cas, par exemple, des contrôles Windows Forms, qui possèdent une collection de contrôles enfants (<code>Controls</code>), et une propriété qui indique le contrôle parent (<code>Parent</code>).</p>
<p>Ce type de structure est assez simple à réaliser, cela nécessite juste un peu de code pour maintenir la cohérence de la relation. Là où ça se complique un peu, c&#8217;est quand on veut sérialiser l&#8217;objet parent en XML&#8230; Prenons un exemple simple (purement théorique bien sûr) :</p>
<p><pre class="brush: csharp;">
    public class Parent
    {
        public Parent()
        {
            this.Children = new List&lt;Child&gt;();
        }

        public string Name { get; set; }

        public List&lt;Child&gt; Children { get; set; }

        public void AddChild(Child child)
        {
            child.ParentObject = this;
            this.Children.Add(child);
        }

        public void RemoveChild(Child child)
        {
            this.Children.Remove(child);
            child.ParentObject = null;
        }
    }
</pre></p>
<p><pre class="brush: csharp;">
    public class Child
    {
        public string Name { get; set; }

        public Parent ParentObject { get; set; }
    }
</pre></p>
<p>Créons une instance de <code>Parent</code> avec quelques enfants, et essayons de la sérialiser en XML :</p>
<p><pre class="brush: csharp;">
            Parent p = new Parent { Name = &quot;The parent&quot; };
            p.AddChild(new Child { Name = &quot;First child&quot; });
            p.AddChild(new Child { Name = &quot;Second child&quot; });

            string xml;
            XmlSerializer xs = new XmlSerializer(typeof(Parent));
            using (StringWriter wr = new StringWriter())
            {
                xs.Serialize(wr, p);
                xml = wr.ToString();
            }

            Console.WriteLine(xml);
</pre></p>
<p>Quand on sérialise l&#8217;objet <code>Parent</code>, il se produit une <code>InvalidOperationException</code> due à une référence circulaire : en effet, le parent référence les enfants, qui référencent le parent, qui référence les enfants&#8230; et ainsi de suite. La première solution qui vient à l&#8217;esprit est de ne pas sérialiser la propriété <code>Child.ParentObject</code> : il suffit pour cela de lui appliquer l&#8217;attribut <code>XmlIgnore</code>. La sérialisation se passe alors sans problème, mais quand on désérialise, mauvaise surprise : la propriété <code>ParentObject</code> n&#8217;est pas renseignée, et pour cause, puisqu&#8217;elle n&#8217;a pas été sauvegardée&#8230;</p>
<p>On pourrait opter pour une solution simple : après la désérialisation : parcourir la liste des enfants pour affecter manuellement la propriété <code>ParentObject</code>. Mais ce n&#8217;est vraiment pas très élégant&#8230; et comme j&#8217;aime bien écrire du beau code, j&#8217;ai donc cherché autre chose <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><strong>La solution</strong></p>
<p>La solution qui m&#8217;est venue à l&#8217;esprit consiste en une collection générique spécialisée <code>ChildItemCollection&lt;P,T&gt;</code>, et une interface <code>IChildItem&lt;P&gt;</code> que les enfants doivent implémenter.</p>
<p>L&#8217;interface <code>IChildItem&lt;P&gt;</code> définit simplement une propriété <code>Parent</code>, de type <code>P</code> :</p>
<p><pre class="brush: csharp;">
    /// &lt;summary&gt;
    /// Defines the contract for an object that has a parent object
    /// &lt;/summary&gt;
    /// &lt;typeparam name=&quot;P&quot;&gt;Type of the parent object&lt;/typeparam&gt;
    public interface IChildItem&lt;P&gt; where P : class
    {
        P Parent { get; set; }
    }
</pre></p>
<p>La collection <code>ChildItemCollection&lt;P,T&gt;</code> implémente <code>IList&lt;T&gt;</code> en délégant l&#8217;implémentation à une <code>List&lt;T&gt;</code> ou à la collection passée en paramètre du constructeur, et gère le maintien de la relation parent/enfant :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    /// &lt;summary&gt;
    /// Collection of child items. This collection automatically set the
    /// Parent property of the child items when they are added or removed
    /// &lt;/summary&gt;
    /// &lt;typeparam name=&quot;P&quot;&gt;Type of the parent object&lt;/typeparam&gt;
    /// &lt;typeparam name=&quot;T&quot;&gt;Type of the child items&lt;/typeparam&gt;
    public class ChildItemCollection&lt;P, T&gt; : IList&lt;T&gt;
        where P : class
        where T : IChildItem&lt;P&gt;
    {
        private P _parent;
        private IList&lt;T&gt; _collection;

        public ChildItemCollection(P parent)
        {
            this._parent = parent;
            this._collection = new List&lt;T&gt;();
        }

        public ChildItemCollection(P parent, IList&lt;T&gt; collection)
        {
            this._parent = parent;
            this._collection = collection;
        }

        #region IList&lt;T&gt; Members

        public int IndexOf(T item)
        {
            return _collection.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            if (item != null)
                item.Parent = _parent;
            _collection.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            T oldItem = _collection[index];
            _collection.RemoveAt(index);
            if (oldItem != null)
                oldItem.Parent = null;
        }

        public T this[int index]
        {
            get
            {
                return _collection[index];
            }
            set
            {
                T oldItem = _collection[index];
                if (value != null)
                    value.Parent = _parent;
                _collection[index] = value;
                if (oldItem != null)
                    oldItem.Parent = null;
            }
        }

        #endregion

        #region ICollection&lt;T&gt; Members

        public void Add(T item)
        {
            if (item != null)
                item.Parent = _parent;
            _collection.Add(item);
        }

        public void Clear()
        {
            foreach (T item in _collection)
            {
                if (item != null)
                    item.Parent = null;
            }
            _collection.Clear();
        }

        public bool Contains(T item)
        {
            return _collection.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            _collection.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return _collection.Count; }
        }

        public bool IsReadOnly
        {
            get { return _collection.IsReadOnly; }
        }

        public bool Remove(T item)
        {
            bool b = _collection.Remove(item);
            if (item != null)
                item.Parent = null;
            return b;
        }

        #endregion

        #region IEnumerable&lt;T&gt; Members

        public IEnumerator&lt;T&gt; GetEnumerator()
        {
            return _collection.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (_collection as System.Collections.IEnumerable).GetEnumerator();
        }

        #endregion
    }
</pre></p>
<p>Voyons donc comment utiliser cette solution dans notre exemple précédent&#8230; Nous allons d&#8217;abord modifier la classe <code>Child</code> pour qu&#8217;elle implémente l&#8217;interface <code>IChildItem&lt;Parent&gt;</code> :</p>
<p><pre class="brush: csharp;">
    public class Child : IChildItem&lt;Parent&gt;
    {
        public string Name { get; set; }

        [XmlIgnore]
        public Parent ParentObject { get; internal set; }

        #region IChildItem&lt;Parent&gt; Members

        Parent IChildItem&lt;Parent&gt;.Parent
        {
            get
            {
                return this.ParentObject;
            }
            set
            {
                this.ParentObject = value;
            }
        }

        #endregion
    }
</pre></p>
<p>Vous remarquerez qu&#8217;on implémente l&#8217;interface <code>IChildItem&lt;Parent&gt;</code> de façon <em>explicite</em> : l&#8217;intérêt est de &#8220;masquer&#8221; la propriété <code>Parent</code>, qui ne sera accessible que si on manipule l&#8217;objet <code>Child</code> via une variable de type <code>IChildItem&lt;Parent&gt;</code>. On définit aussi l&#8217;accesseur <code>set</code> de la propriété <code>ParentObject</code> comme étant <code>internal</code>, de façon à empêcher sa modification à partir d&#8217;un autre assembly.</p>
<p>Dans la classe <code>Parent</code>, il suffit de remplacer la <code>List&lt;Child&gt;</code> par une <code>ChildItemCollection&lt;Parent, Child&gt;</code>. On supprime au passage les méthodes <code>AddChild</code> et <code>RemoveChild</code>, qui ne sont plus nécessaires puisque la classe <code>ChildItemCollection&lt;P,T&gt;</code> se charge de renseigner la propriété <code>Parent</code>.</p>
<p><pre class="brush: csharp;">
    public class Parent
    {
        public Parent()
        {
            this.Children = new ChildItemCollection&lt;Parent, Child&gt;(this);
        }

        public string Name { get; set; }

        public ChildItemCollection&lt;Parent, Child&gt; Children { get; private set; }
    }
</pre></p>
<p>Notez qu&#8217;on passe au constructeur de <code>ChildItemCollection&lt;Parent, Child&gt;</code> une référence vers l&#8217;objet courant : c&#8217;est de cette façon que la collection sait quel doit être le parent de ses éléments.</p>
<p>Le code utilisé précédemment pour sérialiser un <code>Parent</code> fonctionne maintenant correctement. Lors de la désérialisation, la propriété <code>Child.ParentObject</code> n&#8217;est pas affectée quand le <code>Child</code> est désérialisé (puisqu&#8217;elle a l&#8217;attribut <code>XmlIgnore</code>), mais lors de l&#8217;ajout à la collection <code>Parent.Children</code>.</p>
<p>Cette solution permet donc de conserver la relation parent/enfant lors de la sérialisation XML, sans recourir à des bricolages peu élégants&#8230; Notez cependant une restriction : si la propriété <code>ParentObject</code> est modifiée autrement que par la classe <code>ChildItemCollection&lt;P,T&gt;</code>, la cohérence de la relation parent/enfant est rompue. Il est cependant assez simple d&#8217;ajouter à l&#8217;accesseur <code>set</code> la logique pour maintenir la cohérence, je ne l&#8217;ai pas fait dans cet exemple par souci de clarté et de simplicité.</p>
<br /> Tagged: C#, collection, parent/enfant, sérialisation XML <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/180/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/180/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/180/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/180/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/180/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/180/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/180/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/180/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/180/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/180/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/180/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/180/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/180/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/180/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=180&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/06/11/c-relation-parentenfant-et-serialisation-xml/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[Windows Forms] Glisser-déplacer automatique de contrôles (DragMove)</title>
		<link>http://tomlev.wordpress.com/2009/04/27/windows-forms-glisser-deplacer-automatique-controles/</link>
		<comments>http://tomlev.wordpress.com/2009/04/27/windows-forms-glisser-deplacer-automatique-controles/#comments</comments>
		<pubDate>Mon, 27 Apr 2009 22:46:32 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[Windows Forms]]></category>
		<category><![CDATA[DragMove]]></category>
		<category><![CDATA[IExtenderProvider]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=162</guid>
		<description><![CDATA[Je ressors de mes tiroirs un code que j&#8217;avais écrit il y a quelque temps, et dont je voudrais vous faire profiter&#8230; Il existe en WPF une méthode très pratique pour déplacer une fenêtre sans bordure : Window.DragMove. Elle s&#8217;utilise comme ceci : A partir de l&#8217;appel de cette méthode, la fenêtre est déplacée avec [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=162&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Je ressors de mes tiroirs un code que j&#8217;avais écrit il y a quelque temps, et dont je voudrais vous faire profiter&#8230;</p>
<p>Il existe en WPF une méthode très pratique pour déplacer une fenêtre sans bordure : <code>Window.DragMove</code>. Elle s&#8217;utilise comme ceci :</p>
<p><pre class="brush: csharp;">
        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.DragMove();
        }
</pre></p>
<p>A partir de l&#8217;appel de cette méthode, la fenêtre est déplacée avec la souris jusqu&#8217;à ce que le bouton soit relâché. Difficile de faire plus simple <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Malheureusement, cette méthode n&#8217;existe qu&#8217;en WPF, et une majorité de développeurs travaillent encore avec Windows Forms. Je vous propose donc un code qui permet d&#8217;utiliser en Windows Forms la technique décrite ci-dessus, en y apportant les améliorations suivantes :</p>
<ul>
<li>Utilisable sur n&#8217;importe quel contrôle, pas seulement sur une fenêtre</li>
<li>Pas d&#8217;obligation de gérer l&#8217;évènement <code>MouseDown</code></li>
<li>Intégration au designer par un <code>IExtenderProvider</code></li>
</ul>
<p>Ma solution se compose des 2 éléments suivants :</p>
<ul>
<li>une classe statique <code>DragMoveExtensions</code> qui fournit des méthodes d&#8217;extensions pour la classe <code>Control</code> (facilement transformables en simples méthodes statiques pour l&#8217;utilisation en C# 2)</li>
<li>un composant <code>DragMoveProvider</code>, qui implémente <code>IExtenderProvider</code> pour ajouter une propriété <code>EnableDragMove</code> aux contrôles de la Form</li>
</ul>
<p>Pour l&#8217;utiliser, il y en a pour tous les goûts <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<ul>
<li>La méthode la plus simple, sans écrire une seule ligne de code : en mode design, poser un <code>DragMoveProvider</code> sur la Form, et positionner à <code>true</code> la propriété <code>EnableDragMove</code> sur la Form ou le contrôle</li>
<div id="attachment_166" class="wp-caption aligncenter" style="width: 460px"><a href="http://tomlev.files.wordpress.com/2009/04/dragmoveprovider1.png"><img src="http://tomlev.files.wordpress.com/2009/04/dragmoveprovider1.png?w=780" alt="DragMoveProvider" title="DragMoveProvider"   class="size-full wp-image-166" /></a><p class="wp-caption-text">DragMoveProvider</p></div>
<li>La méthode la plus proche de WPF : dans le handler de l&#8217;évènement <code>MouseDown</code>, appeler la méthode d&#8217;extension <code>DragMove</code> sur la Form ou le contrôle à déplacer</li>
<p><pre class="brush: csharp;">
        private void label2_MouseDown(object sender, MouseEventArgs e)
        {
            label2.DragMove();
        }
</pre></p>
<li>La méthode la plus souple : appeler, au cas par cas, la méthode d&#8217;extension <code>EnableDragMove</code> sur la Form ou le contrôle à déplacer (aucun évènement à gérer).</li>
<p><pre class="brush: csharp;">
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            this.EnableDragMove(checkBox1.Checked);
        }
</pre></p>
</ul>
<p>La solution en pièce jointe contient la librairie réutilisable WinFormsDragMove, ainsi qu&#8217;un projet de test qui illustre les différentes manières d&#8217;utiliser cette librairie. Une version compatible C# 2 des ces projets est également inclue.</p>
<p><a href="http://tlevesque.developpez.com/files/DragMove.zip">Télécharger les sources</a></p>
<br /> Tagged: DragMove, IExtenderProvider, Windows Forms <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/162/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/162/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/162/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/162/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/162/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/162/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/162/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/162/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/162/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/162/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/162/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/162/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/162/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/162/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=162&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/04/27/windows-forms-glisser-deplacer-automatique-controles/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2009/04/dragmoveprovider1.png" medium="image">
			<media:title type="html">DragMoveProvider</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Binding sur une collection asynchrone</title>
		<link>http://tomlev.wordpress.com/2009/04/17/wpf-binding-sur-une-collection-asynchrone/</link>
		<comments>http://tomlev.wordpress.com/2009/04/17/wpf-binding-sur-une-collection-asynchrone/#comments</comments>
		<pubDate>Fri, 17 Apr 2009 00:00:19 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[asynchrone]]></category>
		<category><![CDATA[binding]]></category>
		<category><![CDATA[collection]]></category>
		<category><![CDATA[MVVM]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=143</guid>
		<description><![CDATA[Comme je l&#8217;avais évoqué dans mon précédent post, on ne peut pas ajouter des éléments à une ObservableCollection à partir d&#8217;un autre thread si une vue est bindée sur la collection : cela provoque une NotSupportedException. Prenons l&#8217;exemple d&#8217;une ListBox bindée sur une collection de chaines de caractères appartenant au ViewModel : Si on ajoute [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=143&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Comme je l&#8217;avais évoqué dans mon précédent post, on ne peut pas ajouter des éléments à une <code>ObservableCollection</code> à partir d&#8217;un autre thread si une vue est bindée sur la collection : cela provoque une <code>NotSupportedException</code>. Prenons l&#8217;exemple d&#8217;une <code>ListBox</code> bindée sur une collection de chaines de caractères appartenant au <em>ViewModel</em> :</p>
<p><pre class="brush: csharp;">
        private ObservableCollection&lt;string&gt; _strings = new ObservableCollection&lt;string&gt;();
        public ObservableCollection&lt;string&gt; Strings
        {
            get { return _strings; }
            set
            {
                _strings = value;
                OnPropertyChanged(&quot;Strings&quot;);
            }
        }
</pre></p>
<p><pre class="brush: xml;">
&lt;ListBox ItemsSource=&quot;{Binding Strings}&quot;/&gt;
</pre></p>
<p>Si on ajoute des éléments à cette collection hors du thread principal, on obtient l&#8217;exception citée plus haut. Une solution est de créer une nouvelle liste, puis de l&#8217;affecter à la propriété <code>Strings</code> quand elle est remplie, mais dans ce cas l&#8217;interface graphique ne reflète pas la progression : les éléments de la liste apparaissent tous à la fois quand la liste est remplie, et non au fur et à mesure que les éléments sont ajoutés. Si la liste correspond aux résultats d&#8217;une recherche, par exemple, ça peut être assez gênant car l&#8217;utilisateur s&#8217;attend à voir les résultats apparaître au fur et à mesure qu&#8217;ils sont trouvés (comme dans la recherche Windows).</p>
<p>Un moyen simple d&#8217;obtenir le comportement voulu est de créer une classe héritée de <code>ObservableCollection</code> qui déclenche les évènements <code>CollectionChanged</code> et <code>PropertyChanged</code> sur le thread principal au lieu du thread courant. La classe <code>AsyncOperation</code> se prête parfaitement à cet objectif : elle permet de &#8220;poster&#8221; un évènement sur le thread qui l&#8217;a créée. Elle est notamment utilisée par le composant <code>BackgroundWorker</code> et de nombreuses méthodes asynchrones du framework (<code>PictureBox.LoadAsync</code>, <code>WebClient.DownloadAsync</code>, etc&#8230;).</p>
<p>Voici donc le code d&#8217;une collection <code>AsyncObservableCollection</code> qui peut être modifiée à partir de n&#8217;importe quel thread tout en notifiant l&#8217;interface graphique lors d&#8217;une modification :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    public class AsyncObservableCollection&lt;T&gt; : ObservableCollection&lt;T&gt;
    {
        private AsyncOperation asyncOp = null;

        public AsyncObservableCollection()
        {
            CreateAsyncOp();
        }

        public AsyncObservableCollection(IEnumerable&lt;T&gt; list)
            : base(list)
        {
            CreateAsyncOp();
        }

        private void CreateAsyncOp()
        {
            // Create the AsyncOperation to post events on the creator thread
            asyncOp = AsyncOperationManager.CreateOperation(null);
        }

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            // Post the CollectionChanged event on the creator thread
            asyncOp.Post(RaiseCollectionChanged, e);
        }

        private void RaiseCollectionChanged(object param)
        {
            // We are in the creator thread, call the base implementation directly
           base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            // Post the PropertyChanged event on the creator thread
            asyncOp.Post(RaisePropertyChanged, e);
        }

        private void RaisePropertyChanged(object param)
        {
            // We are in the creator thread, call the base implementation directly
            base.OnPropertyChanged((PropertyChangedEventArgs)param);
        }
    }
</pre></p>
<p>La seule contrainte est de créer les instances de cette collection sur le thread de l&#8217;interface graphique, afin que les évènements soient bien déclenchés sur ce thread.</p>
<p>Si on reprend le code de l&#8217;exemple précédent, la seule chose à changer pour pouvoir modifier la collection à partir d&#8217;un autre thread est l&#8217;instantiation de la collection dans le <em>ViewModel</em> :</p>
<p><pre class="brush: csharp;">
private ObservableCollection&lt;string&gt; _strings = new AsyncObservableCollection&lt;string&gt;();
</pre></p>
<p>La <code>ListBox</code> peut maintenant refléter en temps réel les changements intervenus dans la collection.</p>
<p>Enjoy <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><strong>Mise à jour :</strong> Je viens de remarquer un bug dans mon implémentation : dans certains cas le fait de passer par un <code>Post</code> pour lever l&#8217;évènement alors que la collection est modifiée à partir du thread principal peut produire un comportement inattendu. Dans ce cas il faut évidemment lever l&#8217;évènement directement, en vérifiant que le <code>SynchronizationContext</code> courant est le même que celui dans lequel a été créée la collection. Et puisqu&#8217;on en est à se préoccuper du <code>SynchronizationContext</code>, autant l&#8217;utiliser directement et se passer de l&#8217;<code>AsyncOperation</code>, qui finalement n&#8217;apporte rien. Voici donc la nouvelle implémentation :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    public class AsyncObservableCollection&lt;T&gt; : ObservableCollection&lt;T&gt;
    {
        private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;

        public AsyncObservableCollection()
        {
        }

        public AsyncObservableCollection(IEnumerable&lt;T&gt; list)
            : base(list)
        {
        }

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (SynchronizationContext.Current == _synchronizationContext)
            {
                // Execute the CollectionChanged event on the current thread
                RaiseCollectionChanged(e);
            }
            else
            {
                // Post the CollectionChanged event on the creator thread
                _synchronizationContext.Post(RaiseCollectionChanged, e);
            }
        }

        private void RaiseCollectionChanged(object param)
        {
            // We are in the creator thread, call the base implementation directly
            base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (SynchronizationContext.Current == _synchronizationContext)
            {
                // Execute the PropertyChanged event on the current thread
                RaisePropertyChanged(e);
            }
            else
            {
                // Post the PropertyChanged event on the creator thread
                _synchronizationContext.Post(RaisePropertyChanged, e);
            }
        }

        private void RaisePropertyChanged(object param)
        {
            // We are in the creator thread, call the base implementation directly
            base.OnPropertyChanged((PropertyChangedEventArgs)param);
        }
    }
</pre></p>
<br /> Tagged: asynchrone, binding, collection, MVVM, WPF <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/143/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/143/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/143/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/143/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/143/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/143/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/143/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/143/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/143/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/143/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/143/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/143/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/143/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/143/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=143&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/04/17/wpf-binding-sur-une-collection-asynchrone/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Binding asynchrone sur une propriété du ViewModel</title>
		<link>http://tomlev.wordpress.com/2009/04/01/wpf-binding-asynchrone-sur-une-propriete-du-viewmodel/</link>
		<comments>http://tomlev.wordpress.com/2009/04/01/wpf-binding-asynchrone-sur-une-propriete-du-viewmodel/#comments</comments>
		<pubDate>Tue, 31 Mar 2009 22:58:48 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[asynchrone]]></category>
		<category><![CDATA[binding]]></category>
		<category><![CDATA[markup extension]]></category>
		<category><![CDATA[MVVM]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=129</guid>
		<description><![CDATA[Mise à jour : Comme l&#8217;a très justement indiqué Jérémy en commentaire, la propriété IsAsync du Binding permet de faire à peu près la même chose beaucoup plus simplement&#8230; Bien que ma méthode puisse servir pour certains besoins spécifiques, dans la plupart des cas la propriété IsAsync est probablement le meilleur choix ! Je laisse [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=129&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><em><strong>Mise à jour</strong> : Comme l&#8217;a très justement indiqué Jérémy en commentaire, la propriété IsAsync du Binding permet de faire à peu près la même chose beaucoup plus simplement&#8230; Bien que ma méthode puisse servir pour certains besoins spécifiques, dans la plupart des cas la propriété IsAsync est probablement le meilleur choix ! Je laisse le billet malgré tout, ne serait-ce que pour la classe SwitchBinding qui me semble assez utile&#8230;</em></p>
<p>J&#8217;ai eu récemment besoin, dans une application basée sur le pattern MVVM, d&#8217;afficher une propriété dont la valeur était assez longue à obtenir (récupérer par une requête HTTP). Au départ, j&#8217;ai simplement implémenté la propriété en suivant le principe du <em>lazy loading</em> : le binding sur cette propriété provoquait donc l&#8217;obtention de la valeur par une requête HTTP. Le résultat, prévisible, était un gel de l&#8217;interface pendant la récupération de la valeur. La solution classique pour ce genre de problème est de récupérer la valeur sur un autre thread, et d&#8217;affecter le résultat au contrôle qui doit l&#8217;afficher&#8230; sauf qu&#8217;en MVVM on n&#8217;a pas accès à ce contrôle. Une autre approche, plus adaptée, est d&#8217;affecter la valeur à la propriété du ViewModel, ce qui déclenche l&#8217;évènement <code>PropertyChanged</code> et rafraichit la vue.</p>
<p>J&#8217;ai essayé pas mal de choses avant d&#8217;arriver à une solution avec le moins possible de code de &#8220;plomberie&#8221;, je vais donc vous la faire partager. Voilà le code de la propriété :</p>
<p><pre class="brush: csharp;">
        private bool _retrievingValue = false;

        private object _value;
        public object Value
        {
            get
            {
                if (_value == null &amp;&amp; !_retrievingValue)
                {
                    _retrievingValue = true;
                    ThreadPool.QueueUserWorkItem(
                        (state) =&gt;
                        {
                            this.Value = _model.RetrieveValue(); // Very long operation...
                            _retrievingValue = false;
                        });
                }
                return _value;
            }
            set
            {
                _value = value;
                OnPropertyChanged(&quot;Value&quot;);
            }
        }
</pre></p>
<p>Ce code est assez simple à comprendre, mais mérite quand même quelques commentaires :</p>
<ul>
<li>Le premier binding sur cette propriété récupère d&#8217;abord une valeur nulle, mais déclenche aussi la récupération asynchrone de la valeur. Notez le flag <code>_retrievingValue</code> qui évite de lancer plusieurs fois la récupération</li>
<li>Quand la récupération de la valeur est terminée, la propriété est mise à jour, et l&#8217;évènement <code>PropertyChanged</code> met à jour le binding</li>
<li>Un détail intéressant est que la propriété est mise à jour directement dans le thread de travail. Puisque cette mise à jour provoque une modification de la vue, on aurait pu s&#8217;attendre à une <code>InvalidOperationException</code>, car on ne peut pas modifier la vue à partir d&#8217;un autre thread&#8230; mais en fait, le mécanisme de binding de WPF est lui-même asynchrone, ce qui masque la complexité de l&#8217;appel cross-thread. Il est donc inutile de recourir à un Dispatcher.Invoke ou autre pirouette, ce qui simplifie bien la vie des développeurs que nous sommes&#8230;</li>
<p><strong>Attention :</strong> ce système de binding asynchrone fonctionne bien pour affecter une valeur à une propriété du ViewModel, mais ne permet pas, par exemple, de modifier les éléments d&#8217;une ObservableCollection. Si vous essayez, à partir d&#8217;un autre thread, d&#8217;ajouter ou enlever des éléments à une collection sur laquelle la vue est bindée, cela provoquera une <code>NotSupportedException</code> :</p>
<blockquote><p>Ce type de CollectionView ne prend pas en charge les modifications de son SourceCollection à partir d’un thread différent du thread du Dispatcher.</p></blockquote>
<p>Pour modifier des collections de façon asynchrone, il faudra donc se débrouiller autrement&#8230; cela fera l&#8217;objet d&#8217;un prochain billet si je trouve une solution satisfaisante (peut-être à base d&#8217;<code>AsyncOperation</code>&#8230;). Si vous avez une idée là-dessus, n&#8217;hésitez pas à la poster en commentaire !</p>
<p>Fermons cette parenthèse et revenons à notre propriété Value. La méthode décrite plus haut fonctionne bien et nécessite assez peu de code, mais elle a un inconvénient : pendant la récupération de la valeur, l&#8217;utilisateur ne voit rien&#8230; On aimerait pouvoir afficher quelque chose qui indique que l&#8217;application travaille. Pour ça, on peut introduire une propriété <code>IsValueReady</code> qui indiquera si la valeur est prête. Côté XAML, on pourra utiliser un Trigger sur cette propriété pour modifier l&#8217;affichage.</p>
<p><pre class="brush: csharp;">
        private bool _retrievingValue = false;

        private object _value;
        public object Value
        {
            get
            {
                if (!_isValueReady &amp;&amp; !_retrievingValue)
                {
                    _retrievingValue = true;
                    ThreadPool.QueueUserWorkItem(
                        (state) =&gt;
                        {
                            this.Value = _model.RetrieveValue(); // Very long operation...
                            this.IsValueReady = true;
                            _retrievingValue = false;
                        });
                }
                return _value;
            }
            set
            {
                _value = value;
                OnPropertyChanged(&quot;Value&quot;);
            }
        }

        private bool _isValueReady = false;
        public bool IsValueReady
        {
            get { return _isValueReady; }
            private set
            {
                _isValueReady = value;
                OnPropertyChanged(&quot;IsValueReady&quot;);
            }
        }
</pre></p>
<p>Ça commence à faire un code un peu plus conséquent pour une simple propriété, mais ce code est toujours le même&#8230; les seules choses qui changent sont le nom de la propriété, son type, et le code qui récupère la valeur. Si on a beaucoup de propriétés de ce genre à créer, on pourrait donc facilement écrire un <em>code snippet</em> qui génèrerait le plus gros du code.</p>
<p>Avec Blend, il est probablement assez simple de créer un trigger pour prendre en compte la propriété IsValueReady dans la vue&#8230; mais je ne me suis toujours pas mis à Blend, je code directement la vue en XAML, et je trouve les Triggers beaucoup trop lourds à écrire&#8230; J&#8217;ai donc utilisé une autre solution que je trouve  beaucoup plus simple et plus lisible, à base de markup extension (oui, j&#8217;aime bien les markup extensions&#8230;). Ca donne le XAML suivant :</p>
<p><pre class="brush: xml;">
    &lt;Grid&gt;
        &lt;TextBlock Text=&quot;{Binding Value, FallbackValue=Blabla}&quot;
                   Visibility=&quot;{my:SwitchBinding IsValueReady, Visible, Hidden}&quot;/&gt;
        &lt;ProgressBar IsIndeterminate=&quot;True&quot; Width=&quot;150&quot; Height=&quot;30&quot;
                     Visibility=&quot;{my:SwitchBinding IsValueReady, Hidden, Visible}&quot;/&gt;
    &lt;/Grid&gt;
</pre></p>
<p>Ce code masque le <code>TextBlock</code> et affiche la <code>ProgressBar</code> tant que la valeur n&#8217;est pas prête. Quand la récupération de la valeur est terminée, la <code>ProgressBar</code> disparait et le <code>TextBlock</code> redevient visible&#8230;</p>
<p><code>SwitchBinding</code> est une markup extension qui hérite de <code>Binding</code> et renvoie une valeur ou une autre selon que la propriété bindée vaut <code>true</code> ou <code>false</code>. Je ne m&#8217;étendrai pas sur le fonctionnement de cette extension, car ce n&#8217;est pas le sujet de ce billet, mais voici tout de même son code :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
    public class SwitchBindingExtension : Binding
    {
        public SwitchBindingExtension()
        {
            Initialize();
        }

        public SwitchBindingExtension(string path)
            : base(path)
        {
            Initialize();
        }

        public SwitchBindingExtension(string path, object valueIfTrue, object valueIfFalse)
            : base(path)
        {
            Initialize();
            this.ValueIfTrue = valueIfTrue;
            this.ValueIfFalse = valueIfFalse;
        }

        private void Initialize()
        {
            this.ValueIfTrue = Binding.DoNothing;
            this.ValueIfFalse = Binding.DoNothing;
            this.Converter = new SwitchConverter(this);
        }

        [ConstructorArgument(&quot;valueIfTrue&quot;)]
        public object ValueIfTrue { get; set; }

        [ConstructorArgument(&quot;valueIfFalse&quot;)]
        public object ValueIfFalse { get; set; }

        private class SwitchConverter : IValueConverter
        {
            public SwitchConverter(SwitchBindingExtension switchExtension)
            {
                _switch = switchExtension;
            }

            private SwitchBindingExtension _switch;

            #region IValueConverter Members

            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                try
                {
                    bool b = System.Convert.ToBoolean(value);
                    return b ? _switch.ValueIfTrue : _switch.ValueIfFalse;
                }
                catch
                {
                    return DependencyProperty.UnsetValue;
                }
            }

            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return Binding.DoNothing;
            }

            #endregion
        }

    }
</pre></p>
<p>Cette markup extension est en fait l&#8217;équivalent XAML de l&#8217;opérateur conditionnel de C# (<code>condition ? valeurSiVrai : valeurSiFaux</code>), et peut être utilisée pour toutes sortes de valeurs.</p>
<p>Une autre option pour réaliser le comportement voulu aurait été de créer des propriétés qui renvoient une valeur de Visibility selon la valeur de IsValueReady, mais ça fait encore 2 propriétés de plus à créer, ce qui alourdit pas mal le ViewModel.</p>
<p>Voilà, c&#8217;est tout pour aujourd&#8217;hui&#8230; N&#8217;hésitez pas à me faire part de vos commentaires ou suggestions <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<br /> Tagged: asynchrone, binding, markup extension, MVVM, WPF <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/129/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=129&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/04/01/wpf-binding-asynchrone-sur-une-propriete-du-viewmodel/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Tri automatique d&#8217;un GridView lors du clic sur une colonne</title>
		<link>http://tomlev.wordpress.com/2009/03/27/wpf-tri-automatique-dun-gridview-lors-du-clic-sur-une-colonne/</link>
		<comments>http://tomlev.wordpress.com/2009/03/27/wpf-tri-automatique-dun-gridview-lors-du-clic-sur-une-colonne/#comments</comments>
		<pubDate>Fri, 27 Mar 2009 01:00:31 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[GridView]]></category>
		<category><![CDATA[MVVM]]></category>
		<category><![CDATA[propriété attachée]]></category>
		<category><![CDATA[tri]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=121</guid>
		<description><![CDATA[Il est assez simple, en WPF, de présenter des données sous forme de grille, grâce à la classe GridView. Pour le tri, en revanche, ça se complique&#8230; Avec le DataGridView de Windows Forms, c&#8217;était &#8220;automagique&#8221; : quand l&#8217;utilisateur cliquait sur un en-tête de colonne, le tri se faisait automatiquement sur cette colonne. En WPF, par [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=121&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Il est assez simple, en WPF, de présenter des données sous forme de grille, grâce à la classe <code>GridView</code>. Pour le tri, en revanche, ça se complique&#8230; Avec le <code>DataGridView</code> de Windows Forms, c&#8217;était &#8220;automagique&#8221; : quand l&#8217;utilisateur cliquait sur un en-tête de colonne, le tri se faisait automatiquement sur cette colonne. En WPF, par contre, il faut un peu mettre les mains dans le cambouis&#8230; La méthode préconisée par Microsoft pour trier un <code>GridView</code> lors du clic sur une colonne est décrite dans <a href="http://msdn.microsoft.com/fr-fr/library/ms745786.aspx">cet article</a> ; elle est basée sur l&#8217;évènement <code>Click</code> du <code>GridViewColumnHeader</code>. A mon sens, cette méthode présente deux gros inconvénients :</p>
<ul>
<li>Le tri doit être réalisé dans le code-behind, ce qu&#8217;on préfère souvent éviter si on s&#8217;appuie sur un design pattern comme MVVM. De plus, cela rend le code moins facilement réutilisable</li>
<li>Cette méthode suppose que le texte de l&#8217;en-tête de la colonne correspond au nom de la propriété sur laquelle on veut trier. Ce qui, bien sûr, est loin d&#8217;être toujours le cas&#8230; On pourrait se baser sur le <code>DisplayMemberBinding</code> de la colonne, mais il n&#8217;est pas forcément défini (par exemple si on définit un <code>CellTemplate</code> à la place).</li>
</ul>
<p>Après avoir longuement tâtonné pour trouver une approche souple et élégante, j&#8217;ai fini par réaliser une classe <code>GridViewSort</code> qui permet de trier automatiquement un <code>GridView</code> selon des propriétés attachées définies en XAML.</p>
<p>On utilise cette classe de la façon suivante :</p>
<p><pre class="brush: xml;">
                &lt;ListView ItemsSource=&quot;{Binding Persons}&quot;
                      IsSynchronizedWithCurrentItem=&quot;True&quot;
                      util:GridViewSort.AutoSort=&quot;True&quot;&gt;
                    &lt;ListView.View&gt;
                        &lt;GridView&gt;
                            &lt;GridView.Columns&gt;
                                &lt;GridViewColumn Header=&quot;Nom&quot;
                                                DisplayMemberBinding=&quot;{Binding Name}&quot;
                                                util:GridViewSort.PropertyName=&quot;Name&quot;/&gt;
                                &lt;GridViewColumn Header=&quot;Prénom&quot;
                                                DisplayMemberBinding=&quot;{Binding FirstName}&quot;
                                                util:GridViewSort.PropertyName=&quot;FirstName&quot;/&gt;
                                &lt;GridViewColumn Header=&quot;Date de naissance&quot;
                                                DisplayMemberBinding=&quot;{Binding DateOfBirth}&quot;
                                                util:GridViewSort.PropertyName=&quot;DateOfBirth&quot;/&gt;
                            &lt;/GridView.Columns&gt;
                        &lt;/GridView&gt;
                    &lt;/ListView.View&gt;
                &lt;/ListView&gt;
</pre></p>
<p>La propriété <code>GridViewSort.AutoSort</code> active le tri automatique pour la <code>ListView</code>. La propriété <code>GridViewSort.PropertyName</code>, définie sur chaque colonne, indique sur quelle propriété le tri doit être effectué. Il n&#8217;y a aucun code supplémentaire à écrire. Le clic sur un en-tête de colonne déclenche le tri sur cette colonne ; si la <code>ListView</code> est déjà triée sur cette colonne, l&#8217;ordre de tri est inversé.</p>
<p>Pour le cas où on souhaiterait gérer manuellement le tri, j&#8217;ai aussi créé une propriété attachée <code>GridViewSort.Command</code>. Par exemple, dans le cadre de l&#8217;utilisation du pattern MVVM, on peut binder cette propriété sur une commande déclarée dans le ViewModel :</p>
<p><pre class="brush: xml;">
                &lt;ListView ItemsSource=&quot;{Binding Persons}&quot;
                      IsSynchronizedWithCurrentItem=&quot;True&quot;
                      util:GridViewSort.Command=&quot;{Binding SortCommand}&quot;&gt;
                ...
</pre></p>
<p>La commande de tri reçoit en paramètre le nom de la propriété sur laquelle on veut trier.</p>
<p>Note : si les propriétés <code>Command</code> et <code>AutoSort</code> sont définies toutes les deux, c&#8217;est <code>Command</code> qui est prioritaire ; <code>AutoSort</code> est ignorée.</p>
<p>Voici le code complet de la classe <code>GridViewSort</code> :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace Wpf.Util
{
    public class GridViewSort
    {
        #region Attached properties

        public static ICommand GetCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(CommandProperty, value);
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached(
                &quot;Command&quot;,
                typeof(ICommand),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    null,
                    (o, e) =&gt;
                    {
                        ItemsControl listView = o as ItemsControl;
                        if (listView != null)
                        {
                            if (!GetAutoSort(listView)) // Don't change click handler if AutoSort enabled
                            {
                                if (e.OldValue != null &amp;&amp; e.NewValue == null)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (e.OldValue == null &amp;&amp; e.NewValue != null)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static bool GetAutoSort(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoSortProperty);
        }

        public static void SetAutoSort(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoSortProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoSort.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoSortProperty =
            DependencyProperty.RegisterAttached(
                &quot;AutoSort&quot;,
                typeof(bool),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    false,
                    (o, e) =&gt;
                    {
                        ListView listView = o as ListView;
                        if (listView != null)
                        {
                            if (GetCommand(listView) == null) // Don't change click handler if a command is set
                            {
                                bool oldValue = (bool)e.OldValue;
                                bool newValue = (bool)e.NewValue;
                                if (oldValue &amp;&amp; !newValue)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (!oldValue &amp;&amp; newValue)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static string GetPropertyName(DependencyObject obj)
        {
            return (string)obj.GetValue(PropertyNameProperty);
        }

        public static void SetPropertyName(DependencyObject obj, string value)
        {
            obj.SetValue(PropertyNameProperty, value);
        }

        // Using a DependencyProperty as the backing store for PropertyName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.RegisterAttached(
                &quot;PropertyName&quot;,
                typeof(string),
                typeof(GridViewSort),
                new UIPropertyMetadata(null)
            );

        #endregion

        #region Column header click event handler

        private static void ColumnHeader_Click(object sender, RoutedEventArgs e)
        {
            GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
            if (headerClicked != null)
            {
                string propertyName = GetPropertyName(headerClicked.Column);
                if (!string.IsNullOrEmpty(propertyName))
                {
                    ListView listView = GetAncestor&lt;ListView&gt;(headerClicked);
                    if (listView != null)
                    {
                        ICommand command = GetCommand(listView);
                        if (command != null)
                        {
                            if (command.CanExecute(propertyName))
                            {
                                command.Execute(propertyName);
                            }
                        }
                        else if (GetAutoSort(listView))
                        {
                            ApplySort(listView.Items, propertyName);
                        }
                    }
                }
            }
        }

        #endregion

        #region Helper methods

        public static T GetAncestor&lt;T&gt;(DependencyObject reference) where T : DependencyObject
        {
            DependencyObject parent = VisualTreeHelper.GetParent(reference);
            while (!(parent is T))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }
            if (parent != null)
                return (T)parent;
            else
                return null;
        }

        public static void ApplySort(ICollectionView view, string propertyName)
        {
            ListSortDirection direction = ListSortDirection.Ascending;
            if (view.SortDescriptions.Count &gt; 0)
            {
                SortDescription currentSort = view.SortDescriptions[0];
                if (currentSort.PropertyName == propertyName)
                {
                    if (currentSort.Direction == ListSortDirection.Ascending)
                        direction = ListSortDirection.Descending;
                    else
                        direction = ListSortDirection.Ascending;
                }
                view.SortDescriptions.Clear();
            }
            if (!string.IsNullOrEmpty(propertyName))
            {
                view.SortDescriptions.Add(new SortDescription(propertyName, direction));
            }
        }

        #endregion
    }
}
</pre></p>
<p>On pourrait bien sûr envisager certaines améliorations, notamment visuelles, comme l&#8217;ajout d&#8217;une flèche sur la colonne triée (à l&#8217;aide d&#8217;un <code>Adorner</code> par exemple). Mais en attendant, cette classe couvre tout l&#8217;aspect fonctionnel du tri, donc n&#8217;hésitez pas à l&#8217;utiliser !</p>
<p><strong>Mise à jour :</strong> l&#8217;affichage du symbole de tri est maintenant géré par la classe <code>GridViewSort</code>, la nouvelle version est disponible dans <a href="http://tomlev.wordpress.com/2009/08/04/wpf-tri-automatique-d%E2%80%99un-gridview-suite/">ce billet</a>.</p>
<br /> Tagged: GridView, MVVM, propriété attachée, tri, WPF <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/121/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/121/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/121/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/121/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/121/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/121/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/121/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/121/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/121/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/121/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/121/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/121/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/121/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/121/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=121&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/03/27/wpf-tri-automatique-dun-gridview-lors-du-clic-sur-une-colonne/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Utiliser les InputBindings avec le pattern MVVM</title>
		<link>http://tomlev.wordpress.com/2009/03/17/wpf-utiliser-les-inputbindings-avec-le-pattern-mvvm/</link>
		<comments>http://tomlev.wordpress.com/2009/03/17/wpf-utiliser-les-inputbindings-avec-le-pattern-mvvm/#comments</comments>
		<pubDate>Tue, 17 Mar 2009 10:27:58 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[InputBinding]]></category>
		<category><![CDATA[KeyBinding]]></category>
		<category><![CDATA[markup extension]]></category>
		<category><![CDATA[MVVM]]></category>
		<category><![CDATA[XAML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=107</guid>
		<description><![CDATA[Si vous développez des applications WPF en suivant le design pattern Model-View-ViewModel, vous vous êtes peut-être déjà trouvé confronté au problème suivant : comment, en XAML, lier un raccourci clavier ou une action de la souris à une commande du ViewModel ? Idéalement, on aimerait pouvoir faire comme ça : Malheureusement, ce code ne fonctionne [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=107&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Si vous développez des applications WPF en suivant le design pattern Model-View-ViewModel, vous vous êtes peut-être déjà trouvé confronté au problème suivant : comment, en XAML, lier un raccourci clavier ou une action de la souris à une commande du ViewModel ? Idéalement, on aimerait pouvoir faire comme ça :</p>
<p><pre class="brush: xml;">
    &lt;UserControl.InputBindings&gt;
        &lt;KeyBinding Modifiers=&quot;Control&quot; Key=&quot;E&quot; Command=&quot;{Binding EditCommand}&quot;/&gt;
    &lt;/UserControl.InputBindings&gt;
</pre></p>
<p>Malheureusement, ce code ne fonctionne pas, pour deux raisons :</p>
<ol>
<li>La propriété Command n&#8217;est pas une DependencyProperty, on ne peut donc pas faire de binding dessus</li>
<li>Les InputBindings ne font pas partie de l&#8217;arbre logique ou visuel du contrôle, ils n&#8217;héritent donc pas du DataContext</li>
</ol>
<p>Une solution, bien sûr, serait de passer par le code-behind pour créer les InputBindings, mais en général, dans une application développée selon le pattern MVVM, on préfère éviter d&#8217;écrire du code-behind. J&#8217;ai longuement cherché des solutions alternatives pour pouvoir le faire en XAML, mais la plupart sont relativement complexes et peu intuitives. J&#8217;ai donc finalement créé une markup extension qui permet de se binder à une commande du DataContext, à n&#8217;importe quel endroit du code XAML, même si l&#8217;élément n&#8217;hérite pas du DataContext.</p>
<p>Cette extension s&#8217;utilise comme un simple binding :</p>
<p><pre class="brush: xml;">
    &lt;UserControl.InputBindings&gt;
        &lt;KeyBinding Modifiers=&quot;Control&quot; Key=&quot;E&quot; Command=&quot;{input:CommandBinding EditCommand}&quot;/&gt;
    &lt;/UserControl.InputBindings&gt;
</pre></p>
<p>(Le namespace XML <em>input</em> étant mappé sur le namespace CLR où est déclarée la markup extension)</p>
<p>Pour réaliser cette extension, j&#8217;avoue que j&#8217;ai un peu triché&#8230; j&#8217;ai fouillé avec Reflector le code des classes de WPF, afin de trouver des champs privés qui permettraient de récupérer le DataContext de l&#8217;élément racine. J&#8217;accède ensuite à ces champs par réflexion.</p>
<p>Voici le code :</p>
<p><pre class="brush: csharp; collapse: true; light: false; toolbar: true;">
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;

namespace MVVMLib.Input
{
    [MarkupExtensionReturnType(typeof(ICommand))]
    public class CommandBindingExtension : MarkupExtension
    {
        public CommandBindingExtension()
        {
        }

        public CommandBindingExtension(string commandName)
        {
            this.CommandName = commandName;
        }

        [ConstructorArgument(&quot;commandName&quot;)]
        public string CommandName { get; set; }

        private object targetObject;
        private object targetProperty;

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
            if (provideValueTarget != null)
            {
                targetObject = provideValueTarget.TargetObject;
                targetProperty = provideValueTarget.TargetProperty;
            }

            if (!string.IsNullOrEmpty(CommandName))
            {
                // The serviceProvider is actually a ProvideValueServiceProvider, which has a private field &quot;_context&quot; of type ParserContext
                ParserContext parserContext = GetPrivateFieldValue&lt;ParserContext&gt;(serviceProvider, &quot;_context&quot;);
                if (parserContext != null)
                {
                    // A ParserContext has a private field &quot;_rootElement&quot;, which returns the root element of the XAML file
                    FrameworkElement rootElement = GetPrivateFieldValue&lt;FrameworkElement&gt;(parserContext, &quot;_rootElement&quot;);
                    if (rootElement != null)
                    {
                        // Now we can retrieve the DataContext
                        object dataContext = rootElement.DataContext;

                        // The DataContext may not be set yet when the FrameworkElement is first created, and it may change afterwards,
                        // so we handle the DataContextChanged event to update the Command when needed
                        if (!dataContextChangeHandlerSet)
                        {
                            rootElement.DataContextChanged += new DependencyPropertyChangedEventHandler(rootElement_DataContextChanged);
                            dataContextChangeHandlerSet = true;
                        }

                        if (dataContext != null)
                        {
                            ICommand command = GetCommand(dataContext, CommandName);
                            if (command != null)
                                return command;
                        }
                    }
                }
            }

            // The Command property of an InputBinding cannot be null, so we return a dummy extension instead
            return DummyCommand.Instance;
        }

        private ICommand GetCommand(object dataContext, string commandName)
        {
            PropertyInfo prop = dataContext.GetType().GetProperty(commandName);
            if (prop != null)
            {
                ICommand command = prop.GetValue(dataContext, null) as ICommand;
                if (command != null)
                    return command;
            }
            return null;
        }

        private void AssignCommand(ICommand command)
        {
            if (targetObject != null &amp;&amp; targetProperty != null)
            {
                if (targetProperty is DependencyProperty)
                {
                    DependencyObject depObj = targetObject as DependencyObject;
                    DependencyProperty depProp = targetProperty as DependencyProperty;
                    depObj.SetValue(depProp, command);
                }
                else
                {
                    PropertyInfo prop = targetProperty as PropertyInfo;
                    prop.SetValue(targetObject, command, null);
                }
            }
        }

        private bool dataContextChangeHandlerSet = false;
        private void rootElement_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement rootElement = sender as FrameworkElement;
            if (rootElement != null)
            {
                object dataContext = rootElement.DataContext;
                if (dataContext != null)
                {
                    ICommand command = GetCommand(dataContext, CommandName);
                    if (command != null)
                    {
                        AssignCommand(command);
                    }
                }
            }
        }

        private T GetPrivateFieldValue&lt;T&gt;(object target, string fieldName)
        {
            FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
            if (field != null)
            {
                return (T)field.GetValue(target);
            }
            return default(T);
        }

        // A dummy command that does nothing...
        private class DummyCommand : ICommand
        {

            #region Singleton pattern

            private DummyCommand()
            {
            }

            private static DummyCommand _instance = null;
            public static DummyCommand Instance
            {
                get
                {
                    if (_instance == null)
                    {
                        _instance = new DummyCommand();
                    }
                    return _instance;
                }
            }

            #endregion

            #region ICommand Members

            public bool CanExecute(object parameter)
            {
                return false;
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
            }

            #endregion
        }
    }
</pre></p>
<p>Cette solution a cependant une limitation : elle ne fonctionne que pour le DataContext de la racine du XAML. On ne peut donc pas l&#8217;utiliser, par exemple, pour définir des InputBindings sur un contrôle dont on redéfinit aussi le DataContext, car la markup extension renverra le DataContext de l&#8217;élément racine.</p>
<br /> Tagged: InputBinding, KeyBinding, markup extension, MVVM, WPF, XAML <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/107/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=107&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/03/17/wpf-utiliser-les-inputbindings-avec-le-pattern-mvvm/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[Visual Studio] Astuce : définir un élément du projet comme sous-élément d&#8217;un autre</title>
		<link>http://tomlev.wordpress.com/2009/03/05/visual-studio-astuce-definir-un-element-du-projet-comme-sous-element-dun-autre/</link>
		<comments>http://tomlev.wordpress.com/2009/03/05/visual-studio-astuce-definir-un-element-du-projet-comme-sous-element-dun-autre/#comments</comments>
		<pubDate>Thu, 05 Mar 2009 00:32:55 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Astuces]]></category>
		<category><![CDATA[astuce]]></category>
		<category><![CDATA[Visual Studio]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=92</guid>
		<description><![CDATA[Vous avez certainement remarqué que, dans un projet C#, certains éléments sont placés sous un élément parent : c&#8217;est le cas, par exemple, pour les fichiers générés par un designer ou assistant : L&#8217;astuce suivante permet d&#8217;obtenir le même comportement pour vos propres fichiers. Supposons que vous souhaitiez personnaliser les classes générées par le designer [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=92&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Vous avez certainement remarqué que, dans un projet C#, certains éléments sont placés sous un élément parent : c&#8217;est le cas, par exemple, pour les fichiers générés par un designer ou assistant :<br />
<div id="attachment_94" class="wp-caption aligncenter" style="width: 273px"><img src="http://tomlev.files.wordpress.com/2009/03/project1.png?w=780" alt="Explorateur de solution" title="Explorateur de solution"   class="size-full wp-image-94" /><p class="wp-caption-text">Le fichier Model1.Designer.cs est placé sous le fichier Model1.edmx</p></div></p>
<p>L&#8217;astuce suivante permet d&#8217;obtenir le même comportement pour vos propres fichiers.</p>
<p>Supposons que vous souhaitiez personnaliser les classes générées par le designer d&#8217;entités. Vous ne pouvez pas modifier le fichier <em>Model1.Designer.cs</em>, puisque vos modifications seraient écrasées par le designer. Vous allez donc créer un nouveau fichier, par exemple <em>Model1.Custom.cs</em>, où vous allez mettre votre code pour les classes d&#8217;entité (à l&#8217;aide du mot clé <em>partial</em>). Par défaut, ce fichier est placé directement à la racine du projet :<br />
<div id="attachment_98" class="wp-caption aligncenter" style="width: 274px"><img src="http://tomlev.files.wordpress.com/2009/03/project23.png?w=780" alt="Explorateur de solution" title="Explorateur de solution"   class="size-full wp-image-98" /><p class="wp-caption-text">Le fichier Model1.Custom.cs est placé à la racine du projet</p></div></p>
<p>Pour bien mettre en évidence le lien avec <em>Model1.edmx</em>, on préfèrerait le voir &#8220;sous&#8221; <em>Model1.edmx</em>&#8230; Bien que l&#8217;interface de Visual Studio ne propose pas cette option, c&#8217;est possible : il faut pour celà modifier le fichier <em>.csproj</em> à la main. Le plus simple, pour celà, est de décharger le projet (clic droit sur le projet, &#8220;<em>décharger le projet</em>&#8220;) et de l&#8217;éditer directement dans Visual Studio (clic droit sur le projet déchargé, &#8220;<em>modifier FooBar.csproj</em>&#8220;). Cherchez l&#8217;élément <em></em> correspondant au fichier <em>Model1.Custom.cs</em>, et ajoutez un sous-élément <em></em> comme indiqué ci-dessous :</p>
<p><pre class="brush: xml;">
    &lt;Compile Include=&quot;Model1.Custom.cs&quot;&gt;
        &lt;DependentUpon&gt;Model1.edmx&lt;/DependentUpon&gt;
    &lt;/Compile&gt;
</pre></p>
<p>Rechargez le projet : <em>Model1.Custom.cs</em> apparait maintenant sous <em>Model1.edmx</em>.<br />
<div id="attachment_101" class="wp-caption aligncenter" style="width: 275px"><img src="http://tomlev.files.wordpress.com/2009/03/project3.png?w=780" alt="Explorateur de solution" title="Explorateur de solution"   class="size-full wp-image-101" /><p class="wp-caption-text">Model1.Custom.cs apparait sous Model1.edmx</p></div></p>
<p>Cette astuce permet de mieux structurer son projet pour s&#8217;y retrouver plus facilement.</p>
<br /> Tagged: astuce, Visual Studio <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/92/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=92&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/03/05/visual-studio-astuce-definir-un-element-du-projet-comme-sous-element-dun-autre/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2009/03/project1.png" medium="image">
			<media:title type="html">Explorateur de solution</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2009/03/project23.png" medium="image">
			<media:title type="html">Explorateur de solution</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2009/03/project3.png" medium="image">
			<media:title type="html">Explorateur de solution</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Article de Josh Smith sur le design pattern Model-View-ViewModel</title>
		<link>http://tomlev.wordpress.com/2009/02/25/wpf-article-de-josh-smith-sur-le-design-pattern-model-view-viewmodel/</link>
		<comments>http://tomlev.wordpress.com/2009/02/25/wpf-article-de-josh-smith-sur-le-design-pattern-model-view-viewmodel/#comments</comments>
		<pubDate>Wed, 25 Feb 2009 00:30:56 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[WPF]]></category>
		<category><![CDATA[article]]></category>
		<category><![CDATA[design pattern]]></category>
		<category><![CDATA[Josh Smith]]></category>
		<category><![CDATA[MVVM]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=79</guid>
		<description><![CDATA[Depuis l&#8217;apparition de WPF, on entend de plus en plus souvent parler de &#8220;Model-View-ViewModel&#8221; (MVVM). Il s&#8217;agit d&#8217;un design pattern, inspiré entre autres de Model-View-Controller (MVC) et Presentation Model (PM), conçu spécifiquement pour tirer le meilleur parti des fonctionnalités de WPF. Ce pattern permet notamment un excellent découplage entre les données, le comportement, et la [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=79&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Depuis l&#8217;apparition de WPF, on entend de plus en plus souvent parler de &#8220;Model-View-ViewModel&#8221; (MVVM). Il s&#8217;agit d&#8217;un design pattern, inspiré entre autres de Model-View-Controller (MVC) et Presentation Model (PM), conçu spécifiquement pour tirer le meilleur parti des fonctionnalités de WPF. Ce pattern permet notamment un excellent découplage entre les données, le comportement, et la présentation des données, ce qui rend le code plus facile à comprendre et à maintenir, et facilite la collaboration entre un développeur et un designer. Un autre avantage de MVVM est qu&#8217;il est permet d&#8217;écrire des programmes facilement testables.</p>
<p>Pour tout savoir sur le pattern MVVM, je vous invite à lire l&#8217;excellent article de Josh Smith à ce sujet, paru dans l&#8217;édition de février du MSDN Magazine : <a href="http://msdn.microsoft.com/en-us/magazine/dd419663.aspx">WPF Apps With The Model-View-ViewModel Design Pattern</a> (en anglais).</p>
<p>A travers un exemple simple mais concret, Josh Smith aborde pratiquement tous les aspects du pattern MVVM, notamment :</p>
<ul>
<li>Data binding</li>
<li>Commandes</li>
<li>Validation</li>
<li>Tests unitaires</li>
<li>&#8230;</li>
</ul>
<p>Le code source fourni constitue de plus une bonne base de départ pour une application développée selon le pattern MVVM, ainsi qu&#8217;une mine d&#8217;exemples concrets.</p>
<br /> Tagged: article, design pattern, Josh Smith, MVVM, WPF <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/79/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/79/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/79/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/79/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/79/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/79/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/79/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/79/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/79/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/79/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/79/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/79/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/79/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/79/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=79&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/02/25/wpf-article-de-josh-smith-sur-le-design-pattern-model-view-viewmodel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>Créer un lecteur RSS en 5 minutes</title>
		<link>http://tomlev.wordpress.com/2009/02/13/creer-un-lecteur-rss-en-5-minutes/</link>
		<comments>http://tomlev.wordpress.com/2009/02/13/creer-un-lecteur-rss-en-5-minutes/#comments</comments>
		<pubDate>Thu, 12 Feb 2009 23:05:13 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[RSS]]></category>
		<category><![CDATA[syndication]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=58</guid>
		<description><![CDATA[Aujourd&#8217;hui, je suis tombé par hasard sur une petite classe bien pratique : SyndicationFeed. Cette classe, apparue dans le framework 3.5, permet de manipuler des flux de syndication (comme RSS 2.0 ou Atom 1.0) avec un minimum de code. On peut l&#8217;utiliser pour créer et diffuser des flux, ou pour lire des flux existants. Par [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=58&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Aujourd&#8217;hui, je suis tombé par hasard sur une petite classe bien pratique : <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.syndication.syndicationfeed.aspx">SyndicationFeed</a>. Cette classe, apparue dans le framework 3.5, permet de manipuler des flux de syndication (comme RSS 2.0 ou Atom 1.0) avec un minimum de code. On peut l&#8217;utiliser pour créer et diffuser des flux, ou pour lire des flux existants.</p>
<p>Par exemple, voilà comment récupérer le fil d&#8217;actualité de Google News et afficher son titre, son lien d&#8217;origine et les titres de ses articles :</p>
<p><pre class="brush: csharp;">
string url = &quot;http://news.google.fr/nwshp?hl=fr&amp;tab=wn&amp;output=rss&quot;;
using (XmlReader reader = XmlReader.Create(url))
{
    SyndicationFeed feed = SyndicationFeed.Load(reader);
    Console.WriteLine(feed.Title.Text);
    Console.WriteLine(feed.Links[0].Uri);
    foreach(SyndicationItem item in feed.Items)
    {
        Console.WriteLine(item.Title.Text);
    }
}
</pre></p>
<p>Simple, non ? <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Tirons maintenant parti des facilités de binding de WPF pour créer un petit lecteur RSS graphique :</p>
<p><pre class="brush: xml;">
&lt;Window x:Class=&quot;TestFeeds.Window1&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title=&quot;Minimalist feed reader&quot; Height=&quot;286&quot; Width=&quot;531&quot;&gt;
    &lt;Grid&gt;
        &lt;Grid.RowDefinitions&gt;
            &lt;RowDefinition Height=&quot;Auto&quot;/&gt;
            &lt;RowDefinition Height=&quot;*&quot;/&gt;
        &lt;/Grid.RowDefinitions&gt;
        &lt;DockPanel Grid.Row=&quot;0&quot;&gt;
            &lt;Button Name=&quot;btnGo&quot;
                    DockPanel.Dock=&quot;Right&quot;
                    Width=&quot;50&quot;
                    Content=&quot;Go&quot;
                    Click=&quot;btnGo_Click&quot; /&gt;
            &lt;TextBox Name=&quot;txtUrl&quot; /&gt;
        &lt;/DockPanel&gt;
        &lt;Grid Grid.Row=&quot;1&quot;&gt;
            &lt;Grid.ColumnDefinitions&gt;
                &lt;ColumnDefinition Width=&quot;250&quot;/&gt;
                &lt;ColumnDefinition Width=&quot;Auto&quot;/&gt;
                &lt;ColumnDefinition Width=&quot;*&quot;/&gt;
            &lt;/Grid.ColumnDefinitions&gt;
            &lt;ListBox Name=&quot;lstFeedItems&quot;
                     Grid.Column=&quot;0&quot;
                     DisplayMemberPath=&quot;Title.Text&quot; /&gt;
            &lt;GridSplitter Grid.Column=&quot;1&quot;
                          VerticalAlignment=&quot;Stretch&quot;
                          Width=&quot;3&quot;
                          ResizeBehavior=&quot;PreviousAndNext&quot;
                          ResizeDirection=&quot;Columns&quot;/&gt;
            &lt;Frame Name=&quot;frmContents&quot;
                   Source=&quot;{Binding SelectedItem.Links[0].Uri, ElementName=lstFeedItems}&quot;
                   Grid.Column=&quot;2&quot;
                   NavigationUIVisibility=&quot;Visible&quot;&gt;
            &lt;/Frame&gt;
        &lt;/Grid&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;
</pre></p>
<p>Le code-behind :</p>
<p><pre class="brush: csharp;">
    private void btnGo_Click(object sender, RoutedEventArgs e)
    {
        using (XmlReader reader = XmlReader.Create(txtUrl.Text))
        {
            SyndicationFeed feed = SyndicationFeed.Load(reader);
            lstFeedItems.ItemsSource = feed.Items;
        }
    }
</pre></p>
<p>Et voilà le résultat !</p>
<p><a href="http://tomlev.files.wordpress.com/2009/02/feed_reader.png"><img src="http://tomlev.files.wordpress.com/2009/02/feed_reader.png?w=780" border="0" alt="Capture d'écran" title="Capture d'écran"   class="aligncenter size-full wp-image-64" /></a></p>
<br /> Tagged: RSS, syndication, WPF <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/58/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=58&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/02/13/creer-un-lecteur-rss-en-5-minutes/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>

		<media:content url="http://tomlev.files.wordpress.com/2009/02/feed_reader.png" medium="image">
			<media:title type="html">Capture d&#039;écran</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Coller une image du presse-papier (bug dans Clipboard.GetImage)</title>
		<link>http://tomlev.wordpress.com/2009/02/05/wpf-coller-une-image-du-presse-papier/</link>
		<comments>http://tomlev.wordpress.com/2009/02/05/wpf-coller-une-image-du-presse-papier/#comments</comments>
		<pubDate>Thu, 05 Feb 2009 00:26:28 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[clipboard]]></category>
		<category><![CDATA[image]]></category>
		<category><![CDATA[presse-papier]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=21</guid>
		<description><![CDATA[Hum&#8230; 2 mois depuis mon précédent (et premier) post&#8230; il faudra que j&#8217;essaie d&#8217;être un peu plus régulier à l&#8217;avenir Si vous avez déjà essayé d&#8217;utiliser la méthode Clipboard.GetImage avec WPF, vous avez dû avoir une mauvaise surprise&#8230; En effet, cette méthode renvoie un InteropBitmap qui, dans certains cas (voire tout le temps), refuse de [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=21&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Hum&#8230; 2 mois depuis mon précédent (et premier) post&#8230; il faudra que j&#8217;essaie d&#8217;être un peu plus régulier à l&#8217;avenir <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>Si vous avez déjà essayé d&#8217;utiliser la méthode <em>Clipboard.GetImage</em> avec WPF, vous avez dû avoir une mauvaise surprise&#8230; En effet, cette méthode renvoie un <em>InteropBitmap</em> qui, dans certains cas (voire tout le temps), refuse de s&#8217;afficher dans un contrôle <em>Image</em> : aucune exception n&#8217;est levée, la taille de l&#8217;image est correcte, mais&#8230; l&#8217;affichage reste désespérément vide, ou alors l&#8217;image est méconnaissable.</p>
<p>Pourtant, si on enregistre l&#8217;image dans un stream et qu&#8217;on la relit à partir du stream, on obtient une image parfaitement utilisable. Mais bon, je trouve  cette méthode assez lourde (décodage &#8211; réencodage &#8211; redécodage de l&#8217;image). J&#8217;ai donc cherché une solution pour récupérer « manuellement » l&#8217;image dans le presse-papier.</p>
<p>Si on regarde les formats d&#8217;image disponibles dans le presse-papier (<em>Clipboard.GetDataObject().GetFormats()</em>), on voit que ça varie selon l&#8217;origine de l&#8217;image (capture d&#8217;écran, copie dans Paint…). Le seul format qui semble être toujours présent est <em>DeviceIndependentBitmap</em> (DIB). J&#8217;ai donc cherché, à récupérer le <em>MemoryStream</em> pour ce format, et à le décoder en <em>BitmapSource</em> :</p>
<p><pre class="brush: csharp;">
        private ImageSource ImageFromClipboardDib()
        {
            MemoryStream ms = Clipboard.GetData(&quot;DeviceIndependentBitmap&quot;) as MemoryStream;
            BitmapImage bmp = new BitmapImage();
            bmp.BeginInit();
            bmp.StreamSource = ms;
            bmp.EndInit();
            return bmp;
        }
</pre></p>
<p>Malheureusement, ce code renvoie une magnifique <em>NotSupportedException</em> : « Impossible de trouver un composant d&#8217;image adapté pour terminer l&#8217;opération.». En clair, il ne sait pas comment décoder ça… c&#8217;est pourtant un format assez simple a priori, et très répandu. J&#8217;ai donc un peu creusé la question et étudié la structure d&#8217;un DIB. En simplifiant un peu, un fichier bitmap « classique » (.bmp) est composé des sections suivantes :</p>
<ul>
<li>En-tête de fichier (structure <em>BITMAPFILEHEADER</em>)</li>
<li>En-tête de bitmap (structure <em>BITMAPINFO</em>)</li>
<li>Palette (tableau de RGBQUAD)</li>
<li>Données de l&#8217;image</li>
</ul>
<p>En observant le contenu du DIB dans le presse-papier, on voit qu&#8217;il présente la même structure, mais sans le <em>BITMAPFILEHEADER</em>… l&#8217;astuce est donc d&#8217;ajouter cet en-tête au début du buffer, et de passer le tout à <em>BitmapImage</em> ou <em>BitmapFrame</em>. Pas bien difficile a priori… là où ça se corse, c&#8217;est qu&#8217;il faut renseigner dans cet en-tête certaines valeurs qu&#8217;on ne peut obtenir qu&#8217;en lisant le contenu de l&#8217;image. Il faut notamment indiquer à quelle position débutent les données de l&#8217;image proprement dite, et donc connaitre la taille des en-têtes et de la palette. Le code suivant effectue ce traitement, et renvoie une <em>ImageSource</em> à partir du presse-papier :</p>
<p><pre class="brush: csharp;">
        private ImageSource ImageFromClipboardDib()
        {
            MemoryStream ms = Clipboard.GetData(&quot;DeviceIndependentBitmap&quot;) as MemoryStream;
            if (ms != null)
            {
                byte[] dibBuffer = new byte[ms.Length];
                ms.Read(dibBuffer, 0, dibBuffer.Length);

                BITMAPINFOHEADER infoHeader =
                    BinaryStructConverter.FromByteArray&lt;BITMAPINFOHEADER&gt;(dibBuffer);

                int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
                int infoHeaderSize = infoHeader.biSize;
                int fileSize = fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage;

                BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER();
                fileHeader.bfType = BITMAPFILEHEADER.BM;
                fileHeader.bfSize = fileSize;
                fileHeader.bfReserved1 = 0;
                fileHeader.bfReserved2 = 0;
                fileHeader.bfOffBits = fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4;

                byte[] fileHeaderBytes =
                    BinaryStructConverter.ToByteArray&lt;BITMAPFILEHEADER&gt;(fileHeader);

                MemoryStream msBitmap = new MemoryStream();
                msBitmap.Write(fileHeaderBytes, 0, fileHeaderSize);
                msBitmap.Write(dibBuffer, 0, dibBuffer.Length);
                msBitmap.Seek(0, SeekOrigin.Begin);

                return BitmapFrame.Create(msBitmap);
            }
            return null;
        }
</pre></p>
<p>Définition des structures <em>BITMAPFILEHEADER</em> et <em>BITMAPINFOHEADER</em> :</p>
<p><pre class="brush: csharp;">
        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        private struct BITMAPFILEHEADER
        {
            public static readonly short BM = 0x4d42; // BM

            public short bfType;
            public int bfSize;
            public short bfReserved1;
            public short bfReserved2;
            public int bfOffBits;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct BITMAPINFOHEADER
        {
            public int biSize;
            public int biWidth;
            public int biHeight;
            public short biPlanes;
            public short biBitCount;
            public int biCompression;
            public int biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public int biClrUsed;
            public int biClrImportant;
        }
</pre></p>
<p>Classe utilitaire pour convertir des structures en binaire :</p>
<p><pre class="brush: csharp;">
    public static class BinaryStructConverter
    {
        public static T FromByteArray&lt;T&gt;(byte[] bytes) where T : struct
        {
            IntPtr ptr = IntPtr.Zero;
            try
            {
                int size = Marshal.SizeOf(typeof(T));
                ptr = Marshal.AllocHGlobal(size);
                Marshal.Copy(bytes, 0, ptr, size);
                object obj = Marshal.PtrToStructure(ptr, typeof(T));
                return (T)obj;
            }
            finally
            {
                if (ptr != IntPtr.Zero)
                    Marshal.FreeHGlobal(ptr);
            }
        }

        public static byte[] ToByteArray&lt;T&gt;(T obj) where T : struct
        {
            IntPtr ptr = IntPtr.Zero;
            try
            {
                int size = Marshal.SizeOf(typeof(T));
                ptr = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(obj, ptr, true);
                byte[] bytes = new byte[size];
                Marshal.Copy(ptr, bytes, 0, size);
                return bytes;
            }
            finally
            {
                if (ptr != IntPtr.Zero)
                    Marshal.FreeHGlobal(ptr);
            }
        }
    }
</pre></p>
<p>L&#8217;image obtenue par ce code peut être utilisée sans problème dans un contrôle <em>Image</em>.</p>
<p>Comme quoi, WPF a beau être une technologie « dernier cri », il faut encore parfois mettre les mains dans le cambouis ! Espérons que Microsoft corrigera ce bug dans la prochaine version…</p>
<br /> Tagged: clipboard, image, presse-papier, WPF <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/21/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=21&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2009/02/05/wpf-coller-une-image-du-presse-papier/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
		<item>
		<title>[WPF] Binding sur les paramètres d’application à l’aide d’une Markup extension</title>
		<link>http://tomlev.wordpress.com/2008/11/18/wpf-binding-sur-les-parametres-d%e2%80%99application-a-l%e2%80%99aide-d%e2%80%99une-markup-extension/</link>
		<comments>http://tomlev.wordpress.com/2008/11/18/wpf-binding-sur-les-parametres-d%e2%80%99application-a-l%e2%80%99aide-d%e2%80%99une-markup-extension/#comments</comments>
		<pubDate>Tue, 18 Nov 2008 13:56:48 +0000</pubDate>
		<dc:creator>Thomas Levesque</dc:creator>
				<category><![CDATA[Code sample]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[binding]]></category>
		<category><![CDATA[markup extension]]></category>
		<category><![CDATA[paramètres]]></category>
		<category><![CDATA[settings]]></category>
		<category><![CDATA[XAML]]></category>

		<guid isPermaLink="false">http://tomlev.wordpress.com/?p=3</guid>
		<description><![CDATA[Voilà, c’est fait, j&#8217;ai créé mon blog sur .NET&#8230; j&#8217;ai mis le temps, mais j&#8217;ai fini par y venir Je me présente rapidement : Thomas Levesque, 27 ans, ingénieur de formation. Je suis passionné depuis toujours par l&#8217;informatique, et plus particulièrement par la technologie .NET, que je suis de très près depuis ses débuts. Comme [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=3&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Voilà, c’est fait, j&#8217;ai créé mon blog sur .NET&#8230; j&#8217;ai mis le temps, mais j&#8217;ai fini par y venir <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>Je me présente rapidement : Thomas Levesque, 27 ans, ingénieur de formation. Je suis passionné depuis toujours par l&#8217;informatique, et plus particulièrement par la technologie .NET, que je suis de très près depuis ses débuts. Comme je suis du genre curieux, je passe pas mal de temps à fouiner dans les docs MSDN et sur le net pour m&#8217;auto-former sur les dernières nouveautés du framework. Aujourd&#8217;hui, je travaille comme développeur C# pour une PME en région parisienne.</p>
<p>Mais assez parlé de moi, venons-en au sujet qui nous intéresse : le binding sur les paramètres d&#8217;application en WPF.</p>
<p>Pour l&#8217;utilisateur d&#8217;une application, il est plus confortable de ne pas avoir à redéfinir sans arrêt ses préférences (dimensions de la fenêtre, activation de telle ou telle option&#8230;) : d&#8217;où l&#8217;utilité des paramètres d&#8217;application, introduits dans la version 2.0 du framework. Pour le développeur, c&#8217;est un peu pénible&#8230; même avec la classe Settings définie par Visual Studio, il reste encore pas mal de code à écrire pour lire et appliquer les paramètres au démarrage de l&#8217;appli, puis les enregistrer avant de quitter.</p>
<p>Dans Windows Forms, on pouvait définir des bindings entre les propriétés des contrôles et les paramètres d&#8217;application, mais ça restait peu pratique, et finalement assez peu utilisé (du moins c&#8217;est l&#8217;impression que j&#8217;en ai&#8230;).</p>
<p>Avec WPF, ça devient beaucoup plus intéressant&#8230; bien qu&#8217;il n&#8217;y ait pas de documentation &#8220;officielle&#8221; sur cette pratique, il est tout à fait possible de définir dans le code XAML des bindings sur les paramètres d&#8217;application. Par exemple, pour binder les dimensions de la fenêtre sur des paramètres, la méthode qui est présentée sur de nombreux blogs est la suivante :</p>
<pre>
<pre class="brush: xml;">
&lt;Window x:Class=&quot;WpfApplication1.Window1&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:p=&quot;clr-namespace:WpfApplication1.Properties&quot;
        Title=&quot;Window1&quot;
        Height=&quot;{Binding Source={x:Static p:Settings.Default}, Path=Height, Mode=TwoWay}&quot;
        Width=&quot;{Binding Source={x:Static p:Settings.Default}, Path=Width, Mode=TwoWay}&quot;
        Left=&quot;{Binding Source={x:Static p:Settings.Default}, Path=Left, Mode=TwoWay}&quot;
        Top=&quot;{Binding Source={x:Static p:Settings.Default}, Path=Top, Mode=TwoWay}&quot;&gt;
</pre>
</pre>
<p>(Dans cet exemple, Height, Width, Top et Left sont des paramètres de l’application)</p>
<p>Cette méthode a l’avantage de fonctionner, mais franchement, est-ce que vous vous voyez écrire ça des dizaines de fois, pour chaque paramètre de l’application ? C’est long à écrire, peu intuitif, répétitif, et ça rend le code globalement peu lisible…</p>
<p>Loin de moi l’idée de dénigrer ceux qui ont eu cette idée, bien sûr… mais il est très facile d’améliorer cette méthode, en créant notre propre « markup extension ». Nous allons donc définir une classe qui va hériter de Binding, et servir spécifiquement à lier des propriétés à des paramètres de l’application.</p>
<p>Une « markup extension » (qu’on pourrait traduire approximativement par « balise d’extension ») est un objet qu’on peut utiliser dans le code XAML pour récupérer une valeur. On en utilise en permanence en WPF : Binding, StaticResource, DynamicResource sont des exemples de markup extensions.</p>
<p>On peut facilement définir sa propre markup extension en créant une classe dérivée de MarkupExtension, qui doit implémenter une méthode ProvideValue. En l’occurrence, l’essentiel de ce qu’on veut faire est déjà implémenté dans la classe Binding (qui hérite indirectement de MarkupExtension). Nous allons donc hériter directement de Binding, et simplement initialiser les propriétés qui nous intéressent pour se binder sur les paramètres :</p>
<p><pre class="brush: csharp;">
using System.Windows.Data;

namespace WpfApplication1
{
    public class SettingBindingExtension : Binding
    {
        public SettingBindingExtension()
        {
            Initialize();
        }

        public SettingBindingExtension(string path)
            :base(path)
        {
            Initialize();
        }

        private void Initialize()
        {
            this.Source = WpfApplication1.Properties.Settings.Default;
            this.Mode = BindingMode.TwoWay;
        }
    }
}
</pre></p>
<p>Notez le suffixe « Extension » à la fin du nom de la classe : par convention, la plupart des markup extensions ont ce suffixe (Binding est une exception qui confirme la règle…). Ce suffixe pourra être omis quand on utilisera la classe en XAML (un peu comme les attributs, dont on omet le suffixe « Attribute » ).</p>
<p>Dans cette classe, on a défini 2 constructeurs, qui correspondent à ceux de Binding. On ne redéfinit pas la méthode ProvideValue, car celle de la classe Binding nous convient parfaitement (et d’ailleurs elle est marquée comme finale (sealed), si bien qu’on ne pourrait pas la redéfinir…). Le code « intéressant », si j’ose dire, est dans la méthode Initialize. On y définit la propriété Source comme étant les paramètres de notre appli, de façon à ce que le Path indiqué pour le binding renvoie le paramètre qui nous intéresse, et Mode = TwoWay, de façon à ce que les paramètres soient automatiquement mis à jour à partir de l’interface. L’intérêt étant de ne pas avoir à redéfinir ces propriétés à chaque fois…</p>
<p>Pour utiliser cette classe, c’est tout simple ! Reprenons l’exemple précédent, en remplaçant les Binding par notre extension SettingBinding :</p>
<p><pre class="brush: xml;">
&lt;Window x:Class=&quot;WpfApplication1.Window1&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:my=&quot;clr-namespace:WpfApplication1&quot;
        Title=&quot;Window1&quot;
        Height=&quot;{my:SettingBinding Height}&quot;
        Width=&quot;{my:SettingBinding Width}&quot;
        Left=&quot;{my:SettingBinding Left}&quot;
        Top=&quot;{my:SettingBinding Top}&quot;&gt;
</pre></p>
<p>C’est tout de même plus lisible et plus facile à utiliser…</p>
<p>Ah, et bien sûr, pour que ça marche, on n’oublie pas d’enregistrer les paramètres dans l’évènement Exit de l’application…</p>
<p><pre class="brush: csharp;">
        private void Application_Exit(object sender, ExitEventArgs e)
        {
            WpfApplication1.Properties.Settings.Default.Save();
        }
</pre></p>
<p>Et voilà, les dimensions de la fenêtre seront enregistrées et restaurées à chaque lancement de l’application, sans qu’on ait rien de plus à coder !</p>
<p><a href="http://tlevesque.developpez.com/files/SettingBindingSample.zip">Télécharger les sources</a></p>
<p><strong>Mise à jour :</strong> Si vous voulez en savoir plus sur les markup extensions, je vous invite à lire le tutoriel que j&#8217;ai écrit suite à ce billet : <a href="http://tlevesque.developpez.com/dotnet/wpf-markup-extensions/">Les markup extensions en WPF</a></p>
<br /> Tagged: binding, markup extension, paramètres, settings, WPF, XAML <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tomlev.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tomlev.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tomlev.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tomlev.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/tomlev.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/tomlev.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/tomlev.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/tomlev.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tomlev.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tomlev.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tomlev.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tomlev.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tomlev.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tomlev.wordpress.com/3/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tomlev.wordpress.com&amp;blog=5561502&amp;post=3&amp;subd=tomlev&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://tomlev.wordpress.com/2008/11/18/wpf-binding-sur-les-parametres-d%e2%80%99application-a-l%e2%80%99aide-d%e2%80%99une-markup-extension/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f4acdb91aba11ddf8f03d4b12453f3d5?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">tomlev</media:title>
		</media:content>
	</item>
	</channel>
</rss>
