Création d'un contrôle RichTextBox évolué pour du texte Rtf (C#)

Jean-Alain Baeyens

Création d'un contrôle utilisateur prenant en charge les fonctionnalités de mise en forme d'un texte enregistré au format Rtf. Ce contrôle personnalisé est une version évoluée du contrôle RichTextBox.

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Création d'un contrôle RichTextBox évolué pour du texte Rtf (C#)

- Auteur : Jean-Alain Baeyens

- Parution : 6 décembre 2004

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2004 Jean-Alain Baeyens. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Création du contrôle utilisateur.

- III. Associer les fonctions à la ToolBar

- III-A. L'appel d'une fonctionnalité

- III-B. La mise en gras

- III-C. La mise en italique, le soulignement et le surlignement

- III-D. Changer de police ou de couleur.

- III-E. Modifier l'indentation.

- III-F. Liste à puces

- III-G. Modification de l'alignement

- IV. Conclusion

I - Introduction

Bien que nous allions aborder le sujet, il ne s'agit pas ici d'apprendre à créer un contrôle utilisateur, mais bien d'apprendre à manipuler du texte Rtf.

Si vous n'avez jamais créé de contrôle utilisateur, je vous engage à lire les articles sur le sujet. Dans cet exercice, j'ai choisi d'utiliser SharpDevelop. Si vous avez déjà créé des contrôles utilisateurs ou si vous avez lu les articles concernant le sujet, il vous sera facile de transposer la procédure à visual studio ou C# builder.

Dotnet met à notre disposition un contrôle graphique permettant d'enregistrer du texte au format Rtf. Il s'agit de la classe RichTextBox. Par l'intermédiaire de cette classe, il met également à notre disposition les méthodes permettant la mise en forme du texte.

Toutefois, ces méthodes ne sont pas directement accessibles à l'utilisateur de l'application, mais doivent être gérées par le programme.

Une solution est de prévoir dans le menu les fonctions spécifiques. Cette partie du menu étant activée ou désactivée en fonction du contrôle actif. Une autre solution consiste à associer un menu contextuel à votre contrôle Rtf qui prendra en charge les différentes méthodes dont vous avez besoin. Il est possible et même conseillé de mettre les deux solutions en œuvre simultanément.

Nous allons ici mettre en œuvre une tout autre technique, pas forcément meilleure, mais plus originale. Nous allons créer notre propre contrôle Rtf qui inclura une barre d'outils pour la manipulation du texte.

Notre contrôle prendra en charge la mise en gras, le soulignement, la mise en italique, le barré, ainsi que la modification de la police ce qui inclus le type (Arial, Courrier…), la taille et la couleur. Nous ajouterons l'alignement à gauche, à droite ou centré. Nous prévoirons également l'indentation gauche, droite et les listes à puces.

II - Création du contrôle utilisateur.

Dans SharpDevelop (la version utilisée est la 1.0.1), choisissez : Fichier>Nouveau>Fichier et dans la catégorie C# choisissez Contrôle utilisateur. Cliquez sur Créer. Le code de base est généré :

public class UserControl1 : System.Windows.Forms.UserControl
{
public UserControl1()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();

//
// TODO: Add constructor code after the InitializeComponent() call.
//
}

#region Windows Forms Designer generated code
private void InitializeComponent()
{
//
// UserControl1
//
this.Name = "UserControl1" ;
this.Size = new System.Drawing.Size(292, 266);
}
#endregion
}

Nous pouvons maintenant nommer la classe. Remplacez UserControl1 par AdvancedRichTextBox. Enregistrez le fichier ainsi créé et nommez le AdvancedRichTextBox.

Une petite subtilité de SharpDevelop, si au lieu de créer votre fichier via le menu, vous avez choisi le menu contextuel, vous devez introduire le nom du fichier directement dans la fenêtre de choix du type de code à générer.

Passez en mode Design. Ensuite, depuis la barre des outils (Windows Forms), faites glisser l'icône ToolBar sur la fenêtre générée. Fixez les propriétés ButtonSize à 16/16, cursor à Hand et name à tlbCtrlRtf.

Les valeurs choisies pour les propriétés peuvent évidemment être modifiées selon vos goûts. Mais attention, la taille des boutons dépend évidemment de la taille des images que vous allez inclure.

Nous pouvons maintenant faire glisser l'objet RichTextBox. Nous le nommerons txtCtrlRtf. Agrandissez le contrôle pour qu'il occupe le reste de l'espace disponible. N'oubliez pas de supprimer le contenu de la propriété Text. Vous devriez avoir ceci :

Nous allons avoir besoin d'une ImageList. Faites glisser l'icône sur la fenêtre. Elle se place en bas à gauche. Je l'ai appelée ImgLstCtrlRtf.

Accédez à la collection par la propriété Images. Ajoutez les images que nous allons utiliser. Vous pouvez les trouver en téléchargement au bout de cet article.

Via la propriété Buttons de notre ToolBar, ajoutez un bouton pour chaque fonction souhaitée.

Votre contrôle ressemble maintenant à ceci :

On ajoute quelques informations devant la classe pour le RAD.

[ToolboxBitmap(typeof(AdvancedRichTextBox),""),Description("Interface utilisateur évolué pour un RichTextBox")]
public class AdvancedRichTextBox : System.Windows.Forms.UserControl
{
...
}

Il nous reste à rendre accessibles les propriétés de la classe.

Rendre public les propriétés

[Description("RichTextBox associée au contrôle"),Category("Composants")]
public RichTextBox RichTextBox  {
get {
return txtCtrlRtf ;
}
}

[Description("ToolBar associée au contrôle"),Category("Composants")]
public ToolBar ToolBar  {
get {
return tlbCtrlRtf ;
}
}

[Description] permet d'ajouter des informations pour le RAD.

Pour qu'il soit possible d'adapter la taille du composant, nous devons encore ajouter une méthode qui sera associée à l'événement Paint de notre contrôle utilisateur.

Code permettant le redimensionnement du composant

void CtrlRtfPaint(object sender, System.Windows.Forms.PaintEventArgs e)    {
this.txtCtrlRtf.Size = new Size(this.Width, this.Height-24);
}

L'objet RichTextox est redimensionné en fonction de la taille du composant. La toolbar ne demande pas d'adaptation.

Nous allons maintenant pouvoir passer aux choses sérieuses.

III - Associer les fonctions à la ToolBar

III-A - L'appel d'une fonctionnalité

Pour gérer l'appel des fonctionnalités depuis la toolbar, nous devons associer l'événement ButtonClick à la méthode suivante :

Gestion du clic de la souris sur un bouton de la toolbar

void ToolBarButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e){
// on détermine la position du bouton activé.
int indexButton = this.tlbCtrlRtf.Buttons.IndexOf(e.Button);
switch(indexButton){
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 6:
break;
case 7:
break;
case 8:
break;
case 10:
break;
case 11:
break;
case 12:
break;
}
}

C'est un switch qui permet d'orienter le programme vers la bonne action. Remarquez qu'il n'y a pas de traitement pour les valeurs 5 et 9. Dans l'exemple elles correspondent à des espacements dans la toolbar.

III-B - La mise en gras

Le bouton pour la mise en gras est le premier. Nous allons donc changer le code pour la valeur 0 dans notre switch.

Mise en gras du texte

case 0:
this.txtCtrlRtf.SelectionFont = new Font(this.txtCtrlRtf.SelectionFont
,this.txtCtrlRtf.SelectionFont.Style + FontStyle.Bold);
break;

Nous avons pu mettre le texte en gras, mais notre code ne peut être conservé ainsi, car le même bouton sera utilisé pour effectuer l'opération inverse. Nous verrons dans le chapitre suivant comment réaliser cela.

III-C - La mise en italique, le soulignement et le surlignement

Pour permettre d'appliquer correctement le style souhaité en fonction du style déjà défini, nous devons connaître l'état actuel. J'ai choisi d'appliquer les styles en une seule opération.

Application du nouveau style au font

void ToolBarButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e){
// on détermine la position du bouton activé.
int indexButton = this.tlbCtrlRtf.Buttons.IndexOf(e.Button);
// on récupère les styles utilisés dans le texte sélectionné.
bool bold = this.txtCtrlRtf.SelectionFont.Bold;
bool italic = this.txtCtrlRtf.SelectionFont.Italic;
bool underline = this.txtCtrlRtf.SelectionFont.Underline;
bool strikeout = this.txtCtrlRtf.SelectionFont.Strikeout;
int style = 0;

if (indexButton < 4){
// on inverse le style spécifique
switch(indexButton){
case 0:
bold = ! bold;
break;
case 1:
italic = ! italic;
break;
case 2:
underline = ! underline;
break;
case 3:
strikeout = ! strikeout;
break;
}
// on crée un nouveau FontStyle avec les styles activés
if (bold)
style += (int)FontStyle.Bold;
if (italic)
style += (int)FontStyle.Italic;
if (underline)
style += (int)FontStyle.Underline;
if (strikeout)
style += (int)FontStyle.Strikeout;
// on applique le nouveau FontStyle.
this.txtCtrlRtf.SelectionFont = new Font(this.txtCtrlRtf.SelectionFont
,(FontStyle)style);
}else{
...
}
}

Remarquez que les propriétés Bold,Underline,etc. de SelectionFont sont en lecture uniquement et ne peuvent être assignées. Ce qui nous oblige à assigner un nouveau font. Pour ne pas perdre les autres caractéristiques du font, nous utilisons un constructeur qui reçoit le font actuel et le nouveau FontStyle.

Il nous reste encore un problème à résoudre. Notre procédure permet d'activer un style pour le texte qui suit le point d'insertion ou le texte sélectionné, mais le bouton devrait afficher l'état du style en fonction de la position du curseur. Pour y arriver, nous allons utiliser l'événement SelectionChanged de notre RichTextBox et y associer la méthode suivante.

Modification automatique de l'état des boutons en fonction de l'état du style correspondant.

void TxtCtrlRtfSelectionChanged(object sender, System.EventArgs e) {
this.tlbButtonBold.Pushed = this.txtCtrlRtf.SelectionFont.Bold;
this.tlbButtonItalic.Pushed = this.txtCtrlRtf.SelectionFont.Italic;
this.tlbButtonUnderline.Pushed = this.txtCtrlRtf.SelectionFont.Underline;
this.tlbButtonStrikeout.Pushed = this.txtCtrlRtf.SelectionFont.Strikeout;
}

L'état du bouton s'adapte maintenant en fonction du déplacement du curseur.

III-D - Changer de police ou de couleur.

Notre code ressemble maintenant à ceci :

void ToolBarButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e){
// on détermine la position du bouton activé.
int indexButton = this.tlbCtrlRtf.Buttons.IndexOf(e.Button);
// on récupère les styles utilisés dans le texte sélectionné.
bool bold = this.txtCtrlRtf.SelectionFont.Bold;
bool italic = this.txtCtrlRtf.SelectionFont.Italic;
bool underline = this.txtCtrlRtf.SelectionFont.Underline;
bool strikeout = this.txtCtrlRtf.SelectionFont.Strikeout;
int style = 0;

if (indexButton < 4){
...
}else{
switch(indexButton){
case 4:
break;
case 6:
break;
case 7:
break;
case 8:
break;
case 10:
break;
case 11:
break;
case 12:
break;
}
}
}

Pour changer de font ou de couleur, le plus simple est de faire appel à la fenêtre de dialogue prédéfinie, FontDialog.

case 4:
FontDialog fontDlg = new FontDialog();
// on initialise le font en cours comme valeur par défaut de la fenêtre
fontDlg.Font = this.txtCtrlRtf.SelectionFont;
// on demande la possibilité de changer les couleurs.
fontDlg.ShowColor = true;
if (fontDlg.ShowDialog() == DialogResult.OK){
// on applique les changements.
this.txtCtrlRtf.SelectionFont = fontDlg.Font;
this.txtCtrlRtf.SelectionColor = fontDlg.Color;
}
fontDlg.Dispose();
break;

Remarquez qu'ici nous pouvons assigner directement le font sans aucun souci, car dans la fenêtre FontDialog, tout est pris en charge y compris le gras et les autres styles.

III-E - Modifier l'indentation.

Il n'y a pas grand-chose à dire ici, il ne s'agit que d'utiliser la bonne propriété. Remarquez toutefois que l'on teste si l'alignement est bien à gauche, car on ne peut indenter un texte centré ou aligné à droite.

case 6:
if (this.txtCtrlRtf.SelectionIndent >= 36
&& this.txtCtrlRtf.SelectionAlignment == HorizontalAlignment.Left){
this.txtCtrlRtf.SelectionIndent -= 36;
}
break;
case 7:
if (this.txtCtrlRtf.SelectionAlignment == HorizontalAlignment.Left){
this.txtCtrlRtf.SelectionIndent += 36;
}
break;

III-F - Liste à puces

Encore plus simple. On inverse simplement la propriété.

case 8:
this.txtCtrlRtf.SelectionBullet = !this.txtCtrlRtf.SelectionBullet ;
break;

III-G - Modification de l'alignement

Ici encore pas grand-chose à dire.

case 10:
this.txtCtrlRtf.SelectionAlignment = HorizontalAlignment.Left;
break;
case 11:
this.txtCtrlRtf.SelectionAlignment = HorizontalAlignment.Center;
break;
case 12:
this.txtCtrlRtf.SelectionAlignment = HorizontalAlignment.Right;
break;

Pour pimenter un peu, nous allons griser les boutons d'indentation et les rendre inactifs quand l'alignement n'est pas à droite.

case 10:
this.txtCtrlRtf.SelectionAlignment = HorizontalAlignment.Left;
this.tlbButtonIndentLeft.ImageIndex = 9;
this.tlbButtonIndentRight.ImageIndex = 11;
this.tlbButtonCenter.Pushed = false;
this.tlbButtonRight.Pushed = false;
break;
case 11:
this.txtCtrlRtf.SelectionAlignment = HorizontalAlignment.Center;
this.tlbButtonIndentLeft.ImageIndex = 10;
this.tlbButtonIndentRight.ImageIndex = 12;
this.tlbButtonLeft.Pushed = false;
this.tlbButtonRight.Pushed = false;
break;
case 12:
this.txtCtrlRtf.SelectionAlignment = HorizontalAlignment.Right;
this.tlbButtonIndentLeft.ImageIndex = 10;
this.tlbButtonIndentRight.ImageIndex = 12;
this.tlbButtonLeft.Pushed = false;
this.tlbButtonCenter.Pushed = false;
break;

Pour griser le bouton, nous utilisons en fait une autre image.

Il ne sous reste plus qu'à actualiser l'état des boutons relatifs à l'alignement en fonction du texte. Il nous suffit pour cela d'ajouter trois lignes à notre précédente méthode.

void TxtCtrlRtfSelectionChanged(object sender, System.EventArgs e) {
this.tlbButtonBold.Pushed = this.txtCtrlRtf.SelectionFont.Bold;
this.tlbButtonItalic.Pushed = this.txtCtrlRtf.SelectionFont.Italic;
this.tlbButtonUnderline.Pushed = this.txtCtrlRtf.SelectionFont.Underline;
this.tlbButtonStrikeout.Pushed = this.txtCtrlRtf.SelectionFont.Strikeout;
this.tlbButtonBullet.Pushed = this.txtCtrlRtf.SelectionBullet;
this.tlbButtonLeft.Pushed = (this.txtCtrlRtf.SelectionAlignment == HorizontalAlignment.Left);
this.tlbButtonCenter.Pushed = (this.txtCtrlRtf.SelectionAlignment == HorizontalAlignment.Center);
this.tlbButtonRight.Pushed = (this.txtCtrlRtf.SelectionAlignment == HorizontalAlignment.Right);
}

IV - Conclusion

La manipulation du Rtf au travers d'une RichTextBox ne contient en définitive pas de piège particulier et il suffit de peu de connaissances pour la mettre en œuvre. Dans le code source complet de l'exercice, vous trouverez également l'ajout d'un menu contextuel permettant le copier, coller et le défaire, refaire. Cette partie n'est pas expliquée ici simplement parce qu'il n'y a vraiment rien à dire.