Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Thomas Lebrun

Tout sur WPF, LINQ, C# et .NET en général !

Actualités

[WPF] Découvrons la collection CompositeCollection

Je dois reconnaitre que j'ai découvert très récemment cette collection mais elle semble être bien pratique.

En effet, la CompositeCollection vous permet de mélanger plusieurs collections et éléments de façon à ce qu'ils soient affichés comme une seule et même liste.

Par exemple, il arrive souvent que l'on rencontre des gens se demandant comment rajouter un élément vide dans une ComboBox qui est bindée à une ObservableCollection. Pour cela, la première idée serait de travailler directement sur l'ObservableCollection utilisée comme source de données.

L'autre technique est de passer par cette fameuse CompositeCollection soit:

<ComboBox x:Name="TheCombo" DisplayMemberPath="Name">

    <ComboBox.ItemsSource>

        <CompositeCollection>

            <CollectionContainer Collection="{Binding Source={StaticResource TheItems}}" />

            <ComboBoxItem Content="" />

        </CompositeCollection>

    </ComboBox.ItemsSource>

</ComboBox>

C'est simple et efficace mais attention tout de même à ne pas en abuser, afin de conserver un code un tant soit peu correctement organisé Wink

 

A+

[WPF] Intellisense XAML dans Expression Blend !

Je viens de voir passer ce post, sur le blog de Stefan Dobrev, qui devrait faire plaisir à plus d'une personne Smile

En effet, il a développé un petit addin à Expression Blend qui lui permet d'avoir... l'Intellisense:

Le téléchargement et la procédure d'installation peuvent se trouver à l'adresse suivante: http://blogs.telerik.com/StefanDobrev/Posts/08-08-04/IntelliSense_for_Expression_Blend.aspx

 

En voila une bonne idée Wink

 

A+

[WPF] Comment savoir si on peut se binder à une propriété et/ou si le mode de binding de celle-ci est à TwoWay par défaut ?

Il peut-être intéressant, dans certains scénarios, de savoir si l'on peut se binder sur une propriété particulière ou de savoir si le mode de binding par défaut de cette propriété est TwoWay.

Bien sur, la première solution sera de faire le test et d'agir en conséquences si une exception est retournée. Mais il existe un moyen plus propre de parvenir à nos fins.

En effet, chaque Dependency Property (car je rappele que seules les Dependency Properties peuvent-être utilisées comme cible de binding) possède une méthode nommée GetMetaData qui permet de connaitre un grand nombre d'information sur une propriété particulière comme son mode de binding par défaut, si elle peut-être bindée, etc....:

private void GetPropertyMetaData(DependencyProperty property)

{

    var btn = new Button();

    var pm = property.GetMetadata(btn) as FrameworkPropertyMetadata;

 

    if (pm != null)

    {

        var isDataBindingAllowed = pm.IsDataBindingAllowed;

 

        var isBindTwoWayByDefault = pm.BindsTwoWayByDefault;

    }

}

La liste des méta-données qu'il est possible de connaitre pour une propriété particulière est assez conséquente, comme le montre cette image:

image

On pourrait tout de même trouver étrange de disposer d'une méta-donnée nommée IsDataBindingAllowed lorsque l'on sait que les Dependency Properties sont les seules propriétés que l'on peut utiliser pour du binding.

Si les Dependency Properties disponibles avec WPF sont, à ma connaissance (mais il y en a surement que je ne connais pas) toutes bindables, cela n'est pas forcément le cas pour Dependency Properties que vous crééz vous-même. Et pour créer une Dependency Property qui ne peut pas être bindée ou, plus généralement, pour créer une Dependency Property en lui spécifiant des méta-données particulières, il vous suffit de la créer de la même manière que d'habitude mais d'utiliser les membres de l'énumération FrameworkPropertyMetadataOptions pour spécifier les différentes méta-données de votre propriété:

public int Count

{

    get { return (int)GetValue(CountProperty); }

    set { SetValue(CountProperty, value); }

}

 

public static readonly DependencyProperty CountProperty =

DependencyProperty.Register("Count", typeof(int), typeof(Window1), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.NotDataBindable));

 

Et le tour est joué Smile

 

A+

[WPF] Comment ajouter un élément à un ItemsControl dont la propriété ItemsSource a été spécifiée ?

Il est très fréquent que l'on ait besoin, dans son programme, d'ajouter un élément à un ItemsControl (ListBox, ComboBox, etc...). Cela se passe très bien si vous avez indiqué les différents éléments du contrôle mais si vous êtes passé par la propriété ItemsSource, vous risquez d'être vite déçu.

En effet, le code suivant compile

ComboBoxItem item = new ComboBoxItem

                        {

                            Content = "Hello World"

                        };

 

this.cb.Items.Add(item);

Mais il déclenche une exception à l'exécution:

"Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead."

image

En effet, si l'on regarde la MSDN à propos de la propriété ItemsSource, voici ce que l'on peut y voir:

"When the ItemsSource property is set, the Items collection is made read-only and fixed-size."

Heureusement, il est possible de contourner ce "problème" pour arriver à ajouter notre élément manuellement.

La solution consiste tout simplement à accéder à l'objet de type IEnumerable que vous avez assigné à la propriété ItemsSource et à ajouter, sur cet objet, votre nouvel élément. Soit, par le code:

if (this.cb.ItemsSource != null)

{

    Photo p = new Photo

                  {

                      Name = "Hello World"

                  };

 

    ((ObservableCollection<Photo>) this.cb.ItemsSource).Add(p);

 

    this.cb.SelectedIndex = this.cb.Items .Count - 1;

}

De cette façon, plus aucuns problèmes ne survient mais il vous faudra faire attention à appliquer un éventuel filtre lorsque vous tenterez d'accéder à la collection car sinon, vous risquez de vous retrouver avec cet élément "temporaire" Wink

 

A+

[WPF] Comment déclencher un évènement sur un contrôle ?

Il peut parfois s'avérer nécessaire de vouloir déclencher manuellement un évènement sur un contrôle. En effet, vous pourriez vouloir, par exemple, envoyer l'évènement KeyDown à une ComboBox alors que c'est la fenêtre de votre application qui a le focus.

Heureusement, on peut faire cela très simplement avec WPF. En effet, il vous suffit de vous abonner à l'évènement qui vous intéresse (par exemple, l'évènement KeyDown de la Window) et de re-déclencher cet évènement sur un contrôle précis.

Pour redéclencher l'évènement, il vous suffit d'appeler la méthode RaiseEvent qui prend en paramètre une instance d'un objet de type System.Windows.RoutedEventArgs qui identifie l'évènement routé (RoutedEvent) que vous voulez redéclencher.

Autrement dit, dans notre exemple, si l'on veut relancer l'évènement KeyDown d'une ComboBox à partir du KeyDown de la Window, il nous suffit d'appeler ce code:

private void Window_KeyDown(object sender, KeyEventArgs e)

{

    if (e.Key == Key.Up || e.Key == Key.Down)

    {

        this.cb.RaiseEvent(e);

    }

}

(Bien entendu, dans cet exemple, cb est une instance de la classe ComboBox mais cela aurait fonctionné avec un ListBox, une ListView, etc...)

 

A+

[WPF] Des requêtes NDepend pour analyser vos projets WPF

Je ne vais pas vous reparler de NDepend, l'outil développé par Patrick Smacchia, car c'est un outil vraiment très précieux pour tous les développeurs/architectes qui souhaitent avoir des métriques sur le code de leur application.

Si vous ne connaissez pas NDepend, je ne saurais que tro vous conseillé ce lien, en anglais, ou celui-ci, en français.

Depuis quelque temps, je manipule cet outil et je me demandais comment pouvoir profiter de ses possibilités dans le cadre d'un développement WPF. J'en suis arriver à la conclusion que pouvoir disposer de requêtes CQL propres à WPF ne serait pas un mal Smile

Ainsi, j'ai écrit 6 requêtes que je souhaiterais vous faire partager:

<CQLGroup Name="WPF Queries" Active="True">

  <CQLQuery Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False">

    // &lt;Name&gt;Number of DependencyProperties&lt;/Name&gt;

    SELECT FIELDS WHERE IsStatic AND IsPublic AND IsOfType "OPTIONAL:System.Windows.DependencyProperty" AND NameLike "Property$"

  </CQLQuery>

  <CQLQuery Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False">

    // &lt;Name&gt;Number of Routed Events&lt;/Name&gt;

    SELECT FIELDS WHERE IsStatic AND IsPublic AND IsOfType "OPTIONAL:System.Windows.RoutedEvent" AND NameLike "Event$"

  </CQLQuery>

  <CQLQuery Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False">

    // &lt;Name&gt;Number of Binding&lt;/Name&gt;

    SELECT FIELDS WHERE IsOfType "OPTIONAL:System.Windows.Data.Binding"

  </CQLQuery>

  <CQLQuery Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False">

    // &lt;Name&gt;Number of ControlTemplate&lt;/Name&gt;

    SELECT FIELDS WHERE IsOfType "OPTIONAL:System.Windows.Controls.ControlTemplate"

  </CQLQuery>

  <CQLQuery Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False">

    // &lt;Name&gt;Number of DataTemplate&lt;/Name&gt;

    SELECT FIELDS WHERE IsOfType "OPTIONAL:System.Windows.DataTemplate"

 

  </CQLQuery>

  <CQLQuery Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False">

    // &lt;Name&gt;Number of Routed Commands&lt;/Name&gt;

    SELECT FIELDS WHERE IsStatic AND IsPublic AND IsOfType "OPTIONAL:System.Windows.Input.RoutedUICommand" AND NameLike "Command$"

  </CQLQuery>

</CQLGroup>

Ces requêtes vous permettent de savoir combien de Dependency Properties, Routed Events, Binding, etc... vous avez déclaré dans votre code, à condition que ceux-ci respectent les conventions de nommage (les Dependency Properties doivent finir par Property, les Routed Events doivent se terminer par Event, etc....).

Pour les plus curieux, voici un aperçu du résultat (testé sur un projet de démo):

image

C'est simple, rapide à mettre en place et cela vous permettra d'avoir des métriques vraiment précises quand au type de projet sur lequel vous travaillé (WPF en l'occurence dans notre cas).

Il est cependant à noter que NDepend ne permet pas, à l'heure actuelle, d'analyser les ressources d'une application. Ainsi, dans le cas de WPF, tout le code XAML n'est pas analysé: les requêtes montrées juste avant ne fonctionnent que pour le code behind. Autant, pour le binding, cela peut paraitre génant autant pour les Dependency Properties ou Routed Events, pas de soucis étant donné que l'on ne peut pas les déclarer en XAML Wink

En espérant que cela plaise/serve à certains d'entre vous !

 

A+

[WPF] Que faire lorsque vous n'arrivez pas à supprimer un élément d'une ComboBox/ListBox liée à une table LINQ To SQL ?

Imaginez le scénario suivant, très fréquent avec WPF (entre autre): vous disposez d'une ComboBox/ListBox et la source de données de ce contrôle est une "table" générée par LINQ To SQL.

Un bouton de votre interface vous permet de supprimer un élément de votre ListBox/ComboBox et d'envoyer les modifications sur le serveur (via un appel à SubmitChanges)

Seul petit problème, rencontré par beaucoup de monde: l'élément est bien supprimé en base mais l'interface utilisateur ne change pas (l'élément reste dans la ListBox/ComboBox).

Le problème vient du fait que les classes générées par LINQ To SQL n'implémente pas l'interface INotifyCollectionChanged, qui permet de déclencher un évènement notifiant l'interface utilisateur qu'un élément a été ajouté/supprimé.

Afin de corriger le problème dont nous venons de parler, il convient donc d'implémenter cette interface sur les classes générées (n'oubliez pas que ce sont des classes partielles) ou bien de passer par la classe ObservableCollection<T>, qui implémente déjà cette interface.

Ainsi, ce code, non fonctionnel:

PhotosDataContext dc = new PhotosDataContext();

this.lb.DataContext = dc.Photos.ToList();

Devient:

PhotosDataContext dc = new PhotosDataContext();

 

this.lb.DataContext = new ObservableCollection<Photo>(dc.Photos.ToList());

Et, lors de l'appel à la méthode de suppression, il "suffit" d'accéder à la propriété DataContext, de la convertir en ObservableCollection (car il s'agit d'un object) et de supprimer l'élément sélectionné dans le contrôle:

private void btnDelete_Click(object sender, RoutedEventArgs e)

{

    if (this.lb.SelectedItem != null)

    {

        dc.Photos.DeleteOnSubmit((Photo)this.lb.SelectedItem);

 

        ((ObservableCollection<Photo>)this.lb.DataContext).Remove((Photo)this.lb.SelectedItem);

 

        dc.SubmitChanges();

        }

    }

}

Attention, pour que ce code fonctionne, n'oubliez pas de mettre à True la propriété IsSynchronizedWithCurrentItem, sinon vous risquez d'avoir une NullReferenceException sur la propriété SelectedItem:

IsSynchronizedWithCurrentItem = "True"

 

A+

[WPF] Comment filtrer une Listbox en fonction d'informations saisies par l'utilisateur ?

Voila un scénario tout ce qu'il y a de plus simple: vous disposez d'une IHM composée d'une zone de saisie et d'une listbox et vous souhaitez filtrer le contenu de cette listbox en fonction de ce que l'utilisateur insère dans la textbox.

Pour faire cela, avec WPF, il convient tout d'abord de s'abonner à l'évènement Text_Changed de notre Textbox. Ensuite, nous allons récupérer la vue par défaut pour notre Listbox, au moyen d'un appel à la méthode GetDefaultView:

var view = CollectionViewSource.GetDefaultView(this.lb.DataContext);

Enfin, sur cette vue par défaut, nous appliquons un filtre, via la propriété Filter:

view.Filter = photo => ((Photo)photo).Name.ToLower(CultureInfo.InvariantCulture).StartsWith(this.tbSearch.Text);

Ce filtre n'est rien d'autre qu'un délégué, ici représenté avec les expressions lambda de C# 3. La méthode est appelée pour chaque élément de la Listbox et se doit de renvoyer vrai ou faux en fonction de ce que nous indiquons comme critère de rechercher/filtre.

Comme vous pouvez le constater, cela s'avère très simple à mettre en place. Cependant, il faut bien prendre garde, lorsque l'on travaille avec LINQ To SQL, à ce que la source de données implémente, d'une manière ou d'une autre, l'interface ICollection.

En effet, dans le cas contraire, vous recontrerez cette erreur lorsque vous essayerez d'appliquer votre filtre:

"Specified method is not supported."

image

Ainsi, lorsque vous faîtes l'assignation du DataContext/de l'ItemsSource, faîtes un appel à la méthode ToList:

this.lb.DataContext = dc.Photos.ToList();

Et plus de problèmes Smile

 image image

 

Vous pouvez télécharger un projet Visual Studio 2008 reprenant les concepts vus dans ce post juste ici: FilterOnDemand

 

A+

[Silverlight] Comment échanger des données entre une application Silverlight et une page ASP.NET via cookies ?

Il est parfois nécessaire, dans une application Silverlight, de communiquer avec "l'extérieur". Dans la majorité des cas, on aura tendance à utiliser un Web Service mais qu'en est-il lorsque vous souhaitez communiquer avec la page ASP.NET qui héberge votre application Silverlight ?

L'utilisation des cookies est pour ainsi dire nécessaire dans ce cas. Grâce à Silverlight 2, vous avez la possibilité de créer un cookie qui sera récupéré coté ASP.NET. Pour cela, il suffit d'assigner la propriété cookie de la page, en passant par la méthode SetProperty.

Afin de se simplifier la vie au maximum, je vous conseille de passer par une classe qui va encapsuler l'appel à cette méthode:

public class CookieManager

{

    public static void SetCookie(string key, string val, TimeSpan? expires)

    {

        SetCookie(key, val, expires, null, null, false);

    }

 

    public static void SetCookie(string key, string val, TimeSpan? expires, string path, string domain, bool secure)

    {

        var fullCookie = new StringBuilder();

        fullCookie.Append(string.Concat(key, "=", val));

 

        if (expires.HasValue)

        {

            var expire = DateTime.UtcNow + expires.Value; fullCookie.Append(string.Concat(";expires=", expire.ToString("R")));

        }

 

        if (path != null)

        {

            fullCookie.Append(string.Concat(";path=", path));

        }

 

        if (domain != null)

        {

            fullCookie.Append(string.Concat(";domain=", domain));

        }

 

        if (secure)

        {

            fullCookie.Append(";secure");

        }

 

        HtmlPage.Document.SetProperty("cookie", fullCookie.ToString());

    }

}

Pour créer un cookie coté Silverlight, rien de plus simple:

private void btnCreateCookie_Click(object sender, RoutedEventArgs e)

{

    CookieManager.SetCookie("DemoSL2", "Hello World", DateTime.Now.TimeOfDay.Add(new TimeSpan(1, 0, 0, 0)));

}

Enfin, il ne vous reste plus qu'à accéder, coté ASP.NET, à ce cookie nouvellement créé:

public void btnGetCookieClick(object sender, EventArgs e)

{

    var cookie = Request.Cookies["DemoSL2"];

 

    if(cookie != null)

    {

        var s = cookie.Value;

    }

}

C'est simple, efficace et pourtant très pratique !

 

A+

[WPF] Comment cloner un contrôle WPF ?

La semaine dernière, pour les besoins d'un client, j'ai eu besoin de trouver le moyen de cloner un objet graphique WPF afin d'en avoir une copie.

Dans la plupart des cas, on peut se dire qu'appeler la méthode Clone suffit. Seul petit problème: lorsque l'on souhaite cloner un UserControl, cette méthode n'est pas disponible.

Fort heureusement, dans ce post du forum MSDN US, vous trouverez une méthode qui réalisera le clonage de vos objets.

La technique est simple mais efficace: on obtient la représentation XAML du contrôle que l'on souhaite cloner et on appelle la méthode XamlReader.Load en passant en paramètre le code XAML à instancier.

Pour vous faire gagner du temps, voici le code de la méthode:

public static UIElement cloneElement(UIElement orig)

{

    if (orig == null)

        return (null);

 

    string s = XamlWriter.Save(orig);

 

    StringReader stringReader = new StringReader(s);

 

    XmlReader xmlReader = XmlTextReader.Create(stringReader, new XmlReaderSettings());

 

    return (UIElement)XamlReader.Load(xmlReader);

}

En espérant que cela vous utile Wink

 

A+

[Perso] Tom.MVP.Years++ && Tom.MVP.Category = "Client Application Development"

En voila un titre sans doute un peu bizarre Big Smile Enfin bref, juste pour vous dire que je viens d'être renouvellé MVP avec cependant un changement de catégorie: de MVP C#, je passe à MVP Client Application Development.

Cette catégorie contient tout les MVPs qui travaillent, de prêt ou de loin, avec les technologies de développement d'application clientes mais principalement WPF et/ou Silverlight.

Je tenais donc à remercier une nouvelle fois Microsoft pour me faire à nouveau confiance et me décerner cette reconnaissanceSmile

 

A+

[Silverlight] Retrouver la WatermarkTextBox de Silverlight 2

Dans la Beta 1 de Silverlight 2, on disposait d'un contrôle bien sympathique nommé WatermarkTextBox. Ce contrôle vous permettait d'afficher, dans la zone de saisie, du texte qui disparaitrait dès lors que l'utilisateur entrerait lui-même du texte: très pratique sur les IHM où l'on ne dispose pas de beaucoup de place:

Cependant, on est au regret de constater que ce contrôle n'existe plus dans la Beta 2. Certes, il est très facile de le refaire mais bon, cela reste du travail en plus....
De même, on me demande souvent pourquoi Microsoft a décidé de retirer ce contrôle de la liste des contrôles disponibles avec Silverlight.

Je dois reconnaitre que je me posais la même question, sans vraiment avoir d'idée bien précise et je reconnais que la réponse, vue sur le blog de Kathy Kam, permet de clarifier les choses:

"We decided to remove the control because in a future version of Silverlight, we will be adding a “Watermark” property to TextBox."

Bref, pour ceux qui souhaitent re-disposer rapidement de ce contrôle, rendez-vous sur le blog de Kathy, qui le met en téléchargement (code source et tests unitaires): http://blogs.msdn.com/kathykam/archive/2008/06/23/watermarkedtextbox-for-silverlight-2-beta-2.aspx

 

A+

[WPF] Exemple d'application utilisant WPF et ADO.NET

La majorité des exemples et/ou démonstrations que l'on rencontre à l'heure actuelle à propos de WPF utilise les nouvelles technologies d'accès aux données: LINQ To SQL ou ADO.NET Entity Framework.

Seul problème: tout le monde ne peut pas encore utiliser toutes ces technologies en même temps.

Ainsi, et pour répondre à certaines questions/demandes, j'ai développé une petite application qui montre comment utiliser ADO.NET avec WPF. Rien de bien méchant, je vous l'accorde, mais cela permettra à toute les personnes qui se posent la question d'avoir une réponse Smile

L'application peut-être téléchargée ici: DemoWPFWithDB

 

A+

 

PS: Penser à changer la chaîne de connexion et, éventuellement, à installer la base Northwind sur votre machine pour que l'exemple fonctionne.

[WPF] Comment appeler une méthode depuis du code XAML ?

Beaucoup de gens se demande s'il est possible d'appeler une méthode depuis son code XAML. Par exemple, comment appeler une méthode d'un Web Service directement depuis son XAML ?

Il est possible d'y parvenir en utilisant un ObjectDataProvider. Prenons l'exemple de la classe suivante:

public class BusinessClass

{

    public string SayHelloToSomeOne(string someone)

    {

        return !string.IsNullOrEmpty(someone) ? string.Format("Hello {0}", someone) : "Hello everybody";

    }

}

Comment parvenir à appeler cette méthode depuis un ObjectDataProvider ? Tout simplement en spécifiant les propriétés ObjectType et MethodName de notre objet:

<ObjectDataProvider x:Key="odp" ObjectType="local:BusinessClass" MethodName="SayHelloToSomeOne">

    <ObjectDataProvider.MethodParameters>

        <s:String>Thomas</s:String>

    </ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

A noter qu'il est possible d'utiliser la propriété ObjectInstance pour se lier à une instance déjà existante.

Ensuite, dans votre code XAML, il ne vous reste plus qu'à vous lier à votre objet, via un petit binding:

<TextBlock x:Name="tb" Grid.Row="0" Text="{Binding Source={StaticResource odp}}" />

De plus, si vous souhaitez modifier les paramètres de la méthode appellée, il vous convient d'accéder, par code, à votre ObjectDataProvider:

private void btn_Click(object sender, RoutedEventArgs e)

{

    var odp2 = Resources["odp"] as ObjectDataProvider;

    if (odp2 != null)

    {

        odp2.MethodParameters[0] = string.Empty;

        odp2.Refresh();  

    }

}

Et le tour est joué Smile

 

A+

[WPF] Bug avec les BitmapEffects

Si vous travaillez avec les BitmapEffect de WPF, vous avez peut-être remarqué un bug pour le moins génant. Essayer le code suivant:

<Window x:Class="TestSmyley.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Window1" Height="300" Width="300">

    <Grid>

        <Rectangle>

            <Rectangle.Fill>

                <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

                    <GradientStop Color="White" Offset="0"/>

                    <GradientStop Color="Black" Offset="1"/>

                </LinearGradientBrush>

            </Rectangle.Fill>

        </Rectangle>

 

        <TextBlock Name="MyBox" VerticalAlignment="Bottom" TextAlignment="Center" Foreground="Silver">

            <TextBlock.BitmapEffect>

                <OuterGlowBitmapEffect GlowColor="White" GlowSize="2"/>

            </TextBlock.BitmapEffect>

        </TextBlock>

 

        <Button Margin="0,5,0,0" Width="100" Height="40" Click="Button_Click">Mettre du texte</Button>

        <Button Margin="0,120,0,0" Width="100" Height="40" Click="Button_Click_1">Effacer le texte</Button>

    </Grid>

 

</Window>

public partial class Window1 : Window

{

    public Window1()

    {

        InitializeComponent();

    }

 

    private void Button_Click(object sender, RoutedEventArgs e)

    {

        MyBox.Text = "Ceci est du texte";

    }

 

    private void Button_Click_1(object sender, RoutedEventArgs e)

    {

        MyBox.Text = "";

    }

}

Si vous cliquez sur le deuxième bouton, vous vous attendez à ce que le texte de la TextBlock disparaisse. Hors, à l'exécution, il s'avère que cela n'est pas le cas: le contenu est bien remis à zéro mais l'interface graphique, elle, ne change pas:

image

Il s'agit en effet d'un bug connu par Microsoft qui propose ceci, comme solution:

private void Button_Click_1(object sender, RoutedEventArgs e)

{

    object be = MyBox.GetValue(TextBlock.BitmapEffectProperty);

    MyBox.ClearValue(TextBlock.BitmapEffectProperty);

    MyBox.ClearValue(TextBlock.TextProperty);

    MyBox.SetValue(TextBlock.BitmapEffectProperty, be);

}

Il est à noter qu'il s'agit d'une solution "temporaire" car l'utilisation de la propriété BitmapEffect est déprécié dans le Service Pack 1 !

En savoir plus: http://forums.msdn.microsoft.com/en-US/wpf/thread/d2a5ec3c-db4b-4360-8265-84552fa2d3bf/

 

A+

[Silverlight] Comment migrer son "Custom Control" de la Beta 1 à la Beta 2

Avec l'arrivée de Silverlight 2 Beta 2, on peut remarquer qu'il y a quand même pas mal de "breaking changes" entre ces 2 versions. L'une des plus importantes, à mon sens, est la méthodologie utilisée pour la création de contrôles personnalisés.

Avant, nous étions obligé d'hériter d'un contrôle de Silverlight, de surcharger une méthode (OnApplyTemplate) et de définir, à la main, l'apparence de notre contrôle. Avec cette nouvelle version, on découvre un nouvel élément qui entre en jeu: le Visual State Manager, qui vous permettra de définir (via Expression Blend), les différents états et les transitions pour un contrôles.

Certes, cela est très pratique mais cela signifie-t-il que tout le travail que l'on a fait sur la Beta 1 doit-être refait ? Par forcément ! Smile

En effet, si vous souhaitez continuer à développer vos propres contrôles, il faut trouver le moyen d'indiquer au moteur Silverlight que les styles/templates de vos contrôles se trouvent dans le fichier generic.xaml. Pour cela, rien de plus simple: il vous suffit d'indiquer le style par défaut de vos contrôles, en rajoutant une ligne dans le constructeur:

public ProgressBar() : base()

{

    DefaultStyleKey = typeof(ProgressBar);

}

Ensuite, surchargez toujours la méhode OnApplyTemplate (attention, la signature a changé: avant, le modificateur était protected et on est maintenant passé à public):

public override void OnApplyTemplate()

Et c'est tout: votre contrôle devrait remarcher correctement :)

Bien que fonctionnelle, cette technique requiert cependant d'écrire pas mal de code pour faire passer le contrôle d'un état à un autre. Grâce au Visual State Manager, le designer a la possibilité de gérer cela directement dans Blend ! Pour avoir un aperçu, je vous recommande cette vidéo: Aperçu du Visual State Manager.

 

A+

[Silverlight] Silverlight 2 Beta 2 est disponible en téléchargement !

Microsoft nous l'avais promis avant la fin de la semaine, c'est maintenant chose faite !

Vous pouvez télécharger le Runtime Silverlight 2 Beta 2 a cette addresse:

http://www.microsoft.com/silverlight/resources/install.aspx?v=2.0

Pour les extensions Visual Studio, c'est par ici: Install Silverlight Tools Beta 2 for Visual Studio 2008

Enfin, vous trouverez une nouvelle version de Blend et du Deep Zomm Composer: Install Expression Blend 2.5 June 2008 Preview & Install Deep Zoom Composer


Pour en savoir plus, un seul lien: http://weblogs.asp.net/scottgu/archive/2008/06/06/silverlight-2-beta2-released.aspx

 

A+

[WPF] Bug & Fix pour le contrôle "Graph" du Bag'O'Trick de Kevin Moore

Pour les besoins d'un projet WPF, l'équipe avec laquelle je travaille a eu besoin d'utiliser le fameux contrôle Graph, disponible dans le Bag'O'Trick de Kevin Moore.

Ce contrôle est très pratique et relativement simple à utiliser mais il s'avère que nous avons identifié un bug lorsque vous changez les nodes à la volée. Si vous réinstancier le contrôle à chaque fois, avec de nouvelles nodes, pas de problèmes. Seulement si vous conservez toujours le même contrôle masi que vous changez les nodes dynamiquement, vous risquez de vous retrouvez avec un contrôle qui fonctionne correctement la première fois mais qui n'affiche pas son élément central lors du changement des noeuds (y compris du noeud central). Pour être plus parlant, voici ce que vous risquez d'obtenir:

image

Comme vous pouvez le constatez, il semble manquer quelque chose au milieu.... Pour corriger ce problème, la solution est toute simple. Récupérer les sources du Graph sur le site de Kevin et ouvrez le fichier Graph.cs. Là, localisez les lignes de code suivante (aux alentours des 700):

_nodePresenters.Clear();

_nodePresenters.AddRange(newChildren);

Et juste au dessus, rajoutez le code suivant:

if (_centerDataInUse != null)

{

    SetUpCleanCenter(_centerDataInUse);

}

Pour que cela fonctionne, vous devrez cependant modifier le code de la méthode SetUpCleanCenter pour mettre en commentaire cette ligne qui peut, parfois, provoquer une exception:

Debug.Assert(m_centerGraphContentPresenter == null);

A présent, si vous réexécutez votre code, vous remarquez que tout fonctionne parfaitement bien:

image


Il est cependant possible, si vous faîtes ceci, que vous remarquiez que lorsque vous cliquez sur un des noeuds du graph, il reste affiché et ne disparaisse pas. Pour corriger ce problème, là encore, c'est très simple. Mettez en commentaire cette ligne de code, qui doit se trouver vers la ligne 598 de la méthode handleChanges:

_nodePresenters [ i ] = null;

 

En espérant que cela puisse en aider quelques-uns !

 

A+

[Article/WPF] Comment créer votre propre contrôle avec WPF

La création de contrôles, pour une application WPF (Windows Presentation Foundation), est une opération qui peut-être très simple à mettre en place ou bien qui peut prendre un certains temps lorsque l'on désire y ajouter un maximum de fonctionnalités.

Pour que vous sachiez comment vous y prendre, je vous ai écrit un petit article qui décrit les 2 façons de développer un contrôle avec WPF:

  • Créer un UserControl
  • Créer un Custom Control

Pour en savoir plus, c'est par ici: Comment créer votre propre contrôle avec WPF

N'hésitez pas à me faire part de vos commentaires/avis/etc. et j'espère que cela vous plaira et/ou vous sera utile !

 

A+

[WPF] Comment scroller automatiquement dans une ListBox ?

La ListBox de WPF est un composant très pratique qui vous permet de stocker différents types de données.

Lorsqu'il n'y a pas assez de place pour afficher tous les éléments, une scrollbar (horizontale et/ou verticale) apparait, vous permettant d'effectuer un défilement. Si vous en avez besoin, vous avez la possibilité de provoquer le défilement automatique par du code: cela peut s'avérer très pratique à certains moment, par exemple lorsque vous ajouter, à la volée, un élément (en effet, la ListBox ne défile pas automatiquement sur le dernier élément ajouté).

Pour effectuer ce défilement par code, vous avez plusieurs possibilités. Tout d'abord, vous pouvez appeler la méthode ScrollIntoView, qui prend en paramètre l'objet vers lequel vous souhaitez défilé:

this.lb.ScrollIntoView(this.lb.Items.GetItemAt(this.lb.Items.Count - 1));

Une autre possibilité est d'accéder au composant ScrollViewer qui compose la ListBox. Pour cela, rien de plus simple: il suffit de faire appel à la méthode VisualTreeHelper.GetChild:

Border border = VisualTreeHelper.GetChild(this.lb, 0) as Border;

 

if (border != null)

{

    ScrollViewer scrollviewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;

 

    if (scrollviewer != null)

    {

        //

    }

}

Une fois que vous avez accès au ScrollViewer, libre à vous d'utiliser les méthodes de cet object, comme:

  • ScrollToEnd
  • ScrollToBottom
  • Etc.

Une des méthodes intéressantes est la méthode ScrollToVerticalOffset, qui vous permet de faire défiler, de façon verticale, le contenu à la position précise d'un offset. Il est à noter que la même version existe en version horizontale (ScrollToHorizontalOffset):

Border border = VisualTreeHelper.GetChild(this.lb, 0) as Border;

 

if (border != null)

{

    ScrollViewer scrollviewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;

 

    if (scrollviewer != null)

    {

        scrollviewer.ScrollToVerticalOffset(scrollviewer.ViewportHeight);

    }

}

 

Bon développement à tous !

 

A+

Plus de Messages Page suivante »


Les 10 derniers blogs postés

- Attention : Arnaque IE7/MSN par .net is good... C# is better ;) le il y a 5 heures et 18 minutes

- Comme Mitsu :-) par Matthieu MEZIL le il y a 7 heures et 40 minutes

- [OpenDocument] Quelques détails sur l’implémentation dans Office 2007 par Julien Chable le il y a 10 heures et 53 minutes

- Page de démarrage CodeS-SourceS par Nix's Blog le 08-07-2008, 19:14

- SharePoint : Erreur pénible lors de la compilation de projet SharePoint - XmlSerializers.dll par Blog Technique de Romelard Fabrice le 08-07-2008, 18:43

- [MOSS Tip Of The Day] Comment connaître le mode de notre page de publication par Nicolas Humann le 08-07-2008, 18:14

- SETI@CodeS-SourceS par Nix's Blog le 08-07-2008, 16:49

- jQuery VS ASP.NET AJAX par Le blog technique de Loïc Bar le 08-07-2008, 15:29

- Lucas change de fonction chez Microsoft France par LucasR le 08-07-2008, 13:02

- Pixel par Blog Technique d'Audrey PETIT le 08-07-2008, 11:25