WPF

De Banane Atomic
Aller à la navigationAller à la recherche

Généralités

Permet la création d'interfaces graphiques. Remplace Windows Forms.

  • Affichage vectoriel, cela permet d'augmenter la taille des objets en fonction de la résolution de l'écran sans effet de pixellisation.
  • Séparation entre le code de l'interface graphique (XAML) et le code des fonctionnalités (C#).

Historique

WPF Version Release .NET Version Visual Studio Version Major Features
3.0 2006-11 3.0 Initial Release.
3.5 2007-11 3.5 VS 2008 Changes and improvements in: Application model, data binding, controls, documents, annotations, and 3-D UI elements.
3.5 SP1 2008-08 3.5 SP1 Native splash screen support, New WebBrowser control, DirectX pixel shader support.
Faster startup time and improved performance for Bitmap effects.
4.0 2010-04 4.0 VS 2010 New controls: Calendar, DataGrid, and DatePicker.
Multi-Touch and Manipulation
4.5 2012-08 4.5 VS 2012 New Ribbon control, New INotifyDataErrorInfo interface
4.5.1 2013-10 4.5.1 VS 2013
4.5.2 2014-05 4.5.2
4.6 2015-07 4.6 VS 2015 Transparent child window support
HDPI and Touch improvements

Assemblages nécessaires

  • PresentationCore
  • PresentationFramework
  • System.Xaml
  • WindowsBase

Assemblages additionnels

Nom du paquet NuGet Assemblages installés
Expression.Blend.Sdk.WPF
  • Microsoft.Expression.Interactions v4.5 (MouseDragElementBehavior, interactivity:DataTrigger)
  • System.Windows.Interactivity v4.5 (Behavior, InvokeCommandAction, interactivity:EventTrigger)
System.Windows.Interactivity.WPF
  • Microsoft.Expression.Interactions v4.0
  • System.Windows.Interactivity v4.0
Blend.Interactivity.WPF
  • Microsoft.Expression.Interactions v3.5
  • System.Windows.Interactivity v3.5

Visual et Logical Tree

  • Visual Tree: rendu final
  • Logical Tree: code XAML

Snoop

Permet d'afficher l'arborescence visuelle d'une fenêtre WPF.
Crtl + Shift → sélectionner un élément visuel

Récupérer les Templates des composants de base

Csharp.svg
// récupérer le template par défaut de button1 définit dans le XAML
using (var xw = new XmlTextWriter(@"ButtonDefaultTemplate.xml", null))
{
    xw.Formatting = Formatting.Indented;
    System.Windows.Markup.XamlWriter.Save(button1.Template, xw);
}

WPF Control Template Viewer

Forcer la culture locale dans WPF

Par défaut, WPF utilise la culture US English.

Csharp.svg
FrameworkElement.LanguageProperty.OverrideMetadata(
    typeof(FrameworkElement), new FrameworkPropertyMetadata(
        XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));

Erreurs

Impossible de créer le type inconnu ...

Xaml.png
<MainControl xmlns:xxx="clr-namespace:Namespace">
<!-- Ajouter l'assembly car la définition du namespace n'est pas suffisante -->
<MainControl xmlns:xxx="clr-namespace:Namespace;assembly=Assembly">

Astuces

Icônes / Glyphes

NuGet → FontAwesome.WPF

Xaml.png
<Window xmlns:fa="http://schemas.fontawesome.io/icons/">
    <fa:ImageAwesome Icon="Refresh" Spin="True" Height="16" Width="16" />

Clipboard

Csharp.svg
Clipboard.SetText("Hello");

Design Time Data

Xaml.png
<!-- Définition d'un DataContext pour le design time et d'un autre pour le run time -->
<UserControl xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             d:DataContext="{Binding ...}"
             DataContext="{Binding ...}">
    <UserControl.Resources>
        <CollectionViewSource x:Key="MyCollection" Source="{Binding ...}" d:Source="{Binding ...}" />
    </UserControl.Resources>

    <ItemsControl ItemsSource="{Binding Source={StaticResource MyCollection}}" />
Le Designer n’exécute pas le code-behind: le DataContext soit être définit dans le XAML.

Control à définir depuis le code-behind

Xaml.png
<!-- Permet de définir le Control à afficher depuis le code-behind -->
<ContentControl Content="{Binding Path=AutreControl}"/>

UserControl internal

MyUserControl.xaml.cs
internal partial class MyUserControl : UserControl
{ }
MyUserControl.xaml
<UserControl x:Class="MyNamespace.MyUserControl"
             x:ClassModifier="internal">

Un Enum dans du XAML

Xaml.png
<MainControl xmlns:current="clr-namespace:CurrentNameSpace"
             Propriete="{x:Static Member=current:MaClasse+MonEnum.Element1}">
Csharp.svg
namespace CurrentNameSpace
{
    public class MaClasse
    {
        // public ou internal au minimum
        internal enum MonEnum { Element1, Element2 }
    }
}

Image

Xaml.png
<Image Source="C:\chemin\image.jpg" />

<!-- si la taille de l'image est réduite, utiliser DecodePixelWidth ou DecodePixelHeight
pour mettre en cache l'image réduite et non l'originale, ceci afin d'économiser de la mémoire -->
<Image Width="100">
    <Image.Source>
        <BitmapImage DecodePixelWidth="100" UriSource="C:\chemin\image.jpg" />
    </Image.Source>
</Image>

<!-- ajouter l'image au projet:
Build action → Content
Copy to Output Directory → Copy if newer
L'image n'est pas embarqué dans l'assemblage -->
<Image Source="dossier/image.jpg" />
<!-- depuis un autre assemblage -->
<Image Source="/AssemblyName;component/dossier/image.jpg" />
Le format SVG n'est pas supporter nativement, il faut le convertir en XAML (avec Inkscape).

Binding et web API

Csharp.svg
private ImageSource _imageBoxStream;
public ImageSource ImageBoxStream
{
    get
    {
        return _imageBoxStream;
    }
    set
    {
        Set(() => ImageBoxStream, ref _imageBoxStream, value, true);
    }
}

var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await _httpClient.SendAsync(request);

using (var stream = await response.Content.ReadAsStreamAsync())
{
    var bitmapImage = new BitmapImage();
    bitmapImage.BeginInit();
    bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
    bitmapImage.StreamSource = stream;
    bitmapImage.EndInit();
    ImageBoxStream = bitmapImage;
}
Xaml.png
<Image Source="{Binding ImageBoxStream}" Stretch="None" />

Image base64

Csharp.svg
var base64string = "data:image/jpeg;base64,XXXX";
var match = Regex.Match(response, @"data.+,(?<base64>.+)");
if (match.Success)
{
    byte[] binaryData = Convert.FromBase64String(match.Groups["base64"].Value);
    var bitmapImage = new BitmapImage();
    bitmapImage.BeginInit();
    bitmapImage.StreamSource = new MemoryStream(binaryData);
    bitmapImage.EndInit();
    ImageBoxStream = bitmapImage;
}

OriginalSource vs Source sur RoutedEventArgs

OriginalSource correspond à l'objet visuel alors que Source correspond à l'objet XAML.

Xaml.png
<Button Content="Text" 
        PreviewMouseDown="Button_PreviewMouseDown"/>
Csharp.svg
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    var source = e.Source; // Button
    var originalSource = e.OriginalSource; // ButtonChrome
}

Arbre visuel correspondant :

Button
ButtonChrome
ContentPresenter
TextBlock

Rechercher tous les Control d'un type donné

Csharp.svg
public static IEnumerable<T> FindChildren<T>(this DependencyObject parent, Func<T, bool> predicate)
    where T : DependencyObject
{
    var children = new List<DependencyObject>();

    if ((parent is Visual) || (parent is Visual3D))
    {
        var visualChildrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int childIndex = 0; childIndex < visualChildrenCount; childIndex++)
        {
            children.Add(VisualTreeHelper.GetChild(parent, childIndex));
        }
    }
    foreach (var logicalChild in LogicalTreeHelper.GetChildren(parent).OfType<DependencyObject>())
    {
        if (!children.Contains(logicalChild))
        {
            children.Add(logicalChild);
        }
    }

    foreach (var child in children)
    {
        var typedChild = child as T;
        if ((typedChild != null) && predicate.Invoke(typedChild))
        {
            yield return typedChild;
        }

        foreach (var foundDescendant in FindChildren(child, predicate))
        {
            yield return foundDescendant;
        }
    }

    yield break;
}

WPF vs UWP

WPF

  • OS antérieur à Windows 10
  • accès bas niveau au noyau et au matériel
  • applications non-sanboxées, accès aux APIs système
  • APIs graphique: radial brush, clipping mask
  • Fenêtre transparente
  • Accès plus fin à la bar de titre et au chrome des fenêtres
  • intégration de WindowsForm
  • chargement de code dynamique: plugins
  • applications complexes à plusieurs fenêtres

UWP

  • distribution via le store
  • applications sanboxées
  • code à destination de différents supports: smartphone, Xbox