11
votes

Win32: Comment personnaliser Personnaliser un contrôle d'édition?

Je dois mettre en œuvre la fonctionnalité de em_setcubebanner , où un L'indice de texte apparaît à l'intérieur d'un contrôle d'édition:

Exemple de bannière de Cue dans le contrôle d'édition

La capture est que je ne peux pas utiliser la version 6 des contrôles communs, ce qui est nécessaire pour obtenir la mise en œuvre de Microsoft fournie d'une bannière Cue.

J'ai examiné simplement la modification du texte de la modification Contrôle et le format de police sur xxx

mais il lancera change événements ( Enraphant de composant fourni par une bibliothèque de composants supérieure ) que je ne trouve pas un moyen d'éviter.

alors j'étais à la place aller à la coutume dessiner le texte, dessinant le texte de la bannière de la Cue lorsque le contrôle est coulé et vide, et s'appuyer sur D efault peinture sinon.

Le contrôle d'édition n'expose pas de manière significative un mécanisme de dessin personnalisé, Comme ListView, TreeView et d'autres personnes fournissent .

D'autres personnes ont examiné ce , mais il semble être une tâche presque impossible:

de la façon dont les choses cherchent, je vais avoir à gérer ce qui suit Messages:

  • wm_erasebkgnd, wm_paint (pour des raisons évidentes)
  • wm_settfocus, wm_killfocus (pour éviter la barre blanche de l'affichage - décrit ci-dessus)
  • wm_char (pour traiter et mettre à jour le texte dans le contrôle)

    Et j'ai aussi besoin de trouver un moyen de afficher le caret dans le contrôle, depuis que je n'ai pas trouvé de moyen d'autoriser Windows pour le faire pour moi sans aussi peindre la barre blanche que j'ai mentionnée.

    Cela va être amusant. : Rolleyes:

    étant donné que le contrôle de Windows Modifier n'a jamais été significatif pour être personnalisé. >: J'accepterai également des réponses qui résolvent mon problème plutôt que de répondre à ma question. Mais toute autre personne qui souhaite personnaliser sur mesure, dessiner un contrôle d'édition, qui survolait cette question, aimerait probablement une réponse.


3 commentaires

Vous pouvez simplement simuler comme je l'ai fait avec la zone de recherche instantanée dans Aero.Controls Bitbucket.org/FactorformeStic/aero .controls


J'avais essayé votre approche avant. J'étais curieux de savoir comment vous avez résolu le problème de dessiner le thème "Searchbox". Mais je vois que vous n'avez pas résolu le problème.


6 ans sur, je dis juste créewindow avec du texte par défaut. Lorsque l'utilisateur clique sur le contrôle vous effacez le texte. Vous pouvez également définir la police sur italique comme dans l'exemple et le changer en clic.


5 Réponses :


3
votes

Sous-classe le contrôle d'édition. Poignée wm_paint en appelant d'abord la procédure de fenêtre d'origine, puis, si elle est vide et non mise au point, dessinez le texte de la cue. Passer tous les autres message à la procédure de fenêtre d'origine.

Je l'ai fait - ça marche. Le problème que la personne de Codeguru n'avait pas semblé s'appliquer à votre situation. Je crois qu'il essaie de faire plus à l'apparence. Pour la performance, il semble que le contrôle d'édition effectue des mises à jour en dehors de wm_paint traitement (probablement pour la performance). Cela va faire presque impossible de prendre un contrôle complet de l'apparence. Mais vous pouvez dessiner l'invite de la queue.


6 commentaires

Le problème est que tout peindre ne se produit pas dans / de / Durng A wm_paint . Parfois, le contrôle modifier se peindre après un wm_killfocus - et no wm_paint message sera envoyé au contrôle (c'est-à-dire qu'il n'est pas invalider lui-même ). Cela signifie que vous devez également gérer WM_KILLFOCUS et déclencher votre propre peinture après cela. Et wm_settfocus et wm_keyup 'et wm_keydown et d'autres événements qui provoquent une peinture sans wm_paint`.


@IanBoyd: J'ai reconnu que le contrôle d'édition triche et peintures à d'autres moments, mais j'ai mis en place le texte de la CUE en utilisant cette technique et cela fonctionne simplement bien.


@AdrianMcCarthy Débutant , une fois la procédure de fenêtre d'origine, une zone rectangle vide vide (puisque elle a manipulé). Appeler beginPaint Avant d'appeler la procédure Windows d'origine ne fonctionnerait pas. Créer mon propre contexte de périphérique provoque un scintillement (sur un événement de clic droit, en particulier) et il semble assez difficile d'optimiser le dessin des chaînes (puisqu'il doit le limiter à mettre à jour sur une zone arbitraire). En bref, je ne peux pas penser à un moyen facile de dessiner la bannière Cue et de ne pas avoir de scintillement; très intéressant cependant :)


@Pooven: "Créer mon propre contexte de périphérique" - je suppose que vous voulez dire getdc . Bummer sur le scintillement. Je ne me souviens pas de voir cela dans ma mise en œuvre.


@AdrianMcCarthy Oui, j'ai utilisé getdc . Le scintillement n'est pas si grave lorsque vous changez de concentration entre les contrôles. C'était principalement causé par les événements de clic droit. La plupart des implémentations éclairent la bannière lorsque le contrôle se concentre; Peut-être que c'était le cas pour vous aussi? Cela pourrait expliquer nos différentes expériences. Merci pour l'idée cependant :)


@AdrianMcCarthy, oui, le sous-classement a également fonctionné pour moi aussi, à l'aide de getdcex au lieu de la portée de la région redessinée à ce qui est nécessaire. Peut-être que cela a contribué à votre problème de scintillement? J'ai envisagé d'éditer votre réponse pour inclure le code, mais puisque je ne savais pas ce que Stackoverflow avait sur l'édition assis dans une file d'attente, j'ai ajouté ma propre réponse avec un extrait de la mise en œuvre.



5
votes

Créer une classe de fenêtre de votre choix qui ressemble à un contrôle d'édition vide et vide, qui dessine le texte de la queue et affiche un caret et axé sur la mise au point. Créez également le contrôle d'édition, mais positionnez-le derrière votre fenêtre. (ou laissez-le caché)

Ensuite, lorsque vous obtenez le premier message WM_CAR (ou WM_KeyDown?). Vous mettez votre fenêtre derrière l'édition Conrol, donnez-vous au centre de modification et passez le message WM_CHAR sur. À partir de ce moment, le contrôle d'édition prendra la relève.

Vous pouvez écouter en_change notifications à partir du contrôle d'édition si vous devez revenir en arrière pour afficher votre texte de Cue lorsque l'édition est vide. Mais je pense que cela irait bien de revenir au texte de Cue uniquement lorsque l'édition perd la mise au point et est vide.


1 commentaires

Approche intéressante au lieu de simplement sous-classement. Il nécessite deux fenêtres et manipulation distinctes sur la commande de superposition plus la fenêtre mère, mais cela fonctionnera. Je me demande quelles fonctions vous utilisez pour dupliquer le cadre et le style nécessaires du contrôle d'édition, y compris des thèmes actifs (themeDrawText?). Il pourrait être plus facile de simplement créer un deuxième contrôle d'édition pour correspondre à l'apparence, puis une autre que vous utilisez réellement pour conserver le contenu, en faisant attention à EN_CHANGE pour les deux dans le parent.



10
votes

Dessin personnalisé Un contrôle d'édition est essentiellement impossible. Il y a quelques cas spécialisés si vous le faites si peu de choses qui peuvent vous échapper, mais vous risquez de vous briser mal dans la prochaine révision de Windows (ou lorsque quelqu'un dirige votre application sur une version plus ancienne, etc.).

Il suffit de reprendre Wm_paint et Wm_erasebkkground ne sont pas assez bons, car le contrôle va parfois peindre sur d'autres messages.

Vous ferez mieux d'écrire votre propre contrôle d'édition. Je sais que c'est une énorme quantité de travail, mais à long terme, ce sera moins de travail que d'essayer de pirater votre chemin pour reprendre tout le code de dessin des commandes d'édition.

Je me souviens de retour dans le bon vieux temps où tout le monde utilise pour sous-classer la commande de bouton pour ajouter de la couleur et des graphiques, etc. La chose est un jour, je me suis assis et vient d'écrire ma classe de fenêtre de boutons. Et c'était moins de code que ce que nous avions dans notre arborescence source à la sous-classe et à la coutume dessinez le bouton Windows.


5 commentaires

Vous semblez avoir raison. Je sous-classe la modification, la manipulation WM_Paint si je veux dessiner le texte de la signature. Il y a des fois (à l'aide de la souris pour sélectionner, repoussant l'espace de retour dans une édition vide) que le contrôle se peint comme vide sans appeler wm_paint. D'autre part, il y a un lot de fonctionnalité existant dans une édition, et je ne peux aucun moyen de justifier tout l'investissement dans le code. (Couper / copier / coller, tampon d'annulation, ime, rtl, etc.)


Oui, de toute façon est d'être un énorme tas de code. Je dis simplement ne pas vous tromper en pensant que le sous-classement avec moins de code. À long terme, ce n'est probablement pas.


J'ai fini par sous-classement la modification, déclenchant une peinture en réponse aux autres messages, pas seulement wm_paint . Quelques autres messages qui provoquent une peinture interne qui se produise, sans aucun WM_Paint pour se produire, et vous oblige à reformer sur leur repeindre: wm_setfocus , wm_killfocus ', wm_keyup , wm_keydown` et lorsque la souris entre et part.


@Ian: Donc, si vous avez fini par sous-classer le contrôle, pourquoi avez-vous sélectionné cette réponse qui suggère d'écrire votre propre contrôle à partir de zéro?


@Adrian McCarthy: Parce que cela ne fonctionne pas pleinement. Vous devez caler le piratage sur Hack sur Hack - et vous ne pouvez toujours pas le faire fonctionner correctement. Peu pire, c'est qu'il est très fragile; Toute modification du contrôle d'édition en dessous et qu'elle peut casser. La sous-classement Un contrôle d'édition ne peut pas être considéré comme une réponse au problème. À meilleur cela pourrait être appelé une solution de contournement. Faire cela la bonne réponse ( essentiellement impossible )



0
votes

Et j'ai aussi besoin de trouver un moyen d'afficher le caret dans le contrôle, depuis que je n'ai pas trouvé de moyen d'autoriser Windows à faire cela pour moi sans peindre également la barre blanche que j'ai mentionnée.

Si vous voulez gérer WM_PAINT par vous-même sans transférer le message à la fenêtre originale de votre superclasse, vous ne devez pas oublier d'appeler DefwindowProc. De sorte que le caret sera tiré. Pour éviter la barre blanche, vous devez enlever la brosse de classe avec SetClassLongpTR. Et assistez d'une manière ou d'une autre, gardez la région de votre clipsage de votre DC pour clipser les sorties ExtTextout d'édition de contrat. La barre blanche peut être le résultat de l'option opaque passée à ExtTextout par le contrôle d'édition.

Conclusion: écrivez votre propre contrôle. Pas de douleur, pas de gain.


0 commentaires

5
votes

Sous-classement Le contrôle d'édition a bien fonctionné pour moi - nécessaire pour afficher certaines informations de formatage à l'utilisateur lors de la modification des attributs d'objet (et certains attributs pourraient être plusieurs lignes). La chose importante, comme Adrian dit dans sa réponse aussi, doit également appeler la procédure de contrôle de la modification avant em> votre propre dessin. Appelez-le après ou émettre votre propre beinture / Endpaint (avec retour 0 ou DEFWINDOWPROC) a causé des problèmes pour moi du texte ne figurant pas du tout, il affichant uniquement lors du redimensionnement, mais pas après l'édition, de quitter la portée de l'écran du restement caret. Avec cela, je n'ai eu aucune question quel que soit les autres moments de la modification du contrôle de la modification.

Certaines configuration: p> xxx pré>

Le rappel: p>

LRESULT CALLBACK AttributeValueEditProcedure(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam,
    UINT_PTR subclassId,
    DWORD_PTR data
    )
{

...

case WM_PRINTCLIENT:
case WM_PAINT:
    {
        auto textLength = GetWindowTextLength(hwnd);
        if (textLength == 0 && GetFocus() != hwnd)
        {
            // Get the needed DC with DCX_INTERSECTUPDATE before the EDIT
            // control's WM_PAINT handler calls BeginPaint/EndPaint, which
            // validates the update rect and would otherwise lead to drawing
            // nothing later because the region is empty. Also, grab it from
            // the cache so we don't mess with the EDIT's DC.
            HDC hdc = (message == WM_PRINTCLIENT)
                ? reinterpret_cast<HDC>(wParam)
                : GetDCEx(hwnd, nullptr, DCX_INTERSECTUPDATE|DCX_CACHE|DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS);

            // Call the EDIT control so that the caret is properly handled,
            // no caret litter left on the screen after tabbing away.
            auto result = DefSubclassProc(hwnd, message, wParam, lParam);

            // Get the font and margin so the cue banner text has a
            // consistent appearance and placement with existing text.
            HFONT font = GetWindowFont(hwnd);
            RECT editRect;
            Edit_GetRect(hwnd, OUT &editRect);

            // Ideally we would call Edit_GetCueBannerText, but since that message
            // returns nothing when ES_MULTILINE, use a window property instead.
            auto* cueBannerText = reinterpret_cast<wchar_t*>(GetProp(hwnd, L"CueBannerText"));

            HFONT previousFont = SelectFont(hdc, font);
            SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
            SetBkMode(hdc, TRANSPARENT);
            DrawText(hdc, cueBannerText, int(wcslen(cueBannerText)), &editRect, DT_TOP|DT_LEFT|DT_NOPREFIX|DT_NOCLIP);
            SelectFont(hdc, previousFont);

            ReleaseDC(hwnd, hdc);

            // Return the EDIT's result (could probably safely just return zero here,
            // but seems safer to relay whatever value came from the edit).
            return result;
        }
    }
    break;


0 commentaires