Archive

Archive for février 2011

A quoi peut vraiment servir l’attribut PartNotDiscoverableAttribute ?

La plupart d’entre vous connaissent déjà la réponse. Si vous ne la connaissez pas, cet attribut sert à exclure une Part (extension) de la liste des exports qui seront visibles.

Lorsque j’ai découvert cet attribut, je me suis dit quelle est vraiment son utilité. Si on ne veut pas exporter une Part autant ne pas la décorée de l’attribut Export et le tour est joué.

Pour voir où l’attribut PartNotDiscoverableAttribute joue un rôle important, passons à la pratique.

Supposons que nous ayons les classes EmailSender, SecureEmailSender et SMSSender qui feront office d’extension. Toutes ces extensions implémentent l’interface IMessageSender.

Pour ne pas avoir à décorer chacune des classes citées précédemment avec l’attribut Export le plus simple est de décorer l’interface IMessageSender avec l’attribut InheritedExport et toutes les classes implémentant cet interface seront définies automatiquement comme des extensions.

Ainsi nous aurons le code suivant :


[InheritedExport(typeof(IMessenger))]
public interface IMessenger
{
	void Send(string message);
}

public class EmailSender : IMessenger
{
	#region IMessenger Membres

	public void Send(string message)
	{
		Console.WriteLine("Email sender : {0}", message);
	}

	#endregion
}

public class SecureEmailSender : IMessenger
{

	#region IMessenger Membres

	public void Send(string message)
	{
		Console.WriteLine("Secure Email sender : {0}", message);
	}

	#endregion
}

public class SMSSender : IMessenger
{
	#region IMessenger Membres

	public void Send(string message)
	{
		Console.WriteLine("SMS sender : {0}", message);
	}

	#endregion
}

Jusque là pas de problème. Nos extensions sont utilisées durant plusieurs années et un jour il est décidé que l’extension EmailSender ne doit plus être utilisée et donc ne doit plus être exposée ;).
Plusieurs solutions s’offrent à nous :
– Retirer la classe EmailSender du code
– Ne plus utiliser l’attribut InheritedExport défini sur l’interface IMessageSender, mais plutôt utiliser l’attribut Export sur les différentes extensions devant être exposées et ignorer la classe EmailSender

Pour une petite librairie comme celle de l’exemple pas de problème. S’il s’agit d’une grande librairie et que plusieurs extensions doivent être exlues les solutions citées précédemment peuvent devenir vite fastidieuses et entraînées des erreurs.
Le plus simple est de noter les extensions à exclure. Une fois cela fait, modifier le code en décorant chaque extension à exclure (EmailSender dans notre exemple) par l’attribut PartNotDiscoverableAttribute. Avec cette méthode le risque d’erreurs est réduit.

[PartNotDiscoverable()]
public class EmailSender : IMessenger
{
	#region IMessenger Membres

	public void Send(string message)
	{
		Console.WriteLine("Email sender : {0}", message);
	}

	#endregion
}

Nous voyons que l’attribut PartNotDiscoverable est d’une grande utilité contrairement à ce que je pensais au début :D.

Publicités
Catégories :Silverlight, WPF Étiquettes : , , , ,

Comment synchroniser le contenu de deux ScrollViewer ?

J’ai eu à travailler à mes débuts en WPF 😉 sur un projet où je devais faire la synchronisation du contenu de deux ScrollViewer. Pour y arriver j’ai dû passer par les propriétés de dépendances attachées et ainsi bénéficier du binding entre les propriétés VerticalOffset des deux contrôles. Pour infos la proprieté VerticalOffset du contrôle ScrollViewer est en lecture .

Le code que j’ai mis en place et qui m’a permis d’arriver à mes fins est le suivant :

public class ScrollViewerBehaviors
{
	public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), typeof(ScrollViewerBehaviors), new PropertyMetadata(0.0, ScrollViewerBehaviors.offsetChanged));
	public static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.RegisterAttached("HorizontalOffset", typeof(double), typeof(ScrollViewerBehaviors), new PropertyMetadata(0.0, ScrollViewerBehaviors.offsetChanged));

	public static double GetVerticalOffset(UIElement scrollViewer)
	{
		return (Double)scrollViewer.GetValue(ScrollViewerBehaviors.VerticalOffsetProperty);
	}
	public static void SetVerticalOffset(UIElement scrollViewer, object value)
	{
		scrollViewer.SetValue(ScrollViewerBehaviors.HorizontalOffsetProperty, value);
	}

	public static double GetHorizontalOffset(UIElement scrollViewer)
	{
		return (Double)scrollViewer.GetValue(ScrollViewerBehaviors.HorizontalOffsetProperty);
	}
	public static void SetHorizontalOffset(UIElement scrollViewer, object value)
	{
		scrollViewer.SetValue(ScrollViewerBehaviors.HorizontalOffsetProperty, value);
	}

	private static void offsetChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
	{
		ScrollViewer scrollViewer = depObj as ScrollViewer;
		if (scrollViewer == null) return;

		double offset = e.NewValue == null ? 0 : (double)e.NewValue;
		switch (e.Property.Name)
		{
			case "VerticalOffset":
				scrollViewer.ScrollToVerticalOffset(offset);
				break;

			case "HorizontalOffset":
				scrollViewer.ScrollToHorizontalOffset(offset);
				break;
		}
	}
}

Côté interface rien de plus simple tout se fait dans le xaml :

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
         
        <ScrollViewer x:Name="scrollViewerOne" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" 
                      local:ScrollViewerBehaviors.VerticalOffset="{Binding ElementName=scrollViewerTwo, Path=VerticalOffset, Mode=OneWay}"
                      local:ScrollViewerBehaviors.HorizontalOffset="{Binding ElementName=scrollViewerTwo, Path=HorizontalOffset, Mode=OneWay}">
            <Image Source="/Chrysanthemum.jpg" Width="1000" />
        </ScrollViewer>

        <ScrollViewer Grid.Column="1" x:Name="scrollViewerTwo" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" 
                      local:ScrollViewerBehaviors.VerticalOffset="{Binding ElementName=scrollViewerOne, Path=VerticalOffset, Mode=OneWay}"
                      local:ScrollViewerBehaviors.HorizontalOffset="{Binding ElementName=scrollViewerOne, Path=HorizontalOffset, Mode=OneWay}">
            <Image Source="/Chrysanthemum.jpg" Width="1000" />
        </ScrollViewer>
    </Grid>
Catégories :Silverlight, WPF Étiquettes : , ,

Pourquoi ma DataGrid avec des colonnes personnalisées ne met pas à jour ma source?

Lorsque vous liez une source de données à une DataGrid et que vous mettiez la valeur de la propriété AutoGenerateColumns à True (qui est la valeur par défaut), on se rend compte que toute modification apportée à notre grille de données met à jours automatiquement la source de données associée. Mais cela n’est pas le cas si nous générons manuellement les colonnes de notre grille de données en mettant la propriété AutoGenerateColumns à false.

Pour que la source de données soit mis à jour à chaque modification de ligne il faut que la liaison de données associée à chaque contrôle (dont le contenu est susceptible d’être modifié) contenu dans les DataTemplate de vos colonnes ait la propriété UpdateSourceTrigger à LostFocus. Cela est valable même si le contrôle est un TextBox pour qui la valeur par défaut la propriété UpdateSourceTrigger est déjà à LostFocus.

Personnaliser la barre de titre et la bordure d’une fenêtre

WPF ne nous permet pas de modifier la barre de titre et la bordure de la fenêtre.

Dans cet article nous allons voir comment faire pour fournir à notre fenêtre une barre de titre et une bordure personnalisées.

Personnalisation de la barre de titre :

Puisqu’elle n’est pas personnalisable par défaut nous allons la cacher et fournir notre propre barre de titre.

Pour cela nous devons définir la propriété WindowsStyle de notre fenêtre à None qui a pour effet de cacher cette barre et nous allons y ajouter le code xaml suivant :

Code xaml pour cacher la barre de titre

 

Si vous exécutez l’application vous constaterez que nous ne pouvons plus déplacer notre fenêtre. Une solution simple est de s’abonner à l’évènement de notre contrôle Border qui nous sert de barre de titre et dans le gestionnaire de cet évènement de faire appel à la méthode DragMove de notre fenêtre.

Le code du gestionnaire devra ressembler à ceci :

Gestionnaire d'évènement MouseDown de la bordure servant de barre de titre

Lorsque vous ré exécutez l’application on pourra maintenant déplacer notre fenêtre en gardant le clic droit appuyé et en déplaçant la souris.

Personnalisation de la bordure de la fenêtre

La bordure telle que fournie par WPF n’est pas modifiable. Ce que nous pouvons faire c’est de la cacher en mettant la propriété ResizeMode de notre fenêtre à NoResize.

Mais il se trouve qu’avec cette valeur notre fenêtre n’est pas redimensionnable mais ce n’est pas grave nous allons fournir notre propre mécanisme de redimensionnement en utilisant le contrôle Thumb.

Pour cela nous allons changer le code xaml de notre fenêtre pour qu’il ressemble à ça :

Code xaml avec le contrôle Thumb

Notre fenêtre doit ressembler à ceci avec le contrôle Thumb en bas à droite :

Apparence de la fenêtre avec le contrôle Thumb

Dans le code xaml nous nous abonnons à l’évènement DragDelta de notre contrôle Thumb. Cet évènement va nous permettre de redimensionner notre fenêtre à chaque fois que l’utilisateur garde le clic de la souris sur le contrôle et effectue un déplacement.

Voici le code du gestionnaire de l’évènement DragDelta qui est très simple :

Gestionnaire de l'évènement DragDelta

Et voilà nous avons fini avec la personnalisation de la barre de titre et de la bordure de notre fenêtre.

Si vous avez remarqué l’état actuel des choses ne nous permet de réduire, maximiser, minimiser et fermer notre fenêtre. Pour que cela soit possible il suffit juste d’ajouter les boutons nécessaires et de faire appel à la méthode Close de la fenêtre pour la fermer et de jouer sur la propriété WindowState pour la maximiser, la minimiser ou la réduire.

Catégories :WPF Étiquettes : , , ,

Gérer les évènements de la classe ServiceHost sous IIS et WAS

Pour héberger un service web WCF sous IIS ou sous WAS, nous sommes obligés de passer par un fichier portant l’extension .svc et d’utiliser la directive @ServiceHost. Par exemple pour héberger le service WCFLibrary.Service, la directive ressemblera à ceci :
Ceci est la syntaxe de base et ne nous permet pas de savoir à quel moment notre service est ouvert ou fermé (le service peut être fermé par IIS parce que le recyclage est en train de s’effectuer).

Pour pouvoir gérer ces deux évènements (Openned et Closed) nous devons créer une classe que je nomme MyServiceHostFactory et cette classe doit dériver de ServiceHostFactory.

Après la définition la classe nous allons changer la directive de notre fichier *.svc, en y ajoutant l’attribut Factory et nous lui assignes le type MyServiceHostFactory :

L’activation du service s’effectue une fois dès la première requête reçue par le service WCF et après chaque recyclage de IIS. Donc du coup il se peut que l’évènement Opened par exemple soit déclenché plusieurs fois durant toute la durée d’exécution de ton serveur IIS.

J’espère que ce billet vous a aidé.

Catégories :WCF Étiquettes : , , ,
%d blogueurs aiment cette page :