WPF : Comment faire rapidement un user contrôle réutilisable

 

Auteur :Hassan KANDIL

Date de la première publication  :12/04/2010

Revue et adaptée le : 02/07/2017

Gestion de pages virtuelles

WPF: Dispatcher les messages

WPF: Dynamic Binding avec ADO.net

 

 

Pour réaliser un contrôle utilisateur en WPF, Les méthodes sont multiples et efficaces. A commencer par le style et les templates du zammel jusqu’à l’écriture en VB, C++ ou C# d’un code  qui gère l’affichage de l’objet et son comportement.

Bien sûr nous devons garder en tête avant de commencer l’écriture de l’objet,  qu’il y a toujours un objet (ou plusieurs) qui peuvent servir comme ancêtres de notre objet et donc, il vaut mieux se baser sur l’existant pour générer un nouvel objet.

L’exemple que j'ai choisi d’exposer ici, montre un objet d’une utilité moyenne mais avec quelques modifications des images il pourrait s’avérer très utile.

Cet exemple tentera de réaliser un bouton rond avec des aspects « bitmapés ». Il ne prétend pas être le plus ergonomique, mais il pourra être un exemple d’une réalisation très facile.

 

La première question qui se pose est sur l’utilisation des ressources dans un contrôle utilisateur. Il faut que les ressources soient incorporées, or WPF incorpore automatiquement les ressources, à moins que l’on veuille les inclure dans un dictionnaire de ressources, et dans ce cas, il faut assurer l’ajout du dictionnaire au projet (merging), ce qui me semble non adapté à la situation, surtout qu’un objet de cette nature ne doit pas traîner beaucoup de ressources, pour des raisons d’efficacité

 

Bouton Rond.

 

Notre Contrôle utilisateur est un bouton rond cliquable . Il a quatre aspects différents :

 

En état de repos

Pendant le click

Nom è utiliser dans la clé

 bleu

elastique

rouge

vrai

 

.

 

ce qui représente 8 fichiers d’images différentes,

Le traitement regroupe les images  2 par 2 en utilisant un préfixe suivi d’une description identique Origine (repos) – Destination(cliqué) . Ceci est utile dans la constitution de la clé de l’image dans le fichier XAML.

Comme on a dit, les objets graphiques seront inclus dans le contrôle utilisateur D’où  la définition du <UserContol> qui contiendra cette partie de ressources.

    <UserControl.Resources>

        <ImageBrush x:Key="rougeOrigine" ImageSource ="image1/rougeOrigine.bmp"/>

        <ImageBrush x:Key="rougeDestination" ImageSource ="image1/rougeDest.bmp"/>

        <ImageBrush x:Key="bleuOrigine" ImageSource ="image1/bleuOrigine.bmp"/>

        <ImageBrush x:Key="bleuDestination" ImageSource ="image1/bleuDest.bmp"/>

        <ImageBrush x:Key="vraiOrigine" ImageSource ="image1/vraiOrigine.bmp"/>

        <ImageBrush x:Key="vraiDestination" ImageSource ="image1/vraiDest.bmp"/>

        <ImageBrush x:Key="elastiqueOrigine" ImageSource ="image1/elastiqueOrigine.bmp"/>

        <ImageBrush x:Key="elastiqueDestination" ImageSource ="image1/elastiqueDest.bmp"/>

 

Ceci suppose que les huit fichiers sont placés dans un dossier Images au sein du projet WPF.

Les noms des fichiers importent peu, seuls les clés doivent être structurées d’une manière à pouvoir les reconstituer facilement à partir de la couleur. L’idée est de considérer la clé comme étant couleur+chaîne constante (Origine ou Destination)

Le bouton devrait contenir une template qui permettra de le dessiner et de définir la partie qui nous intéresse de son comportement : Changement d’aspect (image) quand il est cliqué.

Commençons déjà par la création du template et donnons un nom Clé à cette création et bien sûr un target type pour l’appliquer à notre futur objet

        <ControlTemplate x:Key="BoutonRondTemplate" TargetType="Button">

Dessinons un aspect rond

                <Ellipse x:Name="BoutonEllipse">

Nous ne mettons pas de Height ni Width dans l’ellipse, mais nous savons que notre bouton devrait avoir la même valeur pour ces deux propriétés. Nous pouvons lier la taille de l’ellipse à la taille de l”objet en ajoutant

Height="{Binding ElementName=BRUser,Path=Height}" Width="{Binding ElementName=BRUser,Path=Width}">.

BRUser étant le nom du bouton que l’on utilisera dans le UserControl (ce nom sera interne à l’objet).

 

Pour le moment remplissons notre Ellipse(circulaire) par l’image au repos, laquelle est une variable objet que l’on nommera FileOrigine. Cet objet fera partie du code behind et assurera la transmission de l’image appropriée.

 

                    <Ellipse.Fill>

                        <ImageBrush ImageSource="{Binding FileOrigine,RelativeSource={RelativeSource AncestorType=UserControl}, Mode=OneWay}"/>

                    </Ellipse.Fill>

                </Ellipse>

 

L’objectif de la RelativeSource={RelativeSource AncestorType=UserControl est tout simplement de signaler l’élément origine de la data c’est à dire notre UserControl. Ceci nous évitera de définir le DataContext plus tard pour pouvoir utiliser notre objet.

 

Un event nous intéresse particulièrement :la souris cliquée. On peut aussi s’intéresser à d’autres events (le mouse over par exemple).

2 propriétés : IsPressed (le bouton est cliqué ou validé par le clavier)  et IsMouseOver (la souris survole l’objet).

La valeur « True « de chacune de ces propriétés doit déclencher des actions qui seront accomplies à travers des triggers et des setter property-value. Le trigger indique l’event, le setter avec TargetName indique la propriété et sa valeur. Ceci se fait de la manière suivante.

            <ControlTemplate.Triggers>

                <Trigger Property="IsPressed" Value="True">

                    <Setter TargetName="BoutonEllipse" Property="Fill" >

                        <Setter.Value>

                            <ImageBrush ImageSource="{Binding FileDestination,RelativeSource={RelativeSource AncestorType=UserControl}, Mode=OneWay}"/>

                        </Setter.Value>

                    </Setter>

                </Trigger>

 

Pour le mouse over nous allons nous contenter de jouer sur la couleur du stroke(le bord de l’ellipse) en le rendant rouge. Rien n’empêche d’ajouter une troisième image pour cette situation ou de faire des transformations (réduction/agrandissement ou autres).

 

                <Trigger Property="IsMouseOver" Value="True">

                    <Setter TargetName="BoutonEllipse" Property="Stroke" Value="red"/>

                </Trigger>

 

            </ControlTemplate.Triggers>

 

Les deux événements sont traités, nous devons fermer les balises

        </ControlTemplate>

    </UserControl.Resources>

 

Pour définir l’objet du Template on va utiliser le layout StackPanel et ne pas oublier la taille à donner au bouton qui sera le même attribué à l’ellipse.

 

    <StackPanel>

        <Button x:Name="BRUser" Height="50" Width="50" Template="{StaticResource BoutonRondTemplate}" Click="Button_Click" Margin="0,0,0,0" />

    </StackPanel>

 

Le click est l’event qui sera traité dans notre programme qui exploitera plus tard le bouton rond.

 

Note : En fait rien ne nous empêche de le faire elliptique en donnant à Width une valeur différente de Height.

 

Le bouton est dessiné Le fichier XAML est le suivant:

 

<UserControl x:Class="AsysButton.BoutonRond"

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

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

             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

             xmlns:local="clr-namespace:AsysButton"

             mc:Ignorable="d"

             d:DesignHeight="100" d:DesignWidth="100">

    <UserControl.Resources>

        <ImageBrush x:Key="rougeOrigine" ImageSource ="image1/rougeOrigine.bmp"/>

        <ImageBrush x:Key="rougeDestination" ImageSource ="image1/rougeDest.bmp"/>

        <ImageBrush x:Key="bleuOrigine" ImageSource ="image1/bleuOrigine.bmp"/>

        <ImageBrush x:Key="bleuDestination" ImageSource ="image1/bleuDest.bmp"/>

        <ImageBrush x:Key="vraiOrigine" ImageSource ="image1/vraiOrigine.bmp"/>

        <ImageBrush x:Key="vraiDestination" ImageSource ="image1/vraiDest.bmp"/>

        <ImageBrush x:Key="elastiqueOrigine" ImageSource ="image1/elastiqueOrigine.bmp"/>

        <ImageBrush x:Key="elastiqueDestination" ImageSource ="image1/elastiqueDest.bmp"/>

 

        <ControlTemplate x:Key="BoutonRondTemplate" TargetType="Button">

 

            <Grid >

                <Ellipse x:Name="BoutonEllipse">

                    <Ellipse.Fill>

                        <ImageBrush ImageSource="{Binding FileOrigine,RelativeSource={RelativeSource AncestorType=UserControl}, Mode=OneWay}"/>

                    </Ellipse.Fill>

                </Ellipse>

            </Grid>

            <ControlTemplate.Triggers>

                <Trigger Property="IsPressed" Value="True">

                    <Setter TargetName="BoutonEllipse" Property="Fill" >

                        <Setter.Value>

                            <ImageBrush ImageSource="{Binding FileDestination,RelativeSource={RelativeSource AncestorType=UserControl}, Mode=OneWay}"/>

                        </Setter.Value>

                    </Setter>

                </Trigger>

                <Trigger Property="IsMouseOver" Value="True">

                    <Setter TargetName=" BoutonEllipse" Property="Stroke" Value="red"/>

                </Trigger>

            </ControlTemplate.Triggers>

        </ControlTemplate>

    </UserControl.Resources>

    <StackPanel>

        <Button x:Name="BRUser" Height="50" Width="50" Template="{StaticResource BoutonRondTemplate}" Click="Button_Click" Margin="0,0,0,0" />

    </StackPanel>

</UserControl>

 

 

La gestion des Méthodes permettant cette liaison créée avec FileOrigine et FileDestination doit être assurée dans le code behind.  L’entête de ce fichier contiendra les using suivants (importations) :

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Controls;

using System.ComponentModel;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;    

 

Remarquons que ces deux éléments doivent retourner des ImageSource … Si elles n’étaient pas capables de l’assurer, on aurait fait appel à un converter qui transformera la ressource en imageSource ou BitmapImage qui sera retournée au Binding. Ceci aurait exigé d’ajouter dans le binding l’élément Converter={staticResource nomdéfinidanslesUserResources}.

Il y aura lieu dans ce cas à ajouter une classe dérivée de IValueConverter avec les deux méthodes Convert et ConvertBack.

Le code Behind doit contenir tout simplement Une dependency property (ce n’est pas la seule façon de faire une INotifyPropertyChanged aurait réalisé la tâche).

Mais Le changement de l’aspect du bouton sera moins élégant que de le faire à travers une dependency property qui peut figurer directement dans le XAML de la fenêtre owner du usercontrol.

La définition des deux attributs qui contiendront la ressource est le premier pas à réaliser dans le code behind

        #region Définitions

        private ImageSource fileOrigine;

        private ImageSource fileDestination;

        #endregion

 

Il faut aussi définir une propriété nommée Couleur (Aspect aurait mieux correspondu à notre cas) qui reçoit les valeurs : bleu/rouge/elastique/vrai et qui définit l’aspect du bouton.

 

Une approche intuitive nous pousse à définir les valeurs possibles (gérées) de la propriété Couleur. Pour ce faire nous utilisons une list<string> qui contiendra toutes les valeurs autorisées. Dans notre cas, quatre valeurs (bleu/rouge/elastique/vrai) sont à stocker dans la liste. Cette liste est unique, elle ne doit pas être régénérée à chaque instanciation du contrôle. C’est pour cette raison qu'elle est définie en static. En plus elle pourrait être exploitée sans instanciation.

 

        public static List<string> Couleurs = new List<string>();

 

Pour remplir cette liste, on pourrait procéder de plusieurs façons, une des plus simples est de le faire dans le constructeur et de s’assurer que cette opération n’est pas reproduite à chaque instanciation.

 

        #region Constructer

        public BoutonRond()

        {

            if (Couleurs.Count() == 0)

           {

//dans un ordre croissant pour utiliser System.Array.BinarySearch(tab, Couleur)

                Couleurs.Add("bleu");

                Couleurs.Add("elastique");

                Couleurs.Add("rouge");

                Couleurs.Add("vrai");

            }

            InitializeComponent();

        }

        #endregion

 

Les éléments du DataBinding sont au nombre de deux FileOrigine et FileDestination

Ces deux éléments doivent tout simplement reconnaître le choix fait par la propriété dépendante Couleur et associer le fichier Bitmap correspondant.

 

Pour cela on peut utiliser indifféremment Ressources(qui pourrait être utilisée aussi avec un dictionnaire de resources) et FindRessource (pareil).

L’approche dans ce cas utilisera Application.Current.FindResource (ou resources[]) et procédera à l’import des ressources soit dans l’application soit dans un dictionnaire partagé.

 

Or nos ressources se trouvent dans le usercontrol XAML, donc on y accède avec this qui représente dans le code behind le usercontrole

Ces deux méthodes d’accès (resources[] et FindResource() ont besoin de la clé de la ressource recherchée).

 

Cette clé est Constituée de la Couleur+le mot Origine ou le mot Destination (d’après notre vision algorithmique.)

La construction de l’image peut se faire avec le set ou au moment du get ou même à un autre moment (au moment du changement de la valeur de Couleur)

 

        #region DataBinding Elments

        public ImageSource FileOrigine

        {

            get

            {

               

                fileOrigine = ((ImageBrush)this.FindResource(Couleur + "Origine")).ImageSource;

                return fileOrigine;

            }

            set

            {

                fileOrigine = value;

                NotifyPropertyChange("FileOrigine");

            }

        }

        public ImageSource FileDestination

        {

            get

            {

                fileDestination = ((ImageBrush)this.Resources[Couleur + "Destination"]).ImageSource;

                return fileDestination;

            }

            set

            {

                fileDestination = value;

                NotifyPropertyChange("FileDestination");

            }

        }

        #endregion       

 

L’event Click que nous avons saisi dans le BRUser de notre contrôle doit être confié à l’Owner du contrôle. Ainsi nous aurons à définir une publication de cet event.

 

        #region Traitement de  l'event

        public event EventHandler OnClick;

        private void ThatWillRaisesEvent()

        {

            OnClick?.Invoke(this, new EventArgs());

        }

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            ThatWillRaisesEvent();

        }

        #endregion

 

La définition de la dependency property est la plus standard qu’elle soit à l’exception de deux lignes qui font le lien entre le changement de la propriété couleur et le changement des FileOrigine et FIleDestination, en plus cette propriété doit avoir des valeurs de type string (bleu/rouge/elastique/vrai). La valeur vrai dans la metadata est la valeur par défaut.

On peut faire un setvalue pour associer l’image à la méthode, ou bien déclarer tout simplement une notification de changement de propriété ou bien évidemment Terminer le travail dans le set de Couleur.

 

System.Array.BinarySearch(tab, Couleur) permet d’éviter l’utilisation des valeurs hors périmètre. Ce qui explique le manque de gestion des exceptions dans le programme. 

Bien sûr il faut ajouter de try .. catch ... mais ce n’est pas le propos de l’actuelle démonstration.

 

        #region DependencyProperty Couleur qui indique l'image

        public static readonly DependencyProperty CouleurProperty = DependencyProperty.Register("Couleur", typeof(string), typeof(BoutonRond), new PropertyMetadata("vrai", new PropertyChangedCallback(OnCouleurChanged)));

 

        public string Couleur

        {

            get

            { return (string)GetValue(CouleurProperty); }

            set

            {

               string[] tab= Couleurs.ToArray();

               if (System.Array.BinarySearch(tab, Couleur)>=0)

{

                SetValue(CouleurProperty, value);

                       FileOrigine = ((ImageBrush)this.Resources[Couleur + "Origine"]).ImageSource;

                       FileDestination = ((ImageBrush)this.Resources[Couleur + "Destination"]).ImageSource;

}

            }

        }

        private static void OnCouleurChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)

        {

            BoutonRond bRControl = depObj as BoutonRond;

            bRControl.OnCouleurChanged(e);

        }

        private void OnCouleurChanged(DependencyPropertyChangedEventArgs e)

        {

                if(e.NewValue!=null) Couleur = e.NewValue.ToString();

        }

        #endregion

 

Le binding doit toujours s’accompagner par une notification de changement de propriété classique

 

        #region NotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChange(String property)

        {

            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

        }

        #endregion

 

Nous avons remarqué le fait que notre UserControle avait un width et un height prédéfinis. Or ces données doivent être logiquement définies comme dependency property pour les manipuler directement en XAML au moment de la creation et même à travers des triggers avec setter pour avoir de l’animation. Nous nous contentons de les rendre souples d’utilisation sans aller plus loin, en ajoutant deux Méthodes qui (seulement en programmation) permettent de changer les valeurs de taille.

 

        #region Méthodes Extra

        public void SetWidth(double dWidth)

        {

            this.BRUser.Width = dWidth;

            this.Width = this.BRUser.Width;

        }

        public void SetHeight(double dHeight)

        {

            this.BRUser.Height = dHeight;

            this.Height = this.BRUser.Height;

        }

        #endregion

remarquez que l’on a utilisé dans nos méthodes le nom interne du bouton défini dans le UserControl XAML.

 

Testons le bouton rond

 

Voici une fenêtre de test que l’on peut directement utiliser dans notre programme qui contient un contrôle utilisateur et une fenêtre principale

 

<Window

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

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        xmlns:local="clr-namespace:AsysButton "

        mc:Ignorable="d"

        Title="MainWindow" Height="350" Width="525">

    <Grid>

        <local:BoutonRond x:Name="RB" Couleur = "{Binding SelectedItem,ElementName=comboCouleursRB,Mode=TwoWay}" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Margin="180,45,0,0" Click=”BR_OnClick”/>

        <ComboBox x:Name="comboCouleursRB" SelectedItem="{Binding Path=Couleur, Mode=TwoWay}" IsReadOnly="True" HorizontalAlignment="Left" Height="30" Margin="210,230,0,0" VerticalAlignment="Top" Width="170"/>

        <TextBox Name="tbRB" HorizontalAlignment="Left" Height="25" Margin="30,185,0,0" TextWrapping="Wrap" Text="{Binding Path=Couleur, Mode=TwoWay}" VerticalAlignment="Top" Width="110"/>

        <Button Content="Rouge" HorizontalAlignment="Left" Height="30" Margin="30,230,0,0" VerticalAlignment="Top" Width="110" Click="Button_Click"/>

    </Grid>

</Window>

 

Code behind

 

namespace AsysButton

{

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

            this.DataContext = new BoutonRond();

//remplir la combobox par les valeurs autorisées

            BoutonRond.Couleurs.ForEach(item => { comboCouleursRB.Items.Add(item); });

        }

        private void BR_OnClick(object sender, RoutedEventArgs e)

{

  // code exploitant le bouton rond s’il est cliqué

}

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

//changer le bouton utilize en rouge

            RB.SetCurrentValue(BoutonRond.CouleurProperty,"rouge");

        }

    }

}

 

Ainsi dans cet exemple on verra facilement les divers aspects du bouton soit avec la combobox, soit en cliquant sur le bouton Rouge.

 

 

Pour transformer l’ensemble en UserControle réutilisable, il suffit de supprimer la fenêtre MainWindow et de définir le projet comme étant une DLL. D’un projet WPF classique on passe à un projet DLL.

Pour cela cliquer sur le nom du projet et accéder à ses propriétés et modifier les infos du type de sortie – Choisissez Bibliothèque de classes.

 

 

 

 

 

Le résultat après régénération du projet sera une DLL que vous pouvez ajouter à d’autres projets .

Ajouter dans tout autre projet une référence et parcourir, choisissez la DLL  que vous venez de faire. Si l’élément BoutonRond n’apparaît pas dans vos outils, ajoutez le en choisissant « Eléments choisis » et encore une fois parcourir et choisir la DLL. L’élément apparaîtra et vous pouvez le placer dans votre fenêtre .

Par défaut la Bitmap vrai serait choisie, pour modifier ce choix il suffit d’ajouter dans le XAML la propriété Couleur= »bleu » (attention  c’est case sensitive à moins que vous ajoutiez un toupper or tolower dans votre code du userControl).

Pour la taille comme dit plus haut il faut le gérer programaticallement avec les deux méthodes SetWidth et SetHeight. Ceci pourrait être fait dans le constructeur de la fenêtre propriétaire du bouton en ajoutant 2 lignes,

NomDuBoutonRond.SetWidth(valeur) ;

NomDuBoutonRond.SetHeight(valeur) ;

 

Espérons avoir fait le tour de la question même si je pense honnêtement que la programmation en général est un vaste domaine et .net en particulier constitue un sous ensemble démonstratif.

 

 

 

 

 

 

BoutonRond.xaml

<UserControl x:Class="AsysButton.BoutonRond"

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

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

             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

             xmlns:local="clr-namespace:AsysButton"

             mc:Ignorable="d"

             d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.Resources>

        <ImageBrush x:Key="rougeOrigine" ImageSource ="image1/rougeOrigine.bmp"/>

        <ImageBrush x:Key="rougeDestination" ImageSource ="image1/rougeDest.bmp"/>

        <ImageBrush x:Key="bleuOrigine" ImageSource ="image1/bleuOrigine.bmp"/>

        <ImageBrush x:Key="bleuDestination" ImageSource ="image1/bleuDest.bmp"/>

        <ImageBrush x:Key="vraiOrigine" ImageSource ="image1/vraiOrigine.bmp"/>

        <ImageBrush x:Key="vraiDestination" ImageSource ="image1/vraiDest.bmp"/>

        <ImageBrush x:Key="elastiqueOrigine" ImageSource ="image1/elastiqueOrigine.bmp"/>

        <ImageBrush x:Key="elastiqueDestination" ImageSource ="image1/elastiqueDest.bmp"/>

 

        <ControlTemplate x:Key="BoutonRondTemplate" TargetType="Button">

 

            <Grid >

                <Ellipse x:Name="BoutonEllipse">

                    <Ellipse.Fill>

                        <ImageBrush ImageSource="{Binding FileOrigine,RelativeSource={RelativeSource AncestorType=UserControl}, Mode=OneWay}"/>

                    </Ellipse.Fill>

                </Ellipse>

            </Grid>

            <ControlTemplate.Triggers>

                <Trigger Property="IsPressed" Value="True">

                    <Setter TargetName="BoutonEllipse" Property="Fill" >

                        <Setter.Value>

                            <ImageBrush ImageSource="{Binding FileDestination,RelativeSource={RelativeSource AncestorType=UserControl}, Mode=OneWay}"/>

                        </Setter.Value>

                    </Setter>

                </Trigger>

                <Trigger Property="IsMouseOver" Value="True">

                    <Setter TargetName="BoutonEllipse" Property="Stroke" Value="red"/>

                </Trigger>

            </ControlTemplate.Triggers>

        </ControlTemplate>

    </UserControl.Resources>

    <StackPanel>

        <Button x:Name="BRUser" Height="50" Width="50" Template="{StaticResource BoutonRondTemplate}" Click="Button_Click" Margin="0,0,0,0" />

    </StackPanel>

</UserControl>

 

Code behind

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Controls;

using System.ComponentModel;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

 

 

 

namespace AsysButton

{

    /// <summary>

    /// Logique d'interaction pour BoutonRond.xaml

    /// </summary>

    public partial class BoutonRond : UserControl,INotifyPropertyChanged

    {

        #region Définitions

        private ImageSource fileOrigine;

        private ImageSource fileDestination;

        #endregion

        public static List<string> Couleurs = new List<string>();

 

        #region Constructer

        public BoutonRond()

        {

            if (Couleurs.Count() == 0)

            {

                //dans un ordre croissant pour utiliser System.Array.BinarySearch(tab, Couleur)

                Couleurs.Add("bleu");

                Couleurs.Add("elastique");

                Couleurs.Add("rouge");

                Couleurs.Add("vrai");

            }

            InitializeComponent();

        }

        #endregion

        #region DataBinding Elments

        public ImageSource FileOrigine

        {

            get

            {

 

                fileOrigine = ((ImageBrush)this.FindResource(Couleur + "Origine")).ImageSource;

                return fileOrigine;

            }

            set

            {

                fileOrigine = value;

                NotifyPropertyChange("FileOrigine");

            }

        }

        public ImageSource FileDestination

        {

            get

            {

                fileDestination = ((ImageBrush)this.Resources[Couleur + "Destination"]).ImageSource;

                return fileDestination;

            }

            set

            {

                fileDestination = value;

                NotifyPropertyChange("FileDestination");

            }

        }

        #endregion

        #region Traitement de  l'event

        public event EventHandler OnClick;

        private void ThatWillRaisesEvent()

        {

            OnClick?.Invoke(this, new EventArgs());

        }

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            ThatWillRaisesEvent();

        }

        #endregion

        #region DependencyProperty Couleur qui indique l'image

        public static readonly DependencyProperty CouleurProperty = DependencyProperty.Register("Couleur", typeof(string), typeof(BoutonRond), new PropertyMetadata("vrai", new PropertyChangedCallback(OnCouleurChanged)));

 

        public string Couleur

        {

            get

            { return (string)GetValue(CouleurProperty); }

            set

            {

                string[] tab = Couleurs.ToArray();

                if (System.Array.BinarySearch(tab, Couleur) >= 0)

                {

                    SetValue(CouleurProperty, value);

                    FileOrigine = ((ImageBrush)this.Resources[Couleur + "Origine"]).ImageSource;

                    FileDestination = ((ImageBrush)this.Resources[Couleur + "Destination"]).ImageSource;

                }

            }

        }

        private static void OnCouleurChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)

        {

            BoutonRond bRControl = depObj as BoutonRond;

            bRControl.OnCouleurChanged(e);

        }

        private void OnCouleurChanged(DependencyPropertyChangedEventArgs e)

        {

            if (e.NewValue != null) Couleur = e.NewValue.ToString();

        }

        #endregion

        #region NotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChange(String property)

        {

            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

        }

        #endregion

        #region Méthodes Extra

        public void SetWidth(double dWidth)

        {

            this.BRUser.Width = dWidth;

            this.Width = this.BRUser.Width;

        }

        public void SetHeight(double dHeight)

        {

            this.BRUser.Height = dHeight;

            this.Height = this.BRUser.Height;

        }

        #endregion

    }

}

 

MainWindow.xaml

<Window x:Class="AsysButton.MainWindow"

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

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        xmlns:local="clr-namespace:AsysButton"

        mc:Ignorable="d"

        Title="MainWindow" Height="350" Width="525">

    <Grid>

        <local:BoutonRond x:Name="RB" Couleur = "{Binding SelectedItem,ElementName=comboCouleursRB,Mode=TwoWay}" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Margin="180,45,0,0" OnClick="RB_OnClick"/>

        <ComboBox x:Name="comboCouleursRB" SelectedItem="{Binding Path=Couleur, Mode=TwoWay}" IsReadOnly="True" HorizontalAlignment="Left" Height="30" Margin="210,230,0,0" VerticalAlignment="Top" Width="170"/>

        <TextBox Name="tbRB" HorizontalAlignment="Left" Height="25" Margin="30,185,0,0" TextWrapping="Wrap" Text="{Binding Path=Couleur, Mode=TwoWay}" VerticalAlignment="Top" Width="110"/>

        <Button Content="Button" HorizontalAlignment="Left" Height="30" Margin="30,230,0,0" VerticalAlignment="Top" Width="110" Click="Button_Click"/>

    </Grid>

</Window>

 

 

MainWindow Code behind

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

 

namespace AsysButton

{

    /// <summary>

    /// Logique d'interaction pour MainWindow.xaml

    /// </summary>

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

            this.DataContext = new BoutonRond();

            //remplir la combobox par les valeurs autorisées

            BoutonRond.Couleurs.ForEach(item => { comboCouleursRB.Items.Add(item); });

        }

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            //changer le bouton utilize en rouge

            RB.SetCurrentValue(BoutonRond.CouleurProperty, "rouge");

        }

 

        private void RB_OnClick(object sender, EventArgs e)

        {

 

        }

    }

}

 

 

 

 

 

Choisir les éléments

 

 

Quick User Control:Bouton rond - format PDF

Site à visiter: C# - .net

 




 



Asystech est une société de droit français qui a son siège social à Paris - France
 
 
© Copyright 2005, Secoris