Ci-dessous, les différences entre deux révisions de la page.
Prochaine révision | Révision précédente | ||
fr:data_grid [2012/05/16 08:42] navy57 créée |
fr:data_grid [2012/10/09 09:20] (Version actuelle) darcybarron [Anatomie d'un contrôle personnalisé (grille de données simple)] |
||
---|---|---|---|
Ligne 2: | Ligne 2: | ||
--- //David MacDermot 2008/09/17 19:55// | --- //David MacDermot 2008/09/17 19:55// | ||
- | Cet article décrit comment créer un contrôle personnalisé dans Pelles C. Le contrôle dans ce cas est une grille de données, utilisant comme composant un affichage de liste "listview" et une zone d'édition "editbox" avec un certain nombre de personnalisations pour améliorer l'apparence du contrôle. | + | Cet article décrit comment créer un contrôle personnalisé dans Pelles C. Le contrôle dans ce cas est une grille de données, utilisant comme composant un affichage de liste "listview" et une zone d'édition "editbox" avec un certain nombre de personnalisations pour améliorer l'apparence du contrôle. [[http://termpaper.biz/buy-paper-online.php|buy admission essay]] |
- | ===== Project ===== | + | |
- | {{:data_grid:sdkdatagridview.zip|Project Source and Demo}} | + | ===== Projet ===== |
+ | {{:data_grid:sdkdatagridview.zip|.zip Source de projet et Démo}} | ||
Ligne 11: | Ligne 12: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | The Windows SDK provides a selection of user interface controls for application development that covers most of the bases when it comes to user interface design. Sooner or later however, you run into a situation where you wish there was something else in the toolbox that isn’t there. A search of the web reveals a number of clever solutions but most of them use MFC or another of the modern object oriented platforms and they don’t translate well into Pelles C. More often than not you are going to have to create your own control from scratch. | + | Le SDK de Windows fournit une sélection de contrôles d'interface utilisateur pour le développement d'applications qui couvre la plupart des bases lorsqu'il s'agit de conception d'interface utilisateur. Tôt ou tard,vous vous trouverez dans une situation ou vous souhaiteriez avoir certain éléments dans la boîte à outils qui n'y figure pas. Une recherche sur le web révèle un certain nombre de solutions intelligentes, mais la plupart d'entre eux utilisent des plates-formes MFC ou autre orientations d'objets modernes,qui se traduisent mal en Pelles C. Le plus souvent vous allez devoir créer votre propre contrôle à partir de zéro.La conception de votre propre contrôle ne doit pas être une affaire difficile |
- | The good news is rolling your own control does not have to be a difficult proposition. | + | |
- | ===== The windows class ===== | + | ===== La classe des fenêtres ===== |
- | The windows class describes common properties of all windows that will be created using that class. We’ll use the Windows class to encapsulate the methods associated with our custom control and also to provide the public interface for our control. The public interface for our control will be message based so there is only one public method that we create in order to register the windows class. This method is called only once to register the class. Instances of the control are created using **CreateWindow()**. | + | La classe décrit les propriétés communes des fenêtres de toutes les fenêtres qui seront créés à l'aide de cette classe. Nous allons utiliser la classe Windows pour encapsuler les méthodes associées à notre contrôle personnalisé et aussi pour fournir l'interface publique pour notre contrôle. L'interface publique pour notre contrôle sera le message de base de sorte qu'il n'existe qu'une seule méthode publique que nous créons dans le but d'enregistrer la classe des fenêtres. Cette méthode est appelée une seule fois pour enregistrer la classe. Les instances de contrôle sont créés en utilisant **CreateWindow()**. |
<code c>ATOM InitDataGridView(HINSTANCE hInstance) | <code c>ATOM InitDataGridView(HINSTANCE hInstance) | ||
Ligne 41: | Ligne 42: | ||
}</code> | }</code> | ||
- | This method should be called in the application's entry point procedure immediately following the calls to initialize common controls. | + | Cette méthode doit être appelée dans la procédure de l'application de point d'entrée, immédiatement après les appels vers l'initialisation des contrôles communs. |
<code c>int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) | <code c>int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) | ||
Ligne 61: | Ligne 62: | ||
</code> | </code> | ||
- | ===== The callback procedure ===== | + | ===== La procédure de rappel ===== |
- | This is where we’ll support the public interface to our control as well as handle the standard windows messages that the operating system sends to our control’s window. | + | C'est là que nous allons fournir l'interface publique à notre contrôle, ainsi que gérer les messages standard de Windows que le système d'exploitation envoie vers la fenêtre de notre contrôle. |
- | Callback procedures can become quite lengthy and difficult to maintain in a complex control. Windows provides a series of macros in **windowsx.h** and **comctrl.h** that greatly enhance code readability and help to break out code into manageable event driven method chunks. These macros are referred to as message crackers and there are some tools that make implementing them fairly easy. I used the [[http://www.codeproject.com/KB/winsdk/msgcrackwizard.aspx|Message Cracker Wizard]] to generate the macros and method prototypes for the standard windows messages, greatly reducing the size of the callback procedure. | + | Les procédures de rappel peuvent devenir très longue et difficile à maintenir dans un contrôle complexe. Windows fournit une série de macros dans ** windowsx.h ** et ** comctrl.h ** afin d'améliorer grandement la lisibilité du code et d'aider à sortir le code en blocs d'événements gérables axés sur la méthode. Ces macros sont considérés comme des crackers de message et il existe des outils qui rendent leur mise en œuvre assez facile. J'ai utilisé [[http://www.codeproject.com/KB/winsdk/msgcrackwizard.aspx|Message Cracker Wizard]] pour générer les macros et les prototypes de méthode pour les messages standard de Windows, ce qui réduit considérablement la taille de la procédure de rappel. |
- | Most of the lines of code in this section are devoted to the control’s own custom messages and indeed mimic the property getters and setters common to C++. | + | La plupart des lignes de code dans cette section sont consacrés aux propres du contrôle des messages personnalisés et même imiter les acquéreurs et mutateurs de propriété communs à C++. |
<code c>static LRESULT CALLBACK Grid_WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | <code c>static LRESULT CALLBACK Grid_WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | ||
Ligne 113: | Ligne 114: | ||
</code> | </code> | ||
- | ===== The public interface ===== | + | ===== L'interface publique ===== |
- | We’ll define the public interface in the control’s header file. | + | Nous allons définir l'en-tête de l'interface publique dans le fichier du contrôle. |
- | Windows reserves a block of messages for custom controls beginning with the definition of **WM_USER** so our messages will be **WM_USER** + some value we assign. Messages are sent to a control via **SendMessage()** which gives us two parameters that we can use for passing values to the control’s callback procedure. We can also obtain the callback return value from **SendMessage()**. I find it helpful to define macros for each message as I define the messages themselves. This serves as a way of documenting each message in the code of the header file during development and provides a syntactically pleasing interface for accessing the control’s properties in source code. | + | Windows réserve un bloc de messages pour les contrôles personnalisés en commençant par la définition **WM_USER** afin que nos messages soient **WM_USER**+, nous allons assigner. une certaine valeur. Les messages sont envoyés à un contrôle par l'intermédiaire **SendMessage()**.ce qui nous donne deux paramètres que nous pourrons utiliser pour passer des valeurs à la procédure de rappel du contrôle. On peut aussi obtenir la valeur de retour de rappel à partir de **SendMessage()** . Je trouve qu'il est utile de définir des macros pour chaque message que de définir les messages eux-mêmes. Cela constitue un moyen de documenter chaque message dans le code du fichier d'en-tête au cours du développement et fournit une interface agréable syntaxiquement pour accéder aux propriétés du contrôle dans le code source. |
- | In addition to the message macros I have also defined some helper macros to make it easier to add columns and rows to our control. Notice that these macros do not contain references to our custom messages! In the control’s callback procedure any listview specific message is forwarded to the grid control’s listview component. | + | En plus des macros de message j'ai également défini quelques macros d'assistance pour rendre plus facile l'ajout de colonnes et de lignes à notre contrôle. Notez que ces macros ne contiennent aucune références à notre messages personnalisés! Dans la procédure de rappel du contrôle toute les messages spécifiques listview sont transmis à la composante du contrôle de grille de listview. |
<code c>/****************************************************************************/ | <code c>/****************************************************************************/ | ||
Ligne 182: | Ligne 183: | ||
/****************************************************************************/ | /****************************************************************************/ | ||
- | // Exported function prototypes | + | // Prototypes de fonctions exportées |
BOOL InitDataGridView(HINSTANCE hInstance); | BOOL InitDataGridView(HINSTANCE hInstance); | ||
</code> | </code> | ||
- | ===== Keeping track of multiple instances ===== | + | ===== Assurer le suivi de plusieurs instances ===== |
- | I use a struct to persist properties of each instance of the custom control. | + | J'utilise une struct pour persister des propriétés de chaque instance du contrôle personnalisé. |
<code c>typedef struct _tagINSTANCEDATA{ | <code c>typedef struct _tagINSTANCEDATA{ | ||
Ligne 209: | Ligne 210: | ||
</code> | </code> | ||
- | In addition to this there are some standard methods that I use to manage the creation and assignment of, access to, and destruction of this structure. | + | En plus de cela, il ya certaines méthodes standard que j'utilise pour gérer la création et l'attribution de .access to, et la destruction de cette structure. |
<code c>static BOOL Control_GetInstanceData (HWND hControl, LPINSTANCEDATA *ppInstanceData) | <code c>static BOOL Control_GetInstanceData (HWND hControl, LPINSTANCEDATA *ppInstanceData) | ||
Ligne 238: | Ligne 239: | ||
</code> | </code> | ||
- | Here I create and assign the structure in the WM_CREATE handler of the custom control | + | Ici, je créer et j’attribue la structure dans le gestionnaire WM_CREATE du contrôle personnalisé |
<code c> static BOOL Grid_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) | <code c> static BOOL Grid_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) | ||
Ligne 249: | Ligne 250: | ||
memset(&inst.vsInfo, 0, sizeof(SCROLLINFO)); | memset(&inst.vsInfo, 0, sizeof(SCROLLINFO)); | ||
- | // get the hInstance | + | // aller à hInstance |
inst.hInstance = lpCreateStruct->hInstance; | inst.hInstance = lpCreateStruct->hInstance; | ||
- | // create the ListView control | + | // créer le contrôle ListView |
inst.hwndList = CreateListView(lpCreateStruct->hInstance, hwnd); | inst.hwndList = CreateListView(lpCreateStruct->hInstance, hwnd); | ||
if(NULL == inst.hwndList) return FALSE; | if(NULL == inst.hwndList) return FALSE; | ||
- | // default ListView Colors | + | // Couleurs par défaut de la ListView |
inst.fPaintByRow = TRUE; | inst.fPaintByRow = TRUE; | ||
inst.Alt_TxtColr = ListView_GetTextColor(inst.hwndList); | inst.Alt_TxtColr = ListView_GetTextColor(inst.hwndList); | ||
inst.Alt_BkColr = ListView_GetBkColor(inst.hwndList); | inst.Alt_BkColr = ListView_GetBkColor(inst.hwndList); | ||
- | // default ListView pseudoHeaders off | + | // ListView pseudoHeaders off par défaut |
inst.fRowHeaders = FALSE; | inst.fRowHeaders = FALSE; | ||
Ligne 267: | Ligne 268: | ||
if(NULL == inst.hwndEditor) return FALSE; | if(NULL == inst.hwndEditor) return FALSE; | ||
- | // default Cell Editor Colors | + | // Couleurs par défaut de la cellule d'édition |
inst.Editor_BkColr = RGB(0,0,160); | inst.Editor_BkColr = RGB(0,0,160); | ||
inst.Editor_TxtColr = RGB(255,255,255); | inst.Editor_TxtColr = RGB(255,255,255); | ||
- | // default Cell selection behavior | + | // Comportement de la sélection par défaut de la cellule |
inst.fDblClick = FALSE; | inst.fDblClick = FALSE; | ||
Ligne 282: | Ligne 283: | ||
</code> | </code> | ||
- | Here I set a global pointer to the struct associated with this instance of the control and then free the structure when it is no longer needed. | + | Ici j'ai mis un pointeur vers la structure globale associée à cette instance du contrôle et alors libre de la structure quand il n'est plus nécessaire. |
<code c> static VOID Grid_OnDestroy(HWND hwnd) | <code c> static VOID Grid_OnDestroy(HWND hwnd) | ||
Ligne 296: | Ligne 297: | ||
</code> | </code> | ||
- | ===== Points of interest ===== | + | ===== Points d'intérêt ===== |
- | ====Custom drawing the listview ==== | + | |
+ | ====Dessin personnalisé d'une liste ==== | ||
- | All of the customization and drawing, of the grid’s components, is handled in the NM_CUSTOMDRAW notification of the list view, this includes skinning the header, something that normally one cannot do, but is possible because the header is subclassed. For a detailed tutorial about simple customizations using this method see [[http://www.codeproject.com/KB/list/lvcustomdraw.aspx|Neat Stuff to Do in List Controls Using Custom Draw]]. | + | Toutes les personnalisations et dessins des composants de la grille, sont gérées dans la notification NM_CUSTOMDRAW de la liste, ce qui inclut le dépouillement l'en-tête, quelque chose qui normalement ne peut pas ce faire, mais il est possible que parce que l'en-tête est sous-classée. Pour un tutoriel détaillé sur les personnalisations simples en utilisant cette méthode voir [[http://www.codeproject.com/KB/list/lvcustomdraw.aspx|Trucs sympa à faire dans la liste des contrôles à l'aide dessin personnalisé]]. |
<code c>LRESULT Grid_OnCustomDraw (HWND hwnd, LPNMLVCUSTOMDRAW lplvcd) | <code c>LRESULT Grid_OnCustomDraw (HWND hwnd, LPNMLVCUSTOMDRAW lplvcd) | ||
Ligne 410: | Ligne 412: | ||
</code> | </code> | ||
- | ====Getting the column count ==== | + | ====Obtenir le nombre de colonnes ==== |
- | The listview has a method for getting the item count (row count) but does not provide a method for getting column count. It is therefore necessary to provide an indirect way of determining column count. One method would be to get an item count from the listview’s header but this does not work if the header is not visible yet. The following is a method that works under any circumstances. | + | La listview a une méthode pour obtenir le nombre d'éléments (nombre de lignes), mais ne fournit pas une méthode pour obtenir le nombre de colonnes. Il est donc nécessaire de prévoir un moyen indirect pour déterminer le nombre de colonnes. Une méthode serait d'obtenir un nombre d'éléments de tête de la listview, mais cela ne fonctionne pas si l'en-tête n'est pas encore visible. Ce qui suit est une méthode qui fonctionne en toutes circonstances. |
<code c>static INT ListView_GetColumnCount(HWND hwnd) | <code c>static INT ListView_GetColumnCount(HWND hwnd) | ||
Ligne 441: | Ligne 443: | ||
</code> | </code> | ||
- | ====Positioning the editor on resize ==== | + | ====Positionnement de l'éditeur sur redimensionnement ==== |
- | Resizing a column is fairly straight forward. But what happens to the edit control that I am positioning over the grid during a resize? It must resize and position accordingly. This is hard to do real time but with some smoke and mirrors I was able to make it look like that is just what is happening. | + | Le redimensionnement d'une colonne est assez simple. Mais ce qui se passe au contrôle d'édition que je suis le positionnement sur la grille au cours d'une de redimensionnement? Il faut redimensionner et positionner en conséquence. C'est difficile à faire en temps réel mais avec un peu de fumée et de miroirs, j'ai pu lui donner l'air comme ça, c'est juste ce qui se passe. |
- | The listview's header posts an HDN_ITEMCHANGING notification that signals a resize has begun. I hide the edit control when this happens but the underlying subitem is painted the same colors as the edit control so it looks like the edit control is stretching real-time. All I need to do when the resize is complete is reposition the edit control and show it again. Unfortunately there is no direct way to determine that a resize is complete. | + | Les messages d'en-tête de ListView une notification HDN_ITEMCHANGING qui signale un redimensionnement a commencé. Je cache le contrôle d'édition lorsque cela se produit, mais le sous-menu sous-jacente est peint de la même couleur que le contrôle d'édition de sorte qu'il ressemble le contrôle d'édition est l'étirement en temps réel. Tout ce que je besoin de faire lorsque le redimensionnement est terminée sont de repositionner le contrôle d'édition et de le montrer encore. Malheureusement, il n'existe aucun moyen direct pour déterminer si un redimensionnement est terminée |
- | I must confess that this one had me stumped for a while. I played with the list view and observed closely what was happening during a resize. | + | Je dois avouer que celui-ci m'a laissé bouche bée pendant un certain temps. J'ai joué avec l'affichage de la liste et observer de près ce qui se passait au cours d'une de redimensionnement. |
* Mouse down on header | * Mouse down on header | ||
Ligne 505: | Ligne 507: | ||
</code> | </code> | ||
- | ====Scrolling==== | + | ====Défilement==== |
- | Getting the scrolling to work right in this control was a challenge. I would resize the grid columns so that the horizontal scrollbar appeared, scroll over, then resize the columns back to the initial size. This caused the scrollbar to disappear leaving the view shifted and some of the control not visible. | + | Obtenir le fonctionnement du contrôle du défilement en plein écrand fut un défi. Je voudrais redimensionner les colonnes de la grille de sorte que la barre de défilement horizontale apparaît, faires défiler l'écran, puis redimensionner les colonnes pour retourner à la taille initiale. Cela a provoqué la disparition de la barre de défilement en laissant le point de vue décalé et une partie du contrôle n'est plus visible. |
- | The solution was to persist the vertical and horizontal scroll info in the control so that it wasn't lost when the bars vanished. In the case of horizontal scrolling, the control pans to the origin if the size of the listview reduces to fit within the parent window. | + | La solution est de conserver l'information verticale et horizontale de défilement dans le contrôle de sorte qu'il n'a pas été perdu lorsque les barres disparu. Dans le cas de défilement horizontal, le contrôle casseroles à l'origine, si la taille de la liste réduit pour tenir dans la fenêtre apparentés. |
- | Vertical scrolling units reflect row height and horizontal units are set by the width of the first visible column. I find that this gives the control the smoothest scrolling behavior while avoiding widowed or orphaned rows. Especially when navigating the control from the keyboard. | + | Unités de défilement verticales reflètent la hauteur des lignes et des unités horizontales qui sont fixées par la largeur de la première colonne visible. Je trouve que cela donne le contrôle du comportement plus doux de défilement tout en évitant les lignes veuves ou orphelins. Surtout lorsque vous naviguez dans le contrôle à partir du clavier. |
- | Horizontal scrolling: | + | Le défilement horizontale: |
<code c>void Grid_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos) | <code c>void Grid_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos) | ||
Ligne 573: | Ligne 575: | ||
</code> | </code> | ||
- | Vertical scrolling: | + | Le défilement vertical: |
<code c>static VOID Grid_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, INT pos) | <code c>static VOID Grid_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, INT pos) | ||
Ligne 640: | Ligne 642: | ||
</code> | </code> | ||
- | Calling the scroll events from the ListView_KeyProc so the edit box stays in view | + | Appel des événements de défilement de la ListView_KeyProc de sorte que la zone d'édition reste en vue |
<code c>static LRESULT CALLBACK ListView_KeyProc (HWND hList, UINT msg, WPARAM wParam, LPARAM lParam) | <code c>static LRESULT CALLBACK ListView_KeyProc (HWND hList, UINT msg, WPARAM wParam, LPARAM lParam) | ||
{ | { | ||
- | // Note: Instance data is attached to ListView's parent | + | // Remarque: Les données d'instance sont attachées à une ListView aparentée |
static RECT rc, rcParent; | static RECT rc, rcParent; | ||
static char buf[2048]; | static char buf[2048]; |