MFC/C++ Helper Class for Window Resizing
![]() |
![]() |
Introduction
In MFC, resizing or repositioning controls could be quite bothersome. If you are familiar with the .NET platform, things are much more simplified with the use of the Anchor
and Dock
properties of the Control
class and design-time support for adding child controls to container controls. I tried to mimic some of these features of .NET, but the C++ way.
Background
There are other solutions available online (also on CodeProject) for this purpose. I think my solution stands out on its design, simplicity, and feature richness.
This solution allows you to do the following:
- Anchor a dialog item using the
Anchor
property. - Create panels which contain other panels or UI controls.
- Restrict the size of UI controls or a panel by using minimum size and maximum size properties.
- Restrict the size of the dialog by using minimum size and maximum size properties.
- Create a horizontal or vertical split container.
- Freeze the splitter of split container so that users cannot use mouse to resize the panels.
- Set a panel to be fixed of a split container so that when the parent resizes only one of the non-fixed panel resizes.
- Set the parent of a UI control or a panel (not related to Win32
SetParent
API). - Show the resize grip on the lower right corner of the dialog using Visual Style (if the application supports it).
Let's take a brief look at how .NET does its resizing of child controls:
Anchor
- It allows a child control to be anchored to the left, top, right, or bottom edge or any combination of these four options.Dock
- It allows a child control to be docked to the left, top, right, and bottom edges.- Visual Studio Designer Support - If you place a frame control on a Windows Form and then drag a button control on top of the frame, the button becomes the actual child of the frame and grandchild of the Windows Form. But in the resource editor of MFC, if you place the frame on the dialog template and then drag a button on top of the frame control, the button is actually an immediate child of the dialog template and not the frame. This means if you move the frame, the button will not move.
SplitContainer
- Creating splitter windows has never been this simple since the invention of this control. It has two panels which can host other controls inside.
So in .NET, all controls are children or grandchildren of the WindowForm
; this creates a hierarchical structure of controls. When a parent is resized or repositioned, all its children are resized or repositioned according to their Anchor
or Dock
property settings.
In my solution, I have implemented the Anchor
, Panel
, and SplitContainer
concept. But I have not implemented the "Dock
" feature yet (may be in the future).
There are several classes in this solution, but CWndResizer
is the only class that a developer will work with.
Typically, you will design your dialog template in the Visual Studio Resource Editor, and then in the dialog class implementation, you will have a member variable like this:
private:
CWndResizer m_resizer;
The samples included with this article uses CDialog
class to demonstrate many features of this class. But this class can be used with any class derived from CWnd
(CDialog
, CPropertyPage
, CPropertySheet
, CFrmWnd
, CFormView
)
Before this class can do anything, you must call the Hook
method like this:
BOOL CExample1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL bOk = m_resizer.Hook(this);
ASSERT(bOk == TRUE);
}
In this article, I will refer to this window (that is passed to the Hook
method) as "hooked-window".
By calling this method, it places a window procedure hook in the WndProc
chain.
When you call the Hook
method, it stores the client area of the hooked-window in a structure called CPanel
. A panel is mainly a rectangle area given in client coordinate of the hooked-window. A panel can have zero or more panels as children. During creation of a panel, you assign a unique name for the panel. The name is used to refer to the panel or find a panel. The client area of the hooked-window is the root of the hierarchy and it is named "_root"
.
Each panel has Anchor
, MinSize
, MaxSize
properties (along with some other properties). But you cannot directly set or get the properties of a panel; instead, you will use member methods of the CWndResizer
class.
The idea is that when a CPanel
is resized or repositioned, all its children are also resized and reposition relatively.
Now, let's look at some code snippets.
The following code will anchor the OK and Cancel buttons of your dialog to the bottom-right corner:
BOOL CExample1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL bOk = m_resizer.Hook(this);
ASSERT(bOk == TRUE);
bOk = m_resizer.SetAnchor(IDOK, ANCHOR_RIGHT | ANCHOR_BOTTOM);
ASSERT(bOk == TRUE);
bOk = m_resizer.SetAnchor(IDCANCEL, ANCHOR_RIGHT | ANCHOR_BOTTOM);
ASSERT(bOk == TRUE);
}
If you want to create a panel and set its Anchor
property to ANCHOR_HORIZONTALLY
, you will do this:
BOOL CExample1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL bOk = m_resizer.Hook(this);
ASSERT(bOk == TRUE);
CRect rc(40, 40, 240, 240);
bOk = m_resizer.CreatePanel(_T("MyNewPanel"), &rc);
bOk = m_resizer.SetAnchor(_T("MyNewPanel"), ANCHOR_HORIZONTALLY);
ASSERT(bOk == TRUE);
ASSERT(bOk == TRUE);
}
Using the Code
The sample application demonstrates many features of this class.
The CWndResizer
class has the following members:
- CreatePanel
- CreateSplitContainer
- GetAnchor
- GetFixedPanel
- GetIsSplitterFixed
- GetMaximumSize
- GetMinimumSize
- GetParent
- GetShowResizeGrip
- Hook
- InvokeOnResized
- SetAnchor
- SetFixedPanel
- SetIsSplitterFixed
- SetMaximumSize
- SetMinimumSize
- SetParent
- SetShowResizeGrip
- Unhook
Method |
BOOL CreatePanel(LPCTSTR panelName, const CRect * prcPanel);
|
|||||||||||||||||||||||
Description | Creates a panel in the client area of the hooked-window. | |||||||||||||||||||||||
Parameters | panelName |
An unique name for this panel. This name is used to refer to this panel later on. | ||||||||||||||||||||||
prcPanel |
A rectangle given in hooked-window's client coordinate. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
-or-
|
|||||||||||||||||||||||
Remarks | Initially, the parent of this panel would be the "_root" , which is the hooked window. You can change the parent by calling the CWndResizer::SetParent method. You can also set its Anchor property by calling CWndResier::SetAnchor . Initially, the Anchor value is ANCHOR_LEFT | ANCHOR_TOP .
BOOL CExample1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL bOk = m_resizer.Hook(this);
ASSERT(bOk == TRUE);
CRect rc(40, 40, 240, 240);
bOk = m_resizer.CreatePanel(_T("MyNewPanel"), &rc);
ASSERT(bOk == TRUE);
// Subsequently you can use the name to refer to it
bOk = m_resizer.SetAnchor(_T("MyNewPanel"), ANCHOR_HORIZONTALLY);
ASSERT(bOk == TRUE);
}
|
|||||||||||||||||||||||
Method |
BOOL CreateSplitContainer(LPCTSTR splitContainerName,
LPCTSTR panelNameA, LPCTSTR panelNameB);
BOOL CreateSplitContainer(LPCTSTR splitContainerName,
LPCTSTR panelNameA, UINT panelIDB);
BOOL CreateSplitContainer(LPCTSTR splitContainerName,
UINT panelIDA, LPCTSTR panelNameB);
BOOL CreateSplitContainer(LPCTSTR splitContainerName,
UINT panelIDA, UINT panelIDB);
|
|||||||||||||||||||||||
Description | Creates a split container. (.NET analogy). | |||||||||||||||||||||||
Parameters | splitContainerName |
An unique name for this split container. This name is used to refer to this panel later on. | ||||||||||||||||||||||
panelNameA |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel or CWndResizer::CreateSplitContainer .
If the resulting panel is a horizontal split container, this refers to the left panel of the split container. Otherwise, it is the top panel of the split container. |
|||||||||||||||||||||||
panelIDA |
An ID of a child control (window) of the hooked-window.
If the resulting panel is a horizontal split container, this refers to the left panel of the split container. Otherwise, it is the top panel of the split container. |
|||||||||||||||||||||||
panelNameB |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel or CWndResizer::CreateSplitContainer .
If the resulting panel is a horizontal split container, this refers to the right panel of the split container. Otherwise, it is the bottom panel of the split container. |
|||||||||||||||||||||||
panelIDB |
An ID of a child control (window) of the hooked-window.
If the resulting panel is a horizontal split container, this refers to the right panel of the split container. Otherwise, it is the bottom panel of the split container |
|||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
-or-
-or- The specified panels (rectangle) overlapped. -or- One or all of the specified panels have already been used to create split container. |
|||||||||||||||||||||||
Remarks | Initially, the parent of this panel would be the "_root" , which is the hooked window. You can change the parent by calling the CWndResizer::SetParent function. You can also set its Anchor property by calling CWndResier::SetAnchor . Initially, its Anchor value is ANCHOR_LEFT | ANCHOR_TOP .
If the If you move your mouse over the space between the two panels that created the splitter, the mouse pointer will change. Then you can press down the left mouse button and drag to resize the panels. If the parent of the panel is resized, the two panels of the splitter panel will resize proportionately. |
|||||||||||||||||||||||
Method |
BOOL GetAnchor(LPCTSTR panelName, UINT & anchor);
BOOL GetAnchor(UINT panelID, UINT & anchor);
|
|||||||||||||||||||||||
Description | Gets the anchor of the specified panel. (.NET analogy). | |||||||||||||||||||||||
Parameters | panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
||||||||||||||||||||||
panelID |
An ID of a child control (window) of the hooked-window. | |||||||||||||||||||||||
anchor |
Receive the anchor value. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. anchor contains valid value. |
||||||||||||||||||||||
FALSE |
Failed. anchor value should be ignored.
-or-
|
|||||||||||||||||||||||
Remarks | Retrieves the anchor value of a panel. See SetAnchor for details. |
|||||||||||||||||||||||
Method |
BOOL GetFixedPanel(LPCTSTR splitContainer, short & panel);
|
|||||||||||||||||||||||
Description | Gets the name of the panel that is fixed (if any). (.NET analogy). | |||||||||||||||||||||||
Parameters | splitContainerName |
Name of split container that was created by a prior call to CWndResizer::CreateSplitContainer . |
||||||||||||||||||||||
panel |
Receives the ID of the fixed panel. If this contains 1, then the left panel (or the top panel if splitContainer is a vertical split container) is fixed, or if it is 2 then the right panel (or the bottom panel if splitContainer is a vertical split container). Otherwise, splitContainer has no fixed panel. |
|||||||||||||||||||||||
Return values | TRUE |
Succeeded. panel contains valid value. |
||||||||||||||||||||||
FALSE |
Failed. panel value should be ignored.
-or-
|
|||||||||||||||||||||||
Remarks | Retrieves the fixed panel ID of a a split container. See SetSetFixedPanel for details. |
|||||||||||||||||||||||
Method |
BOOL GetIsSplitterFixed(LPCTSTR splitContainerName , BOOL &fixed);
|
|||||||||||||||||||||||
Description | Gets whether or not splitter is set to fixed for a split container. (.NET analogy). | |||||||||||||||||||||||
Parameters | splitContainerName |
Name of split container that was created by a prior call to CWndResizer::CreateSplitContainer . |
||||||||||||||||||||||
fixed |
Receives a boolean value. If this is TRUE , then the splitter is fixed, otherwise the splitter is free to be moved by mouse. |
|||||||||||||||||||||||
Return values | TRUE |
Succeeded. fixed contains valid value. |
||||||||||||||||||||||
FALSE |
Failed. fixed value should be ignored.
-or-
|
|||||||||||||||||||||||
Remarks | Retrieves the a flag that indicates whether or not the splitter is free to be moved my mouse for a split container. See SetIsSplitterFixed for details. |
|||||||||||||||||||||||
Method |
BOOL GetMaximumSize(LPCTSTR panelName, CSize & size) ;
BOOL GetMaximumSize(UINT panelID, CSize & size) ;
|
|||||||||||||||||||||||
Description | Gets the maximum size of the specified panel. (.NET analogy). | |||||||||||||||||||||||
Parameters | panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
||||||||||||||||||||||
panelID |
An ID of a child control (window) of the hooked-window. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. size contains valid value. |
||||||||||||||||||||||
FALSE |
Failed. size value should be ignored.
-or-
|
|||||||||||||||||||||||
Remarks | If panelName or panelID refers to a panel that is part of horizontal split container, then CSize::cy member should be ignored. If panelName or panelID refers to a panel that is part of vertical split container, then CSize::cx member should be ignored. |
|||||||||||||||||||||||
Method |
BOOL GetMinimumSize(LPCTSTR panelName, CSize & size) ;
BOOL GetMinimumSize(UINT panelID, CSize & size) ;
|
|||||||||||||||||||||||
Description | Gets the minimum size of the specified panel. (.NET analogy). | |||||||||||||||||||||||
Parameters | panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
||||||||||||||||||||||
panelID |
An ID of a child control (window) of the hooked-window. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. size contains valid value. |
||||||||||||||||||||||
FALSE |
Failed. size value should be ignored.
-or-
|
|||||||||||||||||||||||
Remarks | If panelName or panelID refers to a panel that is part of horizontal split container, then CSize::cy member should be ignored. If panelName or panelID refers to a panel that is part of vertical split container, then CSize::cx member should be ignored. |
|||||||||||||||||||||||
Method |
BOOL GetParent(LPCTSTR panelName, CString & parentName);
BOOL GetParent(UINT panelID, CString & parentName);
|
|||||||||||||||||||||||
Description | Gets the name of the parent of the specified panel. | |||||||||||||||||||||||
Parameters | panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
||||||||||||||||||||||
panelID |
An ID of a child control (window) of the hooked-window. | |||||||||||||||||||||||
parentName |
Receives the name of the parent. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. parentName contains valid value. |
||||||||||||||||||||||
FALSE |
Failed. size value should be ignored.
-or-
|
|||||||||||||||||||||||
Remarks | If parentName is "_root" , it indicates that the panel is immediate child of the hooked-window. |
|||||||||||||||||||||||
Method |
BOOL GetShowResizeGrip();
|
|||||||||||||||||||||||
Description | Gets a flag indication if a resize grip will be drawn on the lower-right corner of the hooked-window. | |||||||||||||||||||||||
Parameters | N/A |
No parameters. | ||||||||||||||||||||||
Return values | TRUE |
It will draw a resize grip at the bottom-right corner of the dialog/window. | ||||||||||||||||||||||
FALSE |
It will NOT draw a resize grip at the bottom-right corner of the dialog/window. | |||||||||||||||||||||||
Remarks | This method never fails. | |||||||||||||||||||||||
Method |
BOOL Hook(CWnd * pWnd);
|
|||||||||||||||||||||||
Description | ||||||||||||||||||||||||
Parameters | pWnd |
A pointer to a CWnd that you want to resize and its child controls. |
||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
-or- This method has already been called once. |
|||||||||||||||||||||||
Remarks | You must call this method before calling any other methods.
The window that the |
|||||||||||||||||||||||
Method |
BOOL InvokeOnResized();
|
|||||||||||||||||||||||
Description | Simulates resizing of the hooked-window. | |||||||||||||||||||||||
Parameters | N/A |
No parameters. | ||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
|
|||||||||||||||||||||||
Remarks | This method sends a WM_SIZE message to hooked-window.
After creating necessary panels and settings anchors, you would want to call this to apply settings initially. |
|||||||||||||||||||||||
Method |
BOOL SetAnchor(LPCTSTR panelName, UINT & anchor);
BOOL SetAnchor(UINT panelID, UINT & anchor);
|
|||||||||||||||||||||||
Description | Sets the anchor of the specified panel. (.NET analogy). | |||||||||||||||||||||||
Parameters | panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
||||||||||||||||||||||
panelID |
An ID of a child control (window) of the hooked-window. | |||||||||||||||||||||||
anchor |
Anchor value. See remarks. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. anchor contains valid value. |
||||||||||||||||||||||
FALSE |
Failed.
-or-
|
|||||||||||||||||||||||
Remarks | anchor could be one or more of the following:
Note: A panel will never grow less than its allowed minimum size and more than its allowed maximum size. |
|||||||||||||||||||||||
Method |
BOOL SetFixedPanel(LPCTSTR splitContainerName, short panel);
|
|||||||||||||||||||||||
Description | Sets one of the panel of a split container to be fixed. (.NET analogy). | |||||||||||||||||||||||
Parameters | splitContainerName |
Name of a panel that was created by a prior call to CWndResizer::CreateSplitContainer . |
||||||||||||||||||||||
panel |
ID of the fixed panel. If this contains 1, then the left panel (or the top panel if splitContainer is a vertical split container) is fixed, or if it is 2 then the right panel (or the bottom panel if splitContainer is a vertical split container). Otherwise, splitContainer has no fixed panel. |
|||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
|
|||||||||||||||||||||||
Remarks | As hooked-window is resizes, the fixed panel remain same and the other panel resizes.
Note: A panel will never grow less than its allowed minimum size and more than its allowed maximum size. |
|||||||||||||||||||||||
Method |
BOOL SetIsSplitterFixed(LPCTSTR splitContainerName , BOOL fixed);
|
|||||||||||||||||||||||
Description | Sets whether or not splitter is set to fixed for a split container. (.NET analogy). | |||||||||||||||||||||||
Parameters | splitContainerName |
Name of split container that was created by a prior call to CWndResizer::CreateSplitContainer . |
||||||||||||||||||||||
fixed |
A boolean value. If this is TRUE , then the splitter is fixed, otherwise the splitter is free to be moved by mouse. |
|||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
|
|||||||||||||||||||||||
Remarks | If the splitter is set to fixed, user cannot move the splitter using mouse. | |||||||||||||||||||||||
Method |
BOOL SetMaximumSize(LPCTSTR panelName, CSize & size) ;
BOOL SetMaximumSize(UINT panelID, CSize & size) ;
|
|||||||||||||||||||||||
Description | Sets the maximum size of the specified panel. (.NET analogy). | |||||||||||||||||||||||
Parameters | panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
||||||||||||||||||||||
panelID |
An ID of a child control (window) of the hooked-window. | |||||||||||||||||||||||
size |
Minimum size of the panel. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
-or-
|
|||||||||||||||||||||||
Remarks | If panelName or panelID refers to a panel that is part of horizontal split container, then cy member of size should be ignored. If panelName or panelID refers to a panel that is part of vertical split container, then cx member of size should be ignored. |
|||||||||||||||||||||||
Method |
BOOL SetMinimumSize(LPCTSTR panelName, CSize & size) ;
BOOL SetMinimumSize(UINT panelID, CSize & size) ;
|
|||||||||||||||||||||||
Description | Sets the minimum size of the specified panel. (.NET analogy). | |||||||||||||||||||||||
Parameters | panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
||||||||||||||||||||||
panelID |
An ID of a child control (window) of the hooked-window. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
-or-
|
|||||||||||||||||||||||
Remarks | If panelName or panelID refers to a panel that is part of horizontal split container, then cy member of size should be ignored. If panelName or panelID refers to a panel that is part of vertical split container, then cx member of size should be ignored. |
|||||||||||||||||||||||
Method |
BOOL SetParent(LPCTSTR panelName, LPCTSTR parentName);
BOOL SetParent(UINT panelID, LPCTSTR parentName);
BOOL SetParent(LPCTSTR panelName, UINT parentID);
BOOL SetParent(UINT panelID, UINT parentID);
|
|||||||||||||||||||||||
Description | Sets the name of the parent of the specified panel. | |||||||||||||||||||||||
Parameters | panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
||||||||||||||||||||||
panelID |
An ID of a child control (window) of the hooked-window. | |||||||||||||||||||||||
parentID |
Name of the parent. | |||||||||||||||||||||||
parentName |
Name of the parent. | |||||||||||||||||||||||
Return values | TRUE |
Succeeded. parentName contains valid value. |
||||||||||||||||||||||
FALSE |
Failed. size value should be ignored.
-or-
|
|||||||||||||||||||||||
Remarks | This should not be thought of as Win32 SetParent API.
Set |
|||||||||||||||||||||||
Method |
void SetShowResizeGrip(BOOL show = TRUE);
|
|||||||||||||||||||||||
Description | Sets a flag indication if a resize grip will be drawn on the lower-right corner of the hooked-window. | |||||||||||||||||||||||
Parameters | show |
TRUE if the resize grip should be drawn, otherwise FALSE . |
||||||||||||||||||||||
Return values | N/A |
No return values. | ||||||||||||||||||||||
Remarks | This method never fails. | |||||||||||||||||||||||
Method |
BOOL Unhook();
|
|||||||||||||||||||||||
Parameters | N/A |
No parameters. | ||||||||||||||||||||||
Return values | TRUE |
Succeeded. | ||||||||||||||||||||||
FALSE |
Failed.
|
|||||||||||||||||||||||
Remarks | You do not have to call this method.
This method is automatically called when hooked-window receives |
Points of Interest
This solution does not require you to calculate any numbers on your part. This solution encapsulates all functionalities in the CWndResizer
.
One can re-design it such that it exposes other objects (CPanel
, CSplitContainer
, etc.). The reason I designed it this way is because I did not want the user to have to know too many objects, and to simplify memory allocation and de-allocation.
Another thing is that it depends on MFC classes (CWnd
etc.); again, one can remove all references to MFC classes and use pure Win32 APIs.
History
- 5th November, 2010: Initial post
- 26th November, 2010: Article updated
- 30th November, 2010: Source code and demo updated
发表评论
SWtH6m web site, since I experienced to reload the
xboWGw Very useful information specifically the last part I care for such information much.
VcEiCB Very good article. I will be dealing with many of these issues as well..
YgYS4F Say, you got a nice blog.Really thank you! Really Cool.
WWi3gE I simply could not depart your web site before suggesting that I actually enjoyed the standard info an individual supply in your visitors? Is gonna be again continuously in order to inspect new posts
Rpg19m just beneath, are a lot of totally not connected web-sites to ours, nevertheless, they may be surely really worth going over
BsoamI You have made some really good points there. I looked on the web to learn more about the issue and found most people will go along with your views on this website.
7koy80 many thanks for sharing source files. many thanks
CeDBqg Some truly fantastic articles on this website , appreciate it for contribution.
9P4pzO Great post. Much obliged.
nFOaYy This excellent website certainly has all of the information and facts I needed concerning this subject and didn at know who to ask.
1aPydU I think this is a real great blog post.Really thank you! Really Cool.
hN66LZ Muchos Gracias for your article post. Much obliged.
qPRFP6 Great post and right to the point. I am not sure if this is truly the best place to ask but do you guys have any ideea where to employ some professional writers? Thanks :)
5hGCvy I think other site proprietors should take this website as an model, very clean and excellent user friendly style and design, as well as the content. You're an expert in this topic!
pu5D4L I really appreciate this post. I have been looking everywhere for this! Thank goodness I found it on Bing. You've made my day! Thanks again
bvCQ7X Thanks-a-mundo for the blog post. Really Great.
RGI3nB Enjoyed every bit of your post.Really thank you! Cool.