2
votes

La modification d'un texte RichTextBox étendu dans le constructeur ne fonctionne pas

J'ai la classe suivante:

public partial class RichTextBoxEx : RichTextBox
{
    public RichTextBoxEx()
    {
        InitializeComponent();
        Text = "Some Text";
    }
}

Cependant, lorsque je la place sur un formulaire et exécute le programme, le RichTextBox est vide. Quel est le problème et comment puis-je le résoudre?

entrez la description de l'image ici

Je suppose qu'il me manque quelque chose de basique ici, mais Je n'arrive pas à savoir quoi et je n'ai pas réussi à trouver d'informations à ce sujet.


3 commentaires

Essayez d'appeler le constructeur de base? Au lieu d'initialiser les composants vous-même


@Zinov - Voulez-vous dire comme public RichTextBoxEx (): base () {Text = "some text"; } ? Ça ne marche pas.


Cela est dû à la méthode InitializeNewComponent de RichTextBoxDesigner. Plus précisément, c'est dans la méthode InitializeNewComponent de la classe TextBoxBaseDesigner qui définit la propriété Text sur une chaîne vide. La bonne façon de résoudre ce problème consiste à créer un concepteur pour votre classe personnalisée. Sinon, vous pouvez pirater un hook dans le service de sélection de l'hôte du concepteur qui définirait la propriété Text uniquement pendant que l'hôte ne se charge pas. Cela serait fait en remplaçant le paramètre de propriété Site du contrôle pour définir le hook.


4 Réponses :


0
votes

Je ne sais pas comment vous avez implémenté votre cours. Lorsque j'ai essayé de reproduire votre problème, j'ai créé une classe, puis j'ai ajouté l'utilisation de System.Windows.Forms, puis j'ai créé la classe. Comme vous l'avez fait, mais je ne me suis pas retrouvé avec une classe partielle publique ni une méthode InitializeComponent () appelée dans mon constructeur (que j'ai dû écrire).

Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FormTester
{
    public class RichTextboxEx : RichTextBox
    {
        public RichTextboxEx() : base()
        {
            Text = "Some Text";
        }
    }
}


1 commentaires

@ jonr79 Vous n'avez pas besoin d'appeler explicitement le constructeur sans paramètre de base, il sera appelé implicitement.



1
votes

La propriété Text est réinitialisée lors de InitializeComponent du formulaire.

Lorsque vous regardez le fichier Designer.cs de le Form , vous devriez trouver une ligne comme celle-ci:

public class RichTextBoxEx : RichTextBox
{
    private bool _initialized = false;
    protected override void OnCreateControl()
    {
        if (!_initialized)
        {
            _initialized = true;
            Text = "Hello World";
        }

        base.OnCreateControl();
    }
}

Vous pouvez surmonter cela en remplaçant le OnCreateControl p>

Alors changez votre contrôle à ceci:

public class RichTextBoxEx : RichTextBox
{
    protected override void OnCreateControl()
    {
        Text = "Hello World";
        base.OnCreateControl();
    }
}

Si le OnCreateControl est appelé plusieurs fois - malgré la définition de celui-ci sur les états MSDN :

La méthode OnCreateControl est appelée lors de la création du contrôle

Ensuite, vous pouvez le forcer à être appelé une fois en utilisant un booléen pour savoir s'il a été appelé ou non, alors essayez ce qui suit:

private void InitializeComponent()
{
    this.richTextBoxEx1 = new WindowsFormsApp1.RichTextBoxEx(); //<-- RichTextBoxEx gets initialized and ITS constructor and InitializeComponent gets called
    this.SuspendLayout();
    // 
    // richTextBoxEx1
    // 
    this.richTextBoxEx1.Location = new System.Drawing.Point(322, 106);
    this.richTextBoxEx1.Name = "richTextBoxEx1";
    this.richTextBoxEx1.Size = new System.Drawing.Size(100, 96);
    this.richTextBoxEx1.TabIndex = 0;
    this.richTextBoxEx1.Text = ""; //<-- Text Property gets reseted
    // 
    // Form1
    // 
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(800, 450);
    this.Controls.Add(this.richTextBoxEx1);
    this.Name = "Form1";
    this.Text = "Form1";
    this.ResumeLayout(false);

}


14 commentaires

@Jazimov - Je ne parle pas du InitializeComponent du Control mais du InitializeComponent du formulaire. - J'ai étendu le Designer.cs pour le rendre plus clair. - Btw, ce code est testé et fonctionne sur ma machine.


C'est vrai. Cela arrive tout le temps :)


Cette réponse fonctionne pour moi, mais je vérifierai les autres réponses avant d'accepter cela.


@ Rand - Excuses - vous avez raison. Le InitializeComponent () affiché serait pour le composant et non pour le formulaire ...


@RandRandom - Lorsque j'ajoute à OnCreateControl cette ligne: BorderStyle = BorderStyle.None; - la méthode est appelée deux fois. Comment puis-je éliminer cela?


@Sipo Tout dépend de la raison pour laquelle vous faites cela. Vous pouvez également masquer ( new ) la propriété Text, en la définissant sur une valeur prédéfinie si le Text est vide, par exemple. En utilisant le remplacement ici, sans autre vérification, vous remplacerez également le texte défini par un utilisateur.


@Jimi - Je ne sais pas trop ce que tu veux dire ni à quoi tu réponds. : /


Mon commentaire est lié à ce que vous demandez à Rand Random et, dans l'ensemble, à la fonctionnalité que vous essayez d'atteindre. Le cas d'utilisation est pertinent, car la méthode peut changer en fonction de ce à quoi sert ce texte prédéfini .


Oui, c'est à peu près compris :) La raison ne l'est pas. Vous pouvez simplement définir le texte dans le concepteur. Sauf si vous créez une bibliothèque qui comprend des contrôles personnalisés. Ou, peut-être, une sorte de filigrane à la place. Ou simplement un texte présenté lorsqu'un utilisateur n'en définit aucun. En fonction de ces cas d'utilisation (ou d'autres), une méthode peut être choisie à la place d'une autre. Pour noter que la poignée d'un contrôle peut être recréée plusieurs fois dans la même exécution.


Par exemple, consultez ici (source .Net). Lorsque vous définissez le BorderStyle d'un contrôle dérivé TextBoxBase, le configurateur de propriétés appelle RecreateHandle () . Par conséquent, les événements OnHandleCreated et OnControlCreated (plus CreateParams , mais c'est sans rapport ici) seront augmentés à cause de cela.


De cette façon, il définira toujours la propriété Text sur "Some Text" . Il doit se comporter comme les autres propriétés, le fait d'avoir une valeur par défaut permet également aux utilisateurs de modifier la valeur de la propriété au niveau du concepteur et de ne pas la remplacer par la valeur par défaut au moment de l'exécution. Je doute que l'OP recherche une propriété Text en lecture seule, mais si c'est le cas, remplacer la propriété Text est le bon chemin à suivre.


@RezaAghaei - je ne sais pas pourquoi vous impliquez que ma solution rend la propriété Text en lecture seule - même la définition trouvée sur MSDN indique que La méthode OnCreateControl est appelée lorsque le contrôle est le premier créé - donc le OnCreateControl ne doit être appelé qu'une seule fois - alors, pourquoi mon code devrait-il le rendre en lecture seule? Oui, mon code ne prend pas en charge le concepteur et la valeur "Par défaut" sera définie lors de l'exécution, mais pourquoi vous pensez qu'il est en lecture seule, je ne comprends pas.


@Rand Random À propos de OnCreateControl , lisez mon commentaire ci-dessus. Simple à tester également.


@RandRandom Je dis qu'il ne respecte pas la valeur que l'utilisateur entre au moment du design et le remplacera toujours par la valeur par défaut lorsque vous exécutez le code.



0
votes

Je ne savais pas comment vous instanciiez le RichTextBox, donc cela pourrait ou non être utile ...

Quand vous avez écrit que vous avez placé un contrôle RichTextBox "sur un formulaire", j'ai supposé que cela signifiait que vous faites glisser / déposez le contrôle de la boîte à outils sur la surface du concepteur. Si vous aviez fait cela, alors vous obtiendriez une instance de RichTextBox et non de RichTextBoxEx.

Pour obtenir une instance de RichTextBoxEx, vous pouvez la compiler dans une DLL et l'ajouter à votre boîte à outils.

Une autre approche, vous donnant un peu plus de contrôle sur l'instanciation / l'initialisation des objets, consiste à l'instancier dans le code et à l'ajouter à votre formulaire de cette façon. Dans le constructeur du formulaire, vous pouvez faire ceci:

var richTextBoxEx = new RichTextBoxEx();
// set your richTextBoxEx properties here
richTextBoxEx.Top = 100;
richTextBoxEx.Left = 100;

this.Controls.Add(richTextBoxEx);

Bien que vous puissiez définir les propriétés comme indiqué ci-dessus, vous pouvez également le faire (comme vous l'avez fait) dans le constructeur du contrôle sous-classé.

J'espère que cela offre une autre façon d'aborder cela.


0 commentaires

2
votes

Les valeurs de propriété que vous définissez dans le constructeur du contrôle sont généralement respectées. Mais pour la propriété Text , le cas est un peu différent. Je l'ai déjà décrit dans une autre réponse . En fait, c'est le concepteur de contrôle qui définit la propriété Text du contrôle dans InitializeNewComponent .

En option, vous pouvez créer et enregistrer un nouveau concepteur de contrôle, remplacer InitializeNewComponent et capturer la valeur de la propriété Text avant d'appeler base.InitializeNewComponent code > méthode. Ensuite, après avoir appelé la méthode de base, redéfinissez la propriété Text sur la valeur par défaut.

using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
[Designer(typeof(RichTextBoxExDesigner))]
public class RichTextBoxEx : RichTextBox
{
    public RichTextBoxEx ()
    {
        Text = "Some Text";
    }
}
public class RichTextBoxExDesigner : ControlDesigner
{
    public override void InitializeNewComponent(System.Collections.IDictionary defaultValues)
    {
        var txt = Control.Text;
        base.InitializeNewComponent(defaultValues);
        Control.Text = txt;
    }
}

Remarque: Ne oubliez d'ajouter une référence à l'assemblage System.Design .

Remarque: Pas pour la propriété Text , mais pour d'autres cas similaires dont vous voyez qu'une valeur de propriété n'est pas respectée lorsque vous définissez dans le constructeur, un autre suspect est CreateComponentsCore de ToolboxItem du contrôle. Pour l ' exemple pour la propriété AutoSize de la Label . P >


10 commentaires

Pour des raisons indéterminées, les concepteurs de WinForms sont (apparemment) considérés comme une affaire obscure . Probablement, la propriété Text pourrait être masquée et la propriété AutoSize d'une étiquette pourrait être définie en remplaçant OnLayout . Mais apprendre à utiliser un concepteur personnalisé est la vraie affaire, IMO.


@Jimi Shadowing peut parfois faire l'affaire alors qu'en général, l'observation de l'OMI n'est pas une bonne idée. Dans ce cas, l'observation ne résoudra pas le problème. Parce que la définition de la valeur Text est effectuée par le concepteur de contrôles lorsque vous déposez le contrôle sur le formulaire.


L'observation peut fonctionner, cela dépend du but. Quelque chose comme ceci: nouvelle chaîne publique Text {get => base.Text; set {if (string.IsNullOrEmpty (value)) {base.Text = this.defaultText; } else {base.Text = valeur; }}} . Il définira le texte sur une valeur par défaut si le texte est une chaîne vide. Cela peut fonctionner, d'une certaine manière. Mais c'est au moins superficiel et sujet à des mauvais comportements inattendus. Je suis d'accord que l'observation n'est pas non plus une bonne idée (même si vous pouvez trouver cela appliqué plusieurs fois dans le code source .Net). D'où le commentaire: apprendre à utiliser les concepteurs personnalisés est la voie à suivre.


En utilisant ce code, j'obtiens l'erreur suivante: Le type ou le nom de l'espace de noms 'ControlDesigner' est introuvable (...) .


Et aussi Une référence d'objet est requise pour le champ non statique, la méthode ou la propriété 'Control.Text' .


Apparemment, j'avais besoin d'ajouter une référence à System.Design ... Quoi qu'il en soit, quand j'essaye d'ajouter une instance de RichTextBoxEx au formulaire en utilisant VS Designer, j'obtiens un hugh message d'erreur, commençant par Impossible de créer le composant 'RichTextBoxEx


@Sipo Ajouter un constructeur vide dans la classe Designer public RichTextBox Designer () {}


@Sipo Eh bien, je l'ai testé (juste pour m'assurer qu'il n'y avait pas quelque chose que je n'ai pas vu en le regardant) et cela fonctionne comme prévu. Aucune erreur d'aucune sorte. Il fonctionne également comme décrit (c'était également prévu :). Avez-vous changé quelque chose, en essayant de le faire fonctionner, par hasard?


@Sipo Lorsque vous utilisez un ControlDesigner personnalisé pour un contrôle, assurez-vous de fermer tous les concepteurs et de nettoyer et reconstruire la solution. (Pas courant, mais dans certains cas rares, vous devez même supprimer le dossier Proj‌ ectAssemblies que vous trouvez sous le dossier Visual Studio sous `% userprofile% \ appdata \ local \ Microsoft \ VisualStudio`. marquez l'adresse quelque part et un jour, cela peut aider.)


Faites-moi savoir si vous rencontrez un problème lors de l'application ou de la vérification de la solution.