[WPF] Designer WPF 4.5 sur Blend pour Visual Studio 2012 ?

Le 19 janvier 2013 à 08:53

Récemment, Microsoft a annoncé l’arrêt de la gamme Expression. Cette gamme était disponible dès 2007 et permettait aux développeurs et aux designers de travailler main dans la main (ou dans la pratique, aux développeurs de désigner facilement leurs applications :-)).
Cette gamme regroupait plusieurs produits, et entre autre Expression Blend, qui était l’outil parfait pour travailler sur nos interfaces à travers plusieurs technologies : WPF et Silverlight.

A présent, Blend est disponible directement avec Visual Studio 2012. Mais il est permet, pour le moment, de ne travailler uniquement que sur des interfaces pour Windows 8 et Windows Phone. Assez problématique si l’on travaille avec la dernière version du Framework et WPF. Il reste la possibilité de travailler avec une ancienne version de Blend, mais on perd l’avantage de travailler avec la version 4.5 du Framework.
Il est vrai que le moteur de Visual Studio 2012 a été grandement amélioré et permet de designer plus facilement le XAML, mais ça reste loin de la facilité que nous apporte Blend.

La bonne nouvelle, c’est que Microsoft travaille sur une update 2 de Visual Studio intégrant une mise à jour pour Blend et apportant le support de WPF et Silverlight sur cette dernière mouture !

Si vous avez besoin de travailler tout de suite avec une version de Blend intégrant WPF 4.5, vous pouvez télécharger une version preview ici.

image

[WPF] Personnaliser le curseur

Le 4 août 2010 à 19:12

L'un des gros avantages de WPF c’est la personnalisation complète de notre application. Dans le même style que mon article sur la personnalisation de la sélection de texte et du curseur d’insertion, voici comment changer le curseur de la souris.

Tout cela est très simple à faire, pour commencer je vais ajouter le fichier du curseur dans mon application :

image

Ensuite, il faut définir le App.ani comme “Resource” (de base il est défini sur “Aucun”, ce qui fera qu’à la compilation notre application risque de ne pas trouver le curseur) :

image

Ensuite, sur nos éléments de l’interface, il existe la propriété “Cursor”, pour exemple, je vais le définir sur l’ensemble de la fenêtre :

<Window x:Class="Curseur.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Cursor="pack://application:,,,/Resources/Cursor/App.ani">
    <Grid>
        
    </Grid>
</Window>

Ce qui va donner à l’exécution :

image

Je peux bien sûr définir le curseur sur d’autres éléments tels que sur Textbox, ListBox… etc :).

Pour télécharger les sources, c’est ici.


A bientôt !

[WPF] Augmenter les performances des ResourceDictionary

Le 17 juillet 2010 à 14:29

Quand vous déclarer un ResourceDictionary, cela crée une nouvelle instance de celui-ci. Mais si par exemple, vous avez des dizaines de contrôles personnalisés et que ceux-ci ont chacun un dictionnaire de ressource en commun, le même dictionnaire de ressource sera instancié plusieurs fois.

Pour remédier à ce problème, Christian Mosers, a crée la classe SharedResourceDictionary. Celle ci s’utilise de la même façon qu’un ResourceDictionary à la différence que cette fois ci, si un dictionnaire de ressource est instancié plusieurs fois, il sera en fait chargé qu’une seule fois.

Avec cette solution, il y aura un impact positif sur les performances et sur la mémoire !

[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", 
"WPFTutorial.Utils")]
 
/// <summary>
/// The shared resource dictionary is a specialized resource dictionary
/// that loads it content only once. If a second instance with the same source
/// is created, it only merges the resources from the cache.
/// </summary>
public class SharedResourceDictionary : ResourceDictionary
{
    /// <summary>
    /// Internal cache of loaded dictionaries 
    /// </summary>
    public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries =
        new Dictionary<Uri, ResourceDictionary>();
 
    /// <summary>
    /// Local member of the source uri
    /// </summary>
    private Uri _sourceUri;
 
    /// <summary>
    /// Gets or sets the uniform resource identifier (URI) to load resources from.
    /// </summary>
    public new Uri Source
    {
        get { return _sourceUri; }
        set
        {
            _sourceUri = value;
 
            if (!_sharedDictionaries.ContainsKey(value))
            {
                // If the dictionary is not yet loaded, load it by setting
                // the source of the base class
                base.Source = value;
 
                // add it to the cache
                _sharedDictionaries.Add(value, this);
            }
            else
            {
                // If the dictionary is already loaded, get it from the cache
                MergedDictionaries.Add(_sharedDictionaries[value]);
            }
        }
    }
}

Et pour l’utiliser :

<ResourceDictionary.MergedDictionaries>
   <SharedResourceDictionary Source="/MyControlLibrary;component/Themes/Brushes.xaml"  />
</ResourceDictionary.MergedDictionaries>

A bientôt !

[WPF] Appliquez des thèmes dynamiquement dans vos applications

Le 11 juillet 2010 à 07:24

Dans mon post précédent, j’ai fait part d’une solution pour localiser vos applications sans passer par l’outil LocBaml. J’utilisais des ResourceDictionary que j’affectais dynamiquement au moment de l’exécution. Cette méthode peut être utilisée dans plusieurs cas, et l’une d’entre elle : les thèmes.
Devoir gérer plusieurs thèmes dans les applications était auparavant un travail assez complexe (je pense à la création de contrôle personnalisée, que la technologie WPF nous a permis de faire beaucoup plus simplement), mais maintenant avec les ResourceDictionary, c’est devenu relativement “simple”. Bien sûr, ceci n’est qu’un exemple d’application et il reste assez basique à mettre en place, mais cela pourra vous inspirer.

Nous allons créer deux thèmes : “default” et “light”. Pour cela, comme expliquer ci-dessus, nous allons ajouter des ResourceDictionary (dictionnaire de ressources en français) :

image


Maintenant, nous allons remplir ces fichiers :

<!-- Default.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:System="clr-namespace:System;assembly=mscorlib">

    <System:String x:Key="ThemeName">Default</System:String>
    <SolidColorBrush x:Key="win_background" Color="DarkBlue"></SolidColorBrush>

    <Style TargetType="Button">
        <Setter Property="FontFamily" Value="Verdana"/>
        <Setter Property="FontSize" Value="10px"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Background" Value="LightBlue" />
    </Style>
</ResourceDictionary>



<!-- Light.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:System="clr-namespace:System;assembly=mscorlib">

    <System:String x:Key="ThemeName">Light</System:String>
    <SolidColorBrush x:Key="win_background" Color="LightBlue"></SolidColorBrush>

    <Style TargetType="Button">
        <Setter Property="FontFamily" Value="Trebuchet MS"/>
        <Setter Property="FontSize" Value="10px"/>
        <Setter Property="FontWeight" Value="Normal"/>
        <Setter Property="Background" Value="White" />
    </Style>
</ResourceDictionary>

Puis la déclaration dans le point d’entrée de l’application du thème par défaut :

<Application x:Class="WPFThemes.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Themes\Default.xaml"></ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Maintenant que tout cela est fait, on va implémenter la méthode qui va nous permettre de changer de thème dynamiquement dans le point d’entrée de notre application :

public partial class App : Application
{
    private ResourceDictionary obj;

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        foreach (ResourceDictionary item in this.Resources.MergedDictionaries)
        {
            if (item.Source != null && item.Source.OriginalString.Contains(@"Themes\"))
            {
                obj = item;
            }
        }
    }

    public void ChangeTheme(Uri dictionnaryUri)
    {
        if (String.IsNullOrEmpty(dictionnaryUri.OriginalString) == false)
        {
            ResourceDictionary objNewLanguageDictionary = 
                                   (ResourceDictionary)(LoadComponent(dictionnaryUri));

            if (objNewLanguageDictionary != null)
            {
                this.Resources.MergedDictionaries.Remove(obj);
                this.Resources.MergedDictionaries.Add(objNewLanguageDictionary);
            }
        }
    }
}

Il faut savoir que quand on utilise beaucoup de ResourceDictionary, il peut se produire un impact négatif sur les performances : ce sera mon prochain article :).

Maintenant pour pouvoir lié notre interface avec les styles :

<Window x:Class="WPFThemes.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Background="{DynamicResource ResourceKey=win_background}">
    <Grid>
        <Button Content="Changer le thème" Height="51" HorizontalAlignment="Center" 
          Name="btnChangeTheme" VerticalAlignment="Center" Width="110" 
          Click="btnChangeTheme_Click" />
    </Grid>
</Window>

Pour télécharger les sources, ici.

A bientôt !

[WPF] Traduisez vos applications en plusieurs langues (sans LocBaml)

Le 5 juillet 2010 à 10:09

Il y a quelques mois, j’avais mis en ligne une façon de traduire ses applications en plusieurs langues, grâce à LocBaml. Nous allons voir maintenant comment traduire une application WPF mais sans cet outil.
La mise en place est très simple et il a l’avantage, par rapport à LocBaml, qu’on puisse traduire l’application « à la volée » sans relancer celle-ci. Par contre, il faut être plus rigoureux : là où LocBaml nous génère les clefs (x:Uid) des contenus de nos différents contrôles, ici il faut bien prendre soin de le faire soi-même.

Nous allons créer un dictionnaire de ressource pour la langue français et un autre pour la langue anglaise :

 

01
Pour plus de commodité, nous allons nommer les dictionnaires de ressources avec le code pays :

02


Dans ces fichiers, j’ajoute quelques balises afin de contenir les phrases dans la langue adéquate :

<!-- Fichier en-US.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <system:String x:Key="Culture">en-US</system:String>

    <system:String x:Key="Window">Window</system:String>
    <system:String x:Key="cmdExit">Exit</system:String>
    <system:String x:Key="lblWelcome">Hello</system:String>
    <system:String x:Key="World">World !</system:String>
    <system:String x:Key="lblChangeLangage">Change language</system:String>

</ResourceDictionary>


<!-- Fichier fr-FR.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <system:String x:Key="Culture">fr-FR</system:String>

    <system:String x:Key="Window">Fenêtre</system:String>
    <system:String x:Key="cmdExit">Quitter</system:String>
    <system:String x:Key="lblWelcome">Bonjour</system:String>
    <system:String x:Key="World">Tout le monde !</system:String>
    <system:String x:Key="lblChangeLangage">Changer de langue</system:String>

</ResourceDictionary>

Puis on va indiquer le ResourceDirectionary de la langue par défaut dans le point d’entrée de notre application (App.xaml), c’est cette ressource qui sera chargée au démarrage de l’application (bien sûr, ici ce n’est que pour exemple, il y aurait moyen de faire mieux dans le contexte où l’on retient le choix de la langue de l’utilisateur, qui peut être différent que le français :)) :

<Application x:Class="MultilangueSansLocBaml.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources\fr-FR.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Maintenant je vais ajouté une méthode qui permettra de changer la ressource de langue utilisée dans l’application :

 

Ici, nous allons chercher la ressource chargée, puis la supprimer afin d’ajouter la nouvelle ressource, puis changer la culture en cours. En effet, nous avons mis dans le XAML une ressource (fr-FR.xaml) chargée par défaut, il faut donc s’en “débarrassé” afin de mettre une autre ressource.
Pour savoir quelle est la ressource chargée, on va au démarrage de l’application récupérer le ResourceDictionary :

public void ChangeLangage(Uri dictionnaryUri)
{
    if (String.IsNullOrEmpty(dictionnaryUri.OriginalString) == false)
    {
        ResourceDictionary objNewLanguageDictionary = 
                   (ResourceDictionary)(LoadComponent(dictionnaryUri));

        if (objNewLanguageDictionary != null)
        {
            this.Resources.MergedDictionaries.Remove(obj);
            this.Resources.MergedDictionaries.Add(objNewLanguageDictionary);

            CultureInfo culture =
               new CultureInfo((string)Application.Current.Resources["Culture"]);
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
        }
    }
}

Maintenant passons à l’interface de notre application, pour exemple, nous allons utiliser cette fenêtre :

03

Au clique sur “Changer de langue”, l’interface se mettra automatiquement à jour avec la ressource anglaise.

private ResourceDictionary obj;

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    foreach (ResourceDictionary item in this.Resources.MergedDictionaries)
    {
        if (item.Source != null && item.Source.OriginalString.Contains(@"Resources\"))
        {
            obj = item;
        }
    }
}

Et le code pour changer la langue quand on clique sur le bouton :

<Window x:Class="MultilangueSansLocBaml.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="{DynamicResource Window}" Height="138" Width="203">
    <Grid>
        <StackPanel>
            <Button Content="{DynamicResource lblWelcome}" Click="Button_Click_1" />
            <Button Content="{DynamicResource lblChangeLangage}" Click="Button_Click" />
        </StackPanel>
    </Grid>
</Window>

Et pour charger un élément d’une ressource en C# :

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    MessageBox.Show((string) Application.Current.Resources["World"]);
}

04     05

Bien sûr, ceci n’est qu’une ébauche. On peut faire quelque chose de beaucoup plus complet, mais l’essentiel est là :).

Les sources du projet peuvent être téléchargés ici.

A bientôt !

A propos de l'auteur

Mathieu Perrein - Software Solutions Manager, Software Architect, Trainer MCT, MSP de 2010 à 2012.

 

MSP

 

MSP

MSP

 MSPD

MCT

 

Facebook

 

Ce blog est strictement personnel et les opinions exprimées ici n'engagent donc que moi, et pas mon employeur.

Tags

Vous avez désactivé JavaScript ou bien vous possédez une ancienne version d'Adobe Flash Player. Téléchargez la dernière version de Flash Player.