• Moyenne
Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Introduction du cours

Introduction

GraphX for .NET est une bibliothèque graphique disponible pour .NET permettant de réaliser des graphes en tout genre, basé sur une autre bibliothèque nommée Graph#. Elle est développée par un russe nommé PantheR.

Elle dépend des bibliothèques open-source suivantes:

  • QuickGraph

  • YAXLib

Et utilise du code des projets open-source suivants :

  • Graph#

  • WPFExtensions

  • NodeXL

  • Extended WPF Toolkit

Exemple de graphe avec GraphX for .NET
Exemple de graphe avec GraphX for .NET

Installation

Le code de GraphX for .NET est disponible sur le site de PantheR (en anglais ou russe).
Pour installer GraphX, rien de bien compliqué : téléchargez le code source sur son site. Vous pouvez ensuite utiliser les bibliothèques .DLL (sachant qu'il a déjà inclus toutes les dépendances).

Attaquons nous maintenant à votre premier graphe !

Premier graphe

Pour ce tutoriel, j'utiliserai la version Visual Studio 2012 Professional.

Nous allons donc créer une application WPF, que je nomme Graph, pour faire original. Vérifiez bien que vous créeiez une application WPF, et non une application console.

ZoomControl - Fenêtre principale

Vous avez donc la fenêtre principale (MainWindow), dont le code se compose d'une partie design, et d'une partie avec le code-behind.

<Window x:Class="Graph.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">
    <Grid>
       <!-- On va ajouter notre graphe ici -->
    </Grid>
</Window>

Le graphe va être encapsulé dans un ZoomControl, fourni par GraphXIl va falloir rajouter la bibliothèque dans les références. Pour ce faire, il suffit d'un clic droit sur References > Add Reference > Browse, puis sélectionnez les bibliothèques de GraphX téléchargées précédemment.

Ensuite, vous pouvez définir le ZoomControl ainsi que le graphe que vous allez utiliser par la suite.

<graphxctrl:ZoomControl x:Name="zoomCtrl">
    <local:MyArea x:Name="myArea"/>
</graphxctrl:ZoomControl>

Il ne faut évidemment pas oublier de définir les namespaces (ou espaces de noms). Voici l'allure de votre code actuel :

<Window x:Class="Graph.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:graphxctrl="clr-namespace:GraphX.Controls;assembly=GraphX.Controls"
        xmlns:local="clr-namespace:Graph"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <graphxctrl:ZoomControl x:Name="zoomCtrl">
            <local:MyArea x:Name="myArea"/>
        </graphxctrl:ZoomControl>
    </Grid>
</Window>

Bien ! Pour l'instant, nous avons donc une erreur : MyArea n'est pas définie.

Pour résoudre ce problème, nous allons la définir.

Nouvelles classes

La première classe à définir est celle qui représente la zone de dessin.
Pour ceci, il suffit de créer ume classe (Clic droit sur votre application WPF puis Add Project ou encore Alt+Shift+C pour les fans de raccourcis). Ce fichier va se nommer MyGraph.cs et contiendra le code suivant :

using GraphX;
using QuickGraph;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Graph
{
    public class MyArea : GraphArea<MyVertex,MyEdge,BidirectionalGraph<MyData,MyEdge>> {}
}

Ca commence à se compliquer légérement. En effet, la zone de dessin à besoin de noeuds (vertex), d'arêtes (edge) et un graphe bidirectionnel. GraphArea est fournie par GraphX.Controls et BidirectionalGraph par QuickGraph.

Du coup, il faut maintenant définir deux nouvelles classes : MyVertex et MyEdge. Pour celles-ci, rebelote !

using GraphX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Graph
{
    public class MyVertex : VertexBase {}
}

La seconde classe (MyEdge) devra elle définir les liaison entre les vertex. Elle va donc avoir un constructeur et qui prendra en paramètre la source, la destination et le poids.

using GraphX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Graph
{
    public class MyEdge : EdgeBase<MyVertex>
    {
        public MyEdge(MyVertex source, MyVertex target, double weight = 1)
            : base(source, target, weight) { }

        public MyEdge()
            : base(null, null, 1) { }
    }
}

Nous avons déjà bien avancé sur cette partie. Mais il manque encore le point le plus important : le graphe à proprement parler ainsi que la logique de ce dernier. Pour ceci, il est nécessaire de créer deux nouvelles classes.

public class MyGraph : BidirectionalGraph<MyVertex, MyEdge> { }

public class MyGXLogicCore : GXLogicCore<MyVertex,MyEdge,
                        BidirectionalGraph<MyVertex,MyEdge>> {}

Nous allons maintenant définir la logique de l'application. Pour cela, nous allons modifier le code-behind de la fenêtre principale. En effet, nous allons abonner la MainWindow à l'évènement Loaded, ce qui est également possible en mode design.

public MainWindow()
{
    InitializeComponent();
    Loaded += MainWindow_Loaded;
}

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    CreateRandomGraph(); 
    myArea.GenerateGraph(true, true); // Génération visuelle du graphe
    myArea.ShowAllEdgesArrows(true); // Rend les flèches des arêtes visibles
    myArea.ShowAllEdgesLabels(true); // Permet de donner des noms aux arêtes
    zoomCtrl.ZoomToFill(); // Zoome au mieux
}

private void CreateRandomGraph()
{
    // C'est ici que nous allons enfin créer notre premier graphe.
    throw new NotImplementedException();
}

Dans cette fonction CreateRandomGraph(), nous allons définir notre premier graphe. Il va être très simple et ne contiendra que des noeuds :

private void CreateRandomGraph()
{
    // Déclaration de la logique du graphe
    MyGXLogicCore logic = new MyGXLogicCore();

    // Définition des noeuds
    List<MyVertices> vertices = new List<MyVertices>();
    // Création du graphe
    MyGraph graph = new MyGraph();

    // Création de dix noeuds
    for (int i = 0; i < 10; i++)
    {
        vertices.Add(new MyVertices() { ID= i });
    }
    
    // Ajout des noeuds au graphe
    graph.AddVertexRange(vertices);

    // Ajout du graphe dans la logiqe et définition des algorithmes d'affichage
    logic.Graph = graph;
    logic.DefaultLayoutAlgorithm = LayoutAlgorithmTypeEnum.Circular;
    logic.DefaultOverlapRemovalAlgorithm = OverlapRemovalAlgorithmTypeEnum.OneWayFSA;

    // Ajout de la logique dans le dessin 
    myArea.LogicCore = logic;
}

Maintenant tout fonctionne ! Enfin presque ... En effet, nous n'avons pas encore défini de design à nos noeuds. Pour ceci, nous allons définir un ResourceDictionary ainsi qu'un UserControl. Nous y sommes presque !

Design

Nous devons donc définir un ResourceDictionary. Pour ceci, créons un nouveau dossier que nous allons appeler Resources. Ce dossier contiendra tous les dictionnaires de resources, s'ils y en a plusieurs. Ajoutons maintenant un nouveau ResourceDictionary, que nous allons nommer VertexStyle :

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:gxl="clr-namespace:GraphX;assembly=GraphX.Controls"
                    xmlns:local="clr-namespace:Graph.Vertices">
    

    <!-- VERTEX CONTROL -->
    <Style TargetType="{x:Type gxl:VertexControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type gxl:VertexControl}">
                    <Grid>
                        <local:MyVerticeControl DataContext="{TemplateBinding Vertex}">
                        </local:MyVerticeControl>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Il faut maintenant déclarer le UserControl, que nous allons appeler MyVerticeControl.

<UserControl x:Class="Graph.Vertices.MyVerticeControl"
             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" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    
     <Border BorderBrush="Black" CornerRadius="10,10,10,10"
             BorderThickness="2" Background="LightGray">
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock>Numéro </TextBlock><TextBlock Text="{Binding ID}"/>
        </StackPanel>
    </Border>
    
</UserControl>

Évidemment, la dernière étape sera de rajouter le dictionnaire de ressources à notre application. Pour ceci, il faut ajouter une nouvelle ligne au fichier App.xaml :

<Application.Resources>
    <ResourceDictionary Source="Resources/VertexStyle.xaml"/>
</Application.Resources>

C'est prêt ! Votre application ne demande plus que d'être lancée !

Premier graphe fonctionnel
Premier graphe fonctionnel
Exemple de certificat de réussite
Exemple de certificat de réussite