Dans mon billet précédent,je rappelais un peu comment avec Silverlight on pouvait créer un bouton (Texte et Image) avec un effet de MouseOver. Ici, je vais faire juste rappeler comment créer un Contrôle qui sera facilement réutilisable (là aussi, rien de compliqué :-) ).

Pour ce faire, je créé un nouveau UserControl (en incluant son CodeBehind) TopMenuButton.xaml par exemple.

<UserControl
  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"
  mc:Ignorable="d"
  x:Class="MonProjet.TopMenuButton"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
  
  <UserControl.Resources>
    <vsm:Style x:Key="BtnTopMenuStyle" TargetType="Button">
     <vsm:Setter Property="Foreground" Value="#FFFFFF"/>
     <vsm:Setter Property="Cursor" Value="Arrow"/>
     <vsm:Setter Property="TextAlignment" Value="Left"/>
     <vsm:Setter Property="TextWrapping" Value="NoWrap"/>
     <vsm:Setter Property="FontSize" Value="10"/>
     <vsm:Setter Property="Template">
        <vsm:Setter.Value>
          <ControlTemplate TargetType="Button">
            <Grid>
              <Grid.Resources>
              <!-- Grid.Resources Here -->
              </Grid.Resources>
              <vsm:VisualStateManager.VisualStateGroups>
                <vsm:VisualStateGroup x:Name="CommonStates">
                  <vsm:VisualStateGroup.Transitions>
                    <vsm:VisualTransition Duration="0:0:0.3" To="MouseOver"/>
                  </vsm:VisualStateGroup.Transitions>
                  <vsm:VisualState x:Name="Normal"/>
                  <vsm:VisualState x:Name="MouseOver">
                    <Storyboard>
                    <ColorAnimation Duration="0"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
To="Black" />
                    </Storyboard>
                  </vsm:VisualState>
                                      
                </vsm:VisualStateGroup>
                  
              </vsm:VisualStateManager.VisualStateGroups>
              <Rectangle x:Name="Background" RadiusX="4" RadiusY="4" Fill="{TemplateBinding Background}"/>
              <ContentPresenter
                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                Padding="{TemplateBinding Padding}"
                VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                Margin="4,5,4,4"
                Content="{TemplateBinding Content}"
                ContentTemplate="{TemplateBinding ContentTemplate}"
                TextAlignment="{TemplateBinding TextAlignment}"
                TextDecorations="{TemplateBinding TextDecorations}"
                TextWrapping="{TemplateBinding TextWrapping}"/>
            </Grid>
          </ControlTemplate>
        </vsm:Setter.Value>
      </vsm:Setter>
    </vsm:Style>
</UserControl.Resources>

<Button Background="Transparent"
x:Name="btnTopMenu"
Style="{StaticResource BtnTopMenuStyle}"
Loaded="btnTopMenu_Loaded">
   <StackPanel Orientation="Horizontal">
     <Image x:Name="imgTopMenu" Margin="0, 0, 5, 0" />
     <TextBlock x:Name="tblTopMenu" VerticalAlignment="Center" Margin="0" />
   </StackPanel>
</Button>

</UserControl>



Le bouton est rendu ici (par rapport au billet précédent) beaucoup plus générique, j'ai aussi rajouté l'évènement Loaded (j'en parlerai plus bas), et j'identifie chacun des éléments enfants de mon bouton.

Je vais créer 2 propriétés maintenant pour mon bouton. Une relative à l'Url de mon image (ImageSource), et l'autre relative au texte que je veux afficher pour le bouton (MenuText).


public String ImageSource
{
   get { return (String)GetValue(ImageSourceProperty); }
   set { SetValue(ImageSourceProperty, value); }
}

public String MenuText
{
   get { return (String)GetValue(MenuTextProperty); }
   set { SetValue(MenuTextProperty, value); }
}

public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
"ImageSource", typeof(String), typeof(TopMenuButton), new PropertyMetadata(OnImageSourceChanged));

public static readonly DependencyProperty MenuTextProperty = DependencyProperty.Register(
"MenuText", typeof(String), typeof(TopMenuButton), new PropertyMetadata(OnMenuTextChanged));


private static void OnImageSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
   if (e.NewValue != null)
   {
     // Your code here
   }
}

private static void OnMenuTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
   if (e.NewValue != null)
   {
     // Your code here
   }
}


L'utilisation des propriétés simples / DependencyProperty, y compris la création de propriétés, Thomas en parlait dans un de ses billets, il y a déjà bien longtemps. Nous n'y reviendrons donc pas :-).

Bien, notre gestionnaire d'évènement Loaded. Quand nos propriétés seront initialisées sur notre contrôle réutilisable, il faudra les initialiser aussi sur les contrôles enfants (le TextBlock et l'Image). C'est à dire que quand je vais mettre à jour la propriété ImageSource de mon TopMenuButton, il faudra mettre à jour la propriété Source de mon Image et etc... (Vous l'aviez déjà compris :-) ).

C'est lors de l'évènement Loaded qu'il faut donc le faire (et pas dans le constructeur de notre contrôle, car ce dernier doit d'abord être initialisé avant de pouvoir accéder aux propriétés des contrôles enfants qui le compose).

private void btnTopMenu_Loaded(object sender, RoutedEventArgs e)
{
   this.imgTopMenu.Source = ResourceHelper.GetBitmap(ImageSource);
   this.tblTopMenu.Text = MenuText;
}


ResourceHelper est une petite classe simple que j'ai trouvé et qui permet de charger à partir d'un chemin (et éventuellement du nom d'une assembly), une ressource (BitmapImage, String, Xaml, ou FontSource, etc...).

Voilà, notre contrôle est prêt. Dans ma page principale (.xaml), je rajoute à l'élement UserControl

xmlns:MonProjet="clr-namespace:MonProjet"

afin de pouvoir référencer puis utiliser mon nouveau contrôle de cette façon :

<MonProjet:TopMenuButton
x:Name="btnAddContent"
        Grid.Column="0"
        MenuText="Menu 01"
        ImageSource="Images/top_menu_01.png" />
      
      <MonProjet:TopMenuButton
x:Name="btnCreatePublicPage"
            Grid.Column="1"
            MenuText="Menu 02"
            ImageSource="Images/top_menu_02.png" />



Fin du rappel :-).

Bonne journée, et A bientôt.