|
|
Comments and Discussions
|
|
 |

|
Thank you!
I'm a student too, and my english is very poor. But i want to say thank you for you, this demo is very useful for me
|
|
|
|

|
Hi Hung
Is there any license associated with this code ?
Or as mentioned in the header can we use it for any purpose
rgds.
Shashi
Rgds.
Sulphur
|
|
|
|
|
|

|
modify something later, i used it in my work
|
|
|
|

|
It's very useful to me,thanks 4 ur share!
|
|
|
|

|
I want to display a Bitmap on the splitter .What should I do?Thx
|
|
|
|
|
|

|
Thanks for your source code!It is so good!
|
|
|
|
|

|
sorry but i'm an asp.net programmer - can you direct me to what to do with this as far as what goes where? i don't know a thing about c#. i need it to work in an asp.net project so i can't put it uncompiled into my project.
thanks for any help.
|
|
|
|

|
Hi all,
My Problem is:
There is two sub-dialogs in the parent dialog box, i use splitter to split the sub-dialog,
but when i create the splitter dynamically and the ID for this splitter is created in STRING table, when i drag the splitter, the parent dialog exits and the splitter bar is displayed on the screen.
My environment: Windows XP and Virtual C++ 6.0
Solution:
Comment the line // CStatic::OnLButtonDown(nFlags, point); in function void CSplitterControl::OnLButtonDown(UINT nFlags, CPoint point).
Now i can split the sub-dialogs correctly.
Hope it's userful to others.
Thanks!
BR,
Tim
I'm on my way!
|
|
|
|

|
Simple usage for a quite little piece of code to add to my app. Thanks.
|
|
|
|

|
Doi voi Dialog thi can gi phai sung Splitter
Chi can ve cac control la giai quyet duoc roi
|
|
|
|

|
Here is what I think is a fix to the SetRange method...
void CSplitterControl::SetRange(int nSubtraction, int nAddition, int nRoot)
{
if (nRoot < 0)
{
CRect rcWnd;
GetWindowRect(rcWnd);
GetParent()->ScreenToClient(rcWnd);
The original code was just calling GetWindowRect, which gets the window coordinates in screen space. What you want though is the coordinates in the client space of the parent window.
|
|
|
|

|
how can i make splitterbar to be not allow to move
|
|
|
|

|
Yes, I think you can do it with the following code,
m_wndSplitter1.SetRange(0, 0, -1);
or don't deal with these messages:
ChangePos(CWnd* pWnd, int dx, int dy);
ChangeWidth(CWnd* pWnd, int dx, DWORD dwFlag = CW_LEFTALIGN);
ChangeHeight(CWnd* pWnd, int dy, DWORD dwFlag = CW_TOPALIGN);
|
|
|
|

|
Why you want to do this?Splitter is designed for that.
or you can delete these lines as follows:
void CSplitterControl::OnLButtonUp(UINT nFlags, CPoint point)
{
if (pOwner && IsWindow(pOwner->m_hWnd))
{
//pOwner->SendMessage(WM_NOTIFY, nmsp.hdr.idFrom, (LPARAM)&nmsp); }
}
and
void CSplitterControl::OnLButtonUp(UINT nFlags, CPoint point)
{
void CSplitterControl::MoveWindowTo(CPoint pt)
{
//MoveWindow(rc);
}
|
|
|
|

|
Hi,
I want to use this code in SDI application that has multiple views? As you made all your changes in the OnInitDialog handler here, what handlers need to be accessed in case of an SDI application? Is it possible? Thank you.
Bhushan.
|
|
|
|

|
First I have to say that its a great control, i've been searching for something like this ... but i have one problem .. control can be moved out of the dialog ... is this handled somehow inside the code, or do I have to check out the new splitter position ?
|
|
|
|

|
I also run into same problem. following code worked for me
>>>>>
void CSplitterControl::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bIsPressed)
{
CWindowDC dc(NULL);
DrawLine(&dc, m_nX, m_nY);
CPoint pt = point;
ClientToScreen(&pt);
// GetParent()->ScreenToClient(&pt);
if (pt.x < m_nMin)
pt.x = m_nMin;
if (pt.y < m_nMin)
pt.y = m_nMin;
if (pt.x > m_nMax)
pt.x = m_nMax;
if (pt.y > m_nMax)
pt.y = m_nMax;
// GetParent()->ClientToScreen(&pt);
m_nX = pt.x;
m_nY = pt.y;
DrawLine(&dc, m_nX, m_nY);
}
CStatic::OnMouseMove(nFlags, point);
}
>>>>
|
|
|
|

|
A better fix to prevent moving outside the parent:
void CSplitterControl::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bIsPressed)
{
CWindowDC dc(NULL);
DrawLine(&dc, m_nX, m_nY);
CPoint pt = point;
ClientToScreen(&pt);
GetParent()->ScreenToClient(&pt);
if (pt.x < m_nMin)
pt.x = m_nMin;
if (pt.y < m_nMin)
pt.y = m_nMin;
if (pt.x > m_nMax)
pt.x = m_nMax;
if (pt.y > m_nMax)
pt.y = m_nMax;
//>>> BM/BEGIN: prevent moving outside the parent
CRect rParent;
GetParent()->GetClientRect( &rParent );
int minBorder = 10;
int iHeight = rParent.bottom - rParent.top - minBorder;
int iWidth = rParent.right - rParent.left - minBorder;
if (pt.x < minBorder)
pt.x = minBorder;
if (pt.y < minBorder)
pt.y = minBorder;
if (pt.x > iWidth)
pt.x = iWidth;
if (pt.y > iHeight)
pt.y = iHeight;
//>>> BM/END
GetParent()->ClientToScreen(&pt);
m_nX = pt.x;
m_nY = pt.y;
DrawLine(&dc, m_nX, m_nY);
}
CStatic::OnMouseMove(nFlags, point);
}
b.minet
|
|
|
|

|
if you add two buttons under the tree control,.i expect two buttons seem independent,in fact,they have overlappeded
each other.can you help me ?thanks a lot
|
|
|
|

|
Hi,
You set property in Picture Control Visible as false ( Remove default check mark in Visible property )
|
|
|
|

|
I am using it in a calender application where there are multiple controls on a single dialog and it works great for me. I am using MFC 7.1. Thanks a lot.
M.Aleem Sheikh
Abacusoft
|
|
|
|

|
In CSplitterControl::Create() function find row
CStatic::Create("", dwStyle, rc, pParent, nID);
replace it with
CStatic::Create(_T(""), dwStyle, rc, pParent, nID);
in order to make code TCHAR.H compatible.
|
|
|
|
|

|
A very simple and helpfull class for an MFC beginner as I am.
Thx again.
Sinouhe
|
|
|
|

|
copy and paste the header and source. Add to project. Compile. hmmmmmm.
\splittercontrol.h(109) : error C2653: 'std' : is not a class or namespace name.
etc etc.
Ok, ive now spent about an hour trying to interpret what this means:
//#include // STL Standard Template Library (add to StdAfx.h).
Lets see.. i know what a header file is. Should i include one where it says #include?
or, do should i add a library under project settings, or do i add a line (#include something maybe?)
to the StdAfx.h file? Should i automatically know what this means?
hmmm.. probably best to give up.
|
|
|
|

|
Mr Razz wrote:
Ok, ive now spent about an hour trying to interpret what this means:
//#include // STL Standard Template Library (add to StdAfx.h).
it's the HTML parsing in this forum...
//#include <vector> // STL Standard Template Library (add to StdAfx.h)
|
|
|
|

|
After I added "#include " to stdafx.h, I still got the following error with the new CSplitterControl code for ResizableDialog.
error C2955: 'vector' : use of class template requires template argument list
Did anyone encounter the same issue? Any solution for this? Thanks.
|
|
|
|

|
Moak:
I got the following error,too:
error C2955: 'vector' : use of class template requires template argument list
Can you help me?
Thanks!
Hello!
Hope all to be pleasure!
|
|
|
|

|
For some reason, the code did not fully compile.
I modified the usage of std::vector to CArray which I'm more familiar with.
Otherwise, I think you can add the 'SCO_SplitterEntry' template whenever you need it, if you insist on using the STL.
Robin Lavallée
|
|
|
|

|
Hi Robin,
I have the same problem with you. I spent one day cannot fix it. Could you please tell me how you fixed it?
Thanks,
wang
wang@recombo.com
|
|
|
|

|
Hi everyone.
I'm Hung Nguyen, the original author of the class.
I fell really happy because you find the class useful.
For a long time, I have not visited the site and my old email has been invalidated. I'm sorry for this. Please contact me via my new email:huyhung@hcm.fpt.vn, if necessary.
Many thanks to Moak for the updated version. I have looked at your interface. It sounds great. So, will you mind sending me your updated version.
Have a good time to everyone.
|
|
|
|

|
Well, my updated version is also available for resizeable dialogs. pretty much the same (simple) interface as before.
Code is too long to post it here.... if someone wants to write an article please contact me (only then, thx!). I will provide code and answers.
|
|
|
|

|
Hi, I did mail the updated version to Nguyen Huy Hung (original author).
PS: He did never write back, I dunno if he got my email and later patches.
|
|
|
|

|
hi,Moak,
Would you like to send me a copy of the updated version available for resizeable dialogs?
Thanks in advanve,
Cui Sheng
|
|
|
|

|
hi Moak and Cui Sheng,
would you please send me a copy, too ?
I'm currently facing the problem to resize/reposition the splitter as my window changes size...
Any further ideas about resizing the splitter control are also welcome !
Thanks a lot,
M
|
|
|
|

|
hi MTenzer,
I had studied it for two days ,but at last I gave it up, Sorry.
Best regards,
Cui Sheng
|
|
|
|

|
Hi Moak, can I have a copy of your control? thanks in advance!
|
|
|
|

|
Someone interested in an updated version of this control?
|
|
|
|

|
Hi,
Yes, I am interested. Please forward.
Thanks,
John
john@1startdepot.com
|
|
|
|

|
please send me
vvsprog@mail.ru
|
|
|
|

|
A few more days, I'm currently solving some issues with resizeable dialogs. Will post after all runs & tested.
|
|
|
|

|
Okay, here is my code.
I did run into wrong direction with resizeable dialogs, so here
is the version for static dialogs only. Couldn't contact the
original author by email, that's why I post here.
New features:
1. You can add controls and the splitter will resize them automatically.
2. You can show/hide controls of a pane (e.g all control left from splitter)
header file (load in VC and doesn't look strange formated)
#if !defined(AFX_SPLITTERCONTROL_H__C5E28DD6_B347_44F2_9DA0_20C4CFD50B5D__INCLUDED_)
#define AFX_SPLITTERCONTROL_H__C5E28DD6_B347_44F2_9DA0_20C4CFD50B5D__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif
class CSplitterControl : public CStatic
{
public:
CSplitterControl();
virtual ~CSplitterControl();
enum { TYPE_VERTICAL = 0, TYPE_HORIZONTAL };
enum { PANE_NONE = 0, PANE_LEFT = 1, PANE_RIGHT,
PANE_TOP = 1, PANE_BOTTOM,
PANE_ALL = 3
};
enum { DRAGGING_NOCHANGE = 0, DRAGGING_RESIZE, DRAGGING_REPOSITION };
typedef struct { HWND hWnd; int nPane; int nDraggingAction; } SCO_SplitterEntry;
public:
void GetType(BOOL& bUpdateWhileDragging, BOOL& bDrawSplitter, BOOL& bDrawRubberband) const
{ bUpdateWhileDragging = m_bUpdateWhileDragging;
bDrawSplitter = m_bDrawSplitter;
bDrawRubberband = m_bDrawRubberband;
}
void SetType(BOOL bUpdateWhileDragging = TRUE, BOOL bDrawSplitter = FALSE, BOOL bDrawRubberband = FALSE)
{ m_bUpdateWhileDragging = bUpdateWhileDragging;
m_bDrawSplitter = bDrawSplitter;
m_bDrawRubberband = bDrawRubberband;
}
void SetBorder(int nBorderLeftTop = 0, int nBorderRightBottom = 0);
void SetRangeRelative(int nDiffNegative, int nDiffPositive);
void SetResize(BOOL bLeft, BOOL bTop, BOOL bWidth, BOOL bHeight);
int GetSplitterPos();
void SetSplitterPos(int nPos);
BOOL Add(HWND hWnd, int nPane, BOOL bResize = TRUE, BOOL bDragChange = TRUE);
BOOL Add(CWnd* pWnd, int nPane, BOOL bResize = TRUE, BOOL bDragChange = TRUE);
BOOL Add(UINT nID, int nPane, BOOL bResize = TRUE, BOOL bDragChange = TRUE);
BOOL Remove(HWND hWnd);
BOOL Remove(CWnd* pWnd);
BOOL Remove(UINT nID);
BOOL IsChildControl(HWND hWnd) const;
BOOL IsChildControl(CWnd* pWnd) const;
BOOL IsChildControl(UINT nID) const;
BOOL ShowPane(int nPane, BOOL bShow = TRUE);
BOOL HidePane(int nPane) { return ShowPane(nPane, FALSE); }
int GetHiddenPane() const;
int GetOtherPane(int nPane) const;
BOOL IsValidPane(int nPane) const { return nPane>=PANE_LEFT && nPane<=PANE_BOTTOM; }
BOOL IsPaneVisible(int nPane) const;
void RecalcLayout();
void ChangePos(HWND hWnd, int dx, int dy);
void ChangePos(CWnd* pWnd, int dx, int dy);
void ChangeWidth(HWND hWnd, int dx, int nAlignPane);
void ChangeWidth(CWnd* pWnd, int dx, int nAlignPane);
void ChangeHeight(HWND hWnd, int dy, int nAlignPane);
void ChangeHeight(CWnd* pWnd, int dy, int nAlignPane);
protected:
protected:
virtual void PreSubclassWindow();
afx_msg void OnPaint();
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMove(int x, int y);
DECLARE_MESSAGE_MAP()
HWND GetCommonParent();
void LoadDefaultCursors();
void SetDraggingPos(CPoint& point);
void DrawRubberBand(BOOL bDraw);
void MoveSplitter(int nDelta);
void ResizeControls(int nDelta, int nPanes = PANE_ALL, BOOL bMoveSplitter = TRUE);
void ShowControls(int nPane, BOOL bShow);
void CalcRange(HWND hWnd, int nPane, int nSplitterPos);
int CalcHiddenRange(int nPane, BOOL bShow);
void CalcHiddenRange(HWND hWnd, int nPane);
private:
int m_nType; BOOL m_bUpdateWhileDragging; BOOL m_bDrawSplitter; BOOL m_bDrawRubberband;
HWND m_hWndParent; std::vector<SCO_SplitterEntry> m_ControlsArray; BOOL m_bNotify; BOOL m_bPaneLeftTopVisible; BOOL m_bPaneRightBottomVisible; BOOL m_bButtonPressed; BOOL m_bUpdating;
int m_nBorderLeftTop; int m_nBorderRightBottom;
int m_nParentPosMin, m_nParentPosMax; int m_nParentPanePosMin, m_nParentPanePosMax; int m_nDistanceLeftTop; int m_nDistanceRightBottom;
int m_nDragPosStart; int m_nDragPosStartOfset; int m_nDragPosX, m_nDragPosY; int m_nDraggedDelta;
HCURSOR m_hCursorHorizontal; HCURSOR m_hCursorVertical;
};
#endif
source code:
// CSplitterControl : implementation file
// Description: Another splitter control for dialogs
// Authors: Mark "Moak" Seuffert <captain@pirate.de>,
// based on previous code from Nguyen Huy Hung (big thx)
// Date: 07.11.2002
// Version: 0.9 (TODO: update for resizeable dialog)
//
// Use: Create a static control (e.g. a picture frame) and make it a
// CSplitterControl member variable. In OnInitDialog() of your dialog use
// member function Add() to attach all your splitted controls. When used in
// a resizeable dialog use SetResize() and move/resize the splitter in your
// dialog, all attached controls are handled by the splitter.
//
// Free for any use. NO WARRANTY, you run at own risk! You may not remove
// the authors or this notice. Support open source. Please let me know of
// any bugs/mods/improvements that you have found/implemented and I will
// fix/incorporate them into this file.
#include "stdafx.h"
#include "SplitterControl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//#define DEBUG_VERBOSE //uncomment this line if you want verbose debug output
#ifndef DEBUG_VERBOSE //... you have been warned. :-)
#undef TRACEV
#define TRACEV (void)0
#else
#undef TRACEV
#define TRACEV TRACE
#endif
const int SCO_BORDER_WIDTH = 6;
/////////////////////////////////////////////////////////////////////////////
// CSplitterControl
CSplitterControl::CSplitterControl()
{
m_nType = 0; //initialize with defaults
m_bUpdateWhileDragging = TRUE;
m_bDrawSplitter = FALSE;
#ifdef DEBUG_VERBOSE
m_bDrawRubberband = TRUE; //note: rubber band is great for debugging
#else
m_bDrawRubberband = FALSE;
#endif
m_hWndParent = NULL;
m_bNotify = FALSE;
m_bPaneLeftTopVisible = TRUE;
m_bPaneRightBottomVisible = TRUE;
m_bButtonPressed = FALSE;
m_bUpdating = FALSE;
m_nBorderLeftTop = 0;
m_nBorderRightBottom = 0;
m_nParentPosMin = m_nParentPosMax = 0;
m_nParentPanePosMin = m_nParentPanePosMax = 0;
m_nDistanceLeftTop = 0;
m_nDistanceRightBottom = 0;
m_nDragPosStart = 0;
m_nDragPosStartOfset = 0;
m_nDragPosX = m_nDragPosY = 0;
m_nDraggedDelta = 0;
m_hCursorHorizontal = NULL;
m_hCursorVertical = NULL;
LoadDefaultCursors();
}
CSplitterControl::~CSplitterControl()
{
}
BEGIN_MESSAGE_MAP(CSplitterControl, CStatic)
//{{AFX_MSG_MAP(CSplitterControl)
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_WM_SETCURSOR()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOVE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSplitterControl member functions and message handlers
// Create initial settings
void CSplitterControl::PreSubclassWindow()
{
CRect rect; //get initial width
GetClientRect(rect); //detect type (horizontal/vertical)
m_nType = (rect.Width() < rect.Height())?TYPE_VERTICAL:TYPE_HORIZONTAL;
m_hWndParent = GetCommonParent(); //get parent for all handled controls
ASSERT(m_hWndParent);
m_bNotify = GetStyle() & SS_NOTIFY; //splitter wants notify?
ModifyStyle(0, SS_NOTIFY | WS_CHILD);
//reset splitter range
SetBorder(SCO_BORDER_WIDTH, SCO_BORDER_WIDTH);
CStatic::PreSubclassWindow();
}
// Get parent of splitter control (used as parent for all handled controls)
HWND CSplitterControl::GetCommonParent()
{
return GetParent()->GetSafeHwnd();
}
// Load default resize cursors
void CSplitterControl::LoadDefaultCursors()
{
m_hCursorHorizontal = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE);
m_hCursorVertical = AfxGetApp()->LoadStandardCursor(IDC_SIZENS);
}
// Return splitter position (parent coordinates)
int CSplitterControl::GetSplitterPos()
{
ASSERT(m_hWndParent);
RECT rect;
::GetWindowRect(m_hWnd, &rect);
POINT point = { rect.left, rect.top };
::ScreenToClient(m_hWndParent, (LPPOINT)&point);
return (m_nType==TYPE_VERTICAL)?point.x:point.y;
}
// Set splitter position (parent coordinates)
void CSplitterControl::SetSplitterPos(int nPos)
{
if(nPos >= m_nParentPosMin && nPos <= m_nParentPosMax)
{
int nDelta = nPos - GetSplitterPos();
ResizeControls(nDelta);
//TODO: that }
}
// Set splitter range
void CSplitterControl::SetBorder(int nBorderLeftTop, int nBorderRightBottom)
{
//Note: Set splitter range by specifying distance to border of controls.
ASSERT(nBorderLeftTop>=0 && nBorderRightBottom>=0);
m_nBorderLeftTop = nBorderLeftTop;
m_nBorderRightBottom = nBorderRightBottom;
TRACEV("[SCO] SetRangeBorder %d, %d\n", m_nBorderLeftTop, m_nBorderRightBottom);
RecalcLayout();
}
// Set relative splitter range
void CSplitterControl::SetRangeRelative(int nDiffNegative, int nDiffPositive)
{
//Note: Set splitter range by specifying range relative to current splitter.
ASSERT(nDiffNegative<=0 && nDiffPositive>=0);
int nSplitterPos = GetSplitterPos(); //get current splitter position (parent coordinates)
m_nParentPosMin = nSplitterPos + nDiffNegative;
m_nParentPosMax = nSplitterPos + nDiffPositive;
TRACEV("[SCO] SetRangeRelative %d (base pos %d) +%d\n", nDiffNegative, nSplitterPos, nDiffPositive);
}
void CSplitterControl::SetResize(BOOL bLeft, BOOL bTop, BOOL bWidth, BOOL bHeight)
{
}
// Recalculate layout
void CSplitterControl::RecalcLayout()
{
ASSERT(m_hWndParent);
if(m_bUpdating) return; //prevent multiple calls (resizing/repositioning would cause them)
//Reset range to parent dimensions
int nSplitterPos = GetSplitterPos(); //get current splitter position (parent coordinates)
RECT rect;
::GetClientRect(m_hWndParent, &rect);
if(m_nType == TYPE_VERTICAL)
{
m_nParentPosMin = rect.left;
m_nParentPosMax = rect.right;
m_nDistanceLeftTop = m_nDistanceRightBottom = rect.right-rect.left;
} else {
m_nParentPosMin = rect.top;
m_nParentPosMax = rect.bottom;
m_nDistanceLeftTop = m_nDistanceRightBottom = rect.bottom-rect.top;
}
//Calculate limits from handled controls
std::vector<SCO_SplitterEntry>::const_iterator itControl;
for(itControl=m_ControlsArray.begin();itControl!=m_ControlsArray.end();itControl++)
{ //only resizeable controls count here
if((*itControl).nDraggingAction == DRAGGING_RESIZE)
CalcRange((*itControl).hWnd, (*itControl).nPane, nSplitterPos);
}
TRACEV("[SCO] RecalcLayout PosSplitter %d, PosMin=%d, PosMax=%d (distance %d, %d)\n", nSplitterPos, m_nParentPosMin, m_nParentPosMax, m_nDistanceLeftTop, m_nDistanceRightBottom);
}
void CSplitterControl::CalcRange(HWND hWnd, int nPane, int nSplitterPos)
{
//Note: Range calculation is based on splitter //adjusted so that splitter
ASSERT(hWnd);
ASSERT(m_hWndParent);
ASSERT(IsValidPane(nPane));
RECT rect; //get position in parent coordinates
::GetWindowRect(hWnd, &rect);
::ScreenToClient(m_hWndParent, (LPPOINT)&rect);
::ScreenToClient(m_hWndParent, ((LPPOINT)&rect)+1);
if(m_nType == TYPE_VERTICAL) //allow splitter within reasonable limits
{
if(nPane == PANE_LEFT) {
int nDistance = nSplitterPos - rect.right;
if(rect.left + nDistance + m_nBorderLeftTop > m_nParentPosMin)
m_nParentPosMin = rect.left + nDistance + m_nBorderLeftTop;
if(nDistance < m_nDistanceLeftTop)
m_nDistanceLeftTop = nDistance;
} else {
int nDistance = rect.left - nSplitterPos;
if(rect.right - nDistance - m_nBorderRightBottom < m_nParentPosMax)
m_nParentPosMax = rect.right - nDistance - m_nBorderRightBottom;
if(nDistance < m_nDistanceRightBottom)
m_nDistanceRightBottom = nDistance;
}
} else {
if(nPane == PANE_TOP) {
int nDistance = nSplitterPos - rect.bottom;
if(rect.top + nDistance + m_nBorderLeftTop > m_nParentPosMin)
m_nParentPosMin = rect.top + nDistance + m_nBorderLeftTop;
if(nDistance < m_nDistanceLeftTop)
m_nDistanceLeftTop = nDistance;
} else {
int nDistance = rect.top - nSplitterPos;
if(rect.bottom - nDistance - m_nBorderRightBottom < m_nParentPosMax)
m_nParentPosMax = rect.bottom - nDistance - m_nBorderRightBottom;
if(nDistance < m_nDistanceRightBottom)
m_nDistanceRightBottom = nDistance;
}
}
}
// Calculate range of a hidden pane
int CSplitterControl::CalcHiddenRange(int nPane, BOOL bShow)
{
ASSERT(IsValidPane(nPane));
ASSERT(PANE_LEFT==PANE_TOP && PANE_RIGHT == PANE_BOTTOM);
ASSERT(m_hWndParent);
int nPos = GetSplitterPos(); //get current splitter position (parent coordinates)
if(!bShow) //hide this pane?
{
m_nParentPanePosMin = m_nParentPanePosMax = nPos;
//calculate range from handled controls
std::vector<SCO_SplitterEntry>::const_iterator itControl;
for(itControl=m_ControlsArray.begin();itControl!=m_ControlsArray.end();itControl++)
{
if((*itControl).nPane == nPane)
CalcHiddenRange((*itControl).hWnd, nPane);
}
//return distance to current splitter position
return (nPane==PANE_LEFT)?m_nParentPanePosMin-nPos:m_nParentPanePosMax-nPos;
} else { //when showing pane again
//... just return reverse distance
return (nPane==PANE_LEFT)?nPos-m_nParentPanePosMin:nPos-m_nParentPanePosMax;
}
}
void CSplitterControl::CalcHiddenRange(HWND hWnd, int nPane)
{
//Note: Hiden range is calculated relative to splitter //will be adjusted so that the other (visible) pane can occupy the whole space.
ASSERT(hWnd);
ASSERT(m_hWndParent);
ASSERT(IsValidPane(nPane));
RECT rect; //get position in parent coordinates
::GetWindowRect(hWnd, &rect);
::ScreenToClient(m_hWndParent, (LPPOINT)&rect);
::ScreenToClient(m_hWndParent, ((LPPOINT)&rect)+1);
if(m_nType == TYPE_VERTICAL) //calculate a new (virtual) splitter range
{
if(nPane == PANE_LEFT) {
if(rect.left - m_nDistanceRightBottom < m_nParentPanePosMin)
m_nParentPanePosMin = rect.left - m_nDistanceRightBottom;
} else {
if(rect.right + m_nDistanceLeftTop > m_nParentPanePosMax)
m_nParentPanePosMax = rect.right + m_nDistanceLeftTop;
}
} else {
if(nPane == PANE_TOP) {
if(rect.top - m_nDistanceRightBottom < m_nParentPanePosMin)
m_nParentPanePosMin = rect.top - m_nDistanceRightBottom;
} else {
if(rect.bottom + m_nDistanceLeftTop > m_nParentPanePosMax)
m_nParentPanePosMax = rect.bottom + m_nDistanceLeftTop;
}
}
}
// Show or hide requested pane
BOOL CSplitterControl::ShowPane(int nPane, BOOL bShow)
{
ASSERT(IsValidPane(nPane));
if(bShow) { //plausability check, only one pane can be hidden at a time
if(nPane != GetHiddenPane()) return FALSE;
} else {
if(GetHiddenPane()) return FALSE;
}
ShowControls(nPane, bShow); //show/hide controls of requested pane
ShowWindow(bShow); //splitter is hidden while one pane is hidden
//resize/reposition controls of remaining pane
int nDelta = CalcHiddenRange(nPane, bShow);
ResizeControls(nDelta, GetOtherPane(nPane), FALSE);
return TRUE; //pane update was sucessfull
}
// Show or hide controls of a pane
void CSplitterControl::ShowControls(int nPane, BOOL bShow)
{
ASSERT(IsValidPane(nPane));
ASSERT(PANE_LEFT==PANE_TOP && PANE_RIGHT == PANE_BOTTOM);
std::vector<SCO_SplitterEntry>::const_iterator itControl;
for(itControl=m_ControlsArray.begin();itControl!=m_ControlsArray.end();itControl++)
{
if((*itControl).nPane == nPane)
::ShowWindow((*itControl).hWnd, bShow);
}
if(nPane == PANE_LEFT) m_bPaneLeftTopVisible = bShow;
else m_bPaneRightBottomVisible = bShow;
}
// Check if pane is visible
BOOL CSplitterControl::IsPaneVisible(int nPane) const
{
ASSERT(IsValidPane(nPane));
ASSERT(PANE_LEFT==PANE_TOP && PANE_RIGHT == PANE_BOTTOM);
return (nPane==PANE_LEFT)?m_bPaneLeftTopVisible:m_bPaneRightBottomVisible;
}
// Return which pane is invisible
int CSplitterControl::GetHiddenPane() const
{
if(!m_bPaneLeftTopVisible) return (m_nType == TYPE_VERTICAL)?PANE_LEFT:PANE_TOP;
if(!m_bPaneRightBottomVisible) return (m_nType == TYPE_VERTICAL)?PANE_RIGHT:PANE_BOTTOM;
return PANE_NONE;
}
int CSplitterControl::GetOtherPane(int nPane) const
{
ASSERT(IsValidPane(nPane));
ASSERT(PANE_LEFT==PANE_TOP && PANE_RIGHT == PANE_BOTTOM);
return (nPane==PANE_LEFT)?PANE_RIGHT:PANE_LEFT;
}
// Add control to list of handled controls
BOOL CSplitterControl::Add(HWND hWnd, int nPane, BOOL bResize, BOOL bDragChange)
{
ASSERT(hWnd);
ASSERT(IsValidPane(nPane));
ASSERT(m_hWndParent == ::GetParent(hWnd));
SCO_SplitterEntry Control; //store control preferences
Control.hWnd = hWnd;
Control.nPane = nPane;
Control.nDraggingAction = bResize?DRAGGING_RESIZE:DRAGGING_REPOSITION;
if(!bDragChange) Control.nDraggingAction = DRAGGING_NOCHANGE;
ASSERT(!IsChildControl(hWnd)); //make sure a control is handled only once
m_ControlsArray.push_back(Control); //add it to handled controls
if(Control.nDraggingAction == DRAGGING_RESIZE)
{ //if it CalcRange(hWnd, nPane, GetSplitterPos());
}
return TRUE;
}
BOOL CSplitterControl::Add(CWnd* pWnd, int nPane, BOOL bResize, BOOL bDragChange)
{
return Add(pWnd->GetSafeHwnd(), nPane, bResize, bDragChange);
}
BOOL CSplitterControl::Add(UINT nID, int nPane, BOOL bResize, BOOL bDragChange)
{
ASSERT(m_hWndParent);
return Add(::GetDlgItem(m_hWndParent, nID), nPane, bResize, bDragChange);
}
// Check if control is already handled by splitter
BOOL CSplitterControl::IsChildControl(HWND hWnd) const
{
std::vector<SCO_SplitterEntry>::const_iterator itControl;
for(itControl=m_ControlsArray.begin();itControl!=m_ControlsArray.end();itControl++)
if((*itControl).hWnd == hWnd) return TRUE;
return FALSE;
}
BOOL CSplitterControl::IsChildControl(CWnd* pWnd) const
{
return IsChildControl(pWnd->GetSafeHwnd());
}
BOOL CSplitterControl::IsChildControl(UINT nID) const
{
ASSERT(m_hWndParent);
return IsChildControl(::GetDlgItem(m_hWndParent, nID));
}
// Remove control from list of handled controls
BOOL CSplitterControl::Remove(HWND hWnd)
{
std::vector<SCO_SplitterEntry>::iterator itControl;
for(itControl=m_ControlsArray.begin();itControl!=m_ControlsArray.end();itControl++)
{
if((*itControl).hWnd == hWnd) //control found?
{ //remove it from handled controls
m_ControlsArray.erase(itControl);
RecalcLayout(); //we need to recalculate splitter layout
return TRUE;
}
}
return FALSE;
}
BOOL CSplitterControl::Remove(CWnd* pWnd)
{
return Remove(pWnd->GetSafeHwnd());
}
BOOL CSplitterControl::Remove(UINT nID)
{
ASSERT(m_hWndParent);
return Remove(::GetDlgItem(m_hWndParent, nID));
}
// Draw splitter control
void CSplitterControl::OnPaint()
{
if(m_bDrawSplitter) //draw splitter separator between panes?
{ //... normaly splitter itself is not visible
CPaintDC dc(this);
CRect rcClient;
GetClientRect(rcClient); //get the client coords
dc.Draw3dRect(rcClient, //draw rectangle
::GetSysColor(COLOR_BTNHIGHLIGHT),
::GetSysColor(COLOR_BTNSHADOW));
} else ValidateRect(NULL); //validate client rect (or window update becomes screwed up)
}
// Move splitter control (e.g. from a dialog resize)
void CSplitterControl::OnMove(int x, int y)
{
CStatic::OnMove(x, y);
TRACEV("[SCO] OnMove PosSplitter %d, PosMinOld=%d, PosMaxOld=%d (distance %d, %d)\n", x, m_nParentPosMin, m_nParentPosMax, m_nDistanceLeftTop, m_nDistanceRightBottom);
int nSplitterPos = x; //equivalent to GetSplitterPos() here
ASSERT(nSplitterPos>=m_nParentPosMin); //Oops, splitter did not stay within range
//...make sure your resize control runs well configured! That RecalcLayout();
}
// Set cursor (depending on type of splitter control)
BOOL CSplitterControl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if(m_nType == TYPE_VERTICAL) ::SetCursor(m_hCursorHorizontal);
else ::SetCursor(m_hCursorVertical);
return 0;
}
// Mouse button pressed
void CSplitterControl::OnLButtonDown(UINT nFlags, CPoint point)
{
//Note: Start dragging splitter
CStatic::OnLButtonDown(nFlags, point);
if(!m_bButtonPressed)
{
m_bButtonPressed = TRUE;
SetCapture(); //claim mouse input
CRect rcWnd; //store start position (screen coordinates)
GetWindowRect(rcWnd);
if(m_nType == TYPE_VERTICAL)
{
m_nDragPosStart = m_nDragPosX = rcWnd.left + point.x;
m_nDragPosStartOfset = point.x;
} else {
m_nDragPosStart = m_nDragPosY = rcWnd.top + point.y;
m_nDragPosStartOfset = point.y;
}
m_nDraggedDelta = 0;
DrawRubberBand(TRUE); //draw rubber band
}
}
// Splitter control is dragged with mouse
void CSplitterControl::OnMouseMove(UINT nFlags, CPoint point)
{
if(m_bButtonPressed)
{
DrawRubberBand(FALSE); //remove last rubber band
SetDraggingPos(point); //update current position
if(m_bUpdateWhileDragging) //update controls while dragging mouse?
{
int nDelta; //calculate dragged distance
if(m_nType == TYPE_VERTICAL)
nDelta = m_nDragPosX - m_nDragPosStart;
else
nDelta = m_nDragPosY - m_nDragPosStart;
//resize/reposition controls
ResizeControls(nDelta-m_nDraggedDelta);
m_nDraggedDelta = nDelta;
}
DrawRubberBand(TRUE); //draw new rubber band
}
CStatic::OnMouseMove(nFlags, point);
}
// Mouse button released
void CSplitterControl::OnLButtonUp(UINT nFlags, CPoint point)
{
//Note: Stop dragging splitter
if(m_bButtonPressed)
{
m_bButtonPressed = FALSE;
ReleaseCapture(); //back to normal mouse input processing
DrawRubberBand(FALSE); //remove last rubber band
SetDraggingPos(point); //update current position
int nDelta; //calculate dragged distance
if(m_nType == TYPE_VERTICAL)
nDelta = m_nDragPosX - m_nDragPosStart;
else
nDelta = m_nDragPosY - m_nDragPosStart;
//resize/reposition controls
ResizeControls(nDelta-m_nDraggedDelta);
}
CStatic::OnLButtonUp(nFlags, point);
}
// Set current dragging position
void CSplitterControl::SetDraggingPos(CPoint& point)
{
ASSERT(m_hWndParent);
if(m_nParentPosMin == m_nParentPosMax) return;
ClientToScreen(&point); //check if we are within limits
::ScreenToClient(m_hWndParent, &point);
if(m_nType == TYPE_VERTICAL)
{
if(point.x < m_nParentPosMin + m_nDragPosStartOfset)
point.x = m_nParentPosMin + m_nDragPosStartOfset;
else if(point.x > m_nParentPosMax + m_nDragPosStartOfset)
point.x = m_nParentPosMax + m_nDragPosStartOfset;
} else {
if(point.y < m_nParentPosMin + m_nDragPosStartOfset)
point.y = m_nParentPosMin + m_nDragPosStartOfset;
else if(point.y > m_nParentPosMax + m_nDragPosStartOfset)
point.y = m_nParentPosMax + m_nDragPosStartOfset;
}
::ClientToScreen(m_hWndParent, &point); //store new position (screen coordinates)
m_nDragPosX = point.x;
m_nDragPosY = point.y;
}
// Draw or remove a rubber band
void CSplitterControl::DrawRubberBand(BOOL bDraw)
{
if(!m_bDrawRubberband) return; //rubber band requested?
CWindowDC dc(NULL);
CRect rcWnd; //draw rubber band
GetWindowRect(&rcWnd);
if(!m_bUpdateWhileDragging)
rcWnd.OffsetRect(m_nDragPosX-m_nDragPosStart, 0);
CSize sizeBar(rcWnd.Width(), rcWnd.Height());
dc.DrawDragRect(&rcWnd, sizeBar, NULL, sizeBar);
#ifdef DEBUG_VERBOSE //also show splitter range in verbose debug mode
int nOldRop = dc.SetROP2(R2_NOTXORPEN);
CPen pen(PS_DOT, 1, RGB(0,0,0));
CPen* pOldPen = dc.SelectObject(&pen);
if(m_nType == TYPE_VERTICAL)
{
CPoint point(m_nParentPosMin, 0);
::ClientToScreen(m_hWndParent, &point);
dc.MoveTo(point.x, rcWnd.top);
dc.LineTo(point.x, rcWnd.bottom);
point.x = m_nParentPosMax;
::ClientToScreen(m_hWndParent, &point);
dc.MoveTo(point.x, rcWnd.top);
dc.LineTo(point.x, rcWnd.bottom);
} else {
CPoint point(0, m_nParentPosMin);
::ClientToScreen(m_hWndParent, &point);
dc.MoveTo(rcWnd.left, point.y);
dc.LineTo(rcWnd.right, point.y);
point.y = m_nParentPosMax;
::ClientToScreen(m_hWndParent, &point);
dc.MoveTo(rcWnd.left, point.y);
dc.LineTo(rcWnd.right, point.y);
}
dc.SetROP2(nOldRop);
dc.SelectObject(pOldPen);
#endif
}
// Resize and reposition all handled controls
void CSplitterControl::ResizeControls(int nDelta, int nPanes, BOOL bMoveSplitter)
{
ASSERT(m_hWndParent);
ASSERT(nPanes != PANE_NONE);
if(!nDelta) return; //nothing to do?
m_bUpdating = TRUE;
TRACEV("[SCO] ResizeControls delta %d, %sPosSplitter %d\n", nDelta, bMoveSplitter?"":"virtual ", GetSplitterPos()+nDelta);
//First reposition splitter itself
if(bMoveSplitter) MoveSplitter(nDelta);
//Update controls
if(m_nType == TYPE_VERTICAL)
{
std::vector<SCO_SplitterEntry>::iterator itControl;
for(itControl=m_ControlsArray.begin();itControl!=m_ControlsArray.end();itControl++)
{
switch((*itControl).nDraggingAction)
{
case DRAGGING_RESIZE: //resize control
if((*itControl).nPane == PANE_LEFT) {
if(nPanes & PANE_LEFT) ChangeWidth((*itControl).hWnd, nDelta, PANE_LEFT);
} else {
if(nPanes & PANE_RIGHT) ChangeWidth((*itControl).hWnd, -nDelta, PANE_RIGHT);
}
break;
case DRAGGING_REPOSITION: //reposition control
if((*itControl).nPane == PANE_LEFT) {
if(nPanes & PANE_LEFT) ChangePos((*itControl).hWnd, nDelta, 0);
} else {
if(nPanes & PANE_RIGHT) ChangePos((*itControl).hWnd, nDelta, 0);
}
break;
}
}
} else {
std::vector<SCO_SplitterEntry>::iterator itControl;
for(itControl=m_ControlsArray.begin();itControl!=m_ControlsArray.end();itControl++)
{
switch((*itControl).nDraggingAction)
{
case DRAGGING_RESIZE: //resize control
if((*itControl).nPane == PANE_TOP) {
if(nPanes & PANE_TOP) ChangeHeight((*itControl).hWnd, nDelta, PANE_TOP);
} else {
if(nPanes & PANE_BOTTOM) ChangeHeight((*itControl).hWnd, -nDelta, PANE_BOTTOM);
}
break;
case DRAGGING_REPOSITION: //reposition control
if((*itControl).nPane == PANE_TOP) {
if(nPanes & PANE_TOP) ChangePos((*itControl).hWnd, 0, nDelta);
} else {
if(nPanes & PANE_BOTTOM) ChangePos((*itControl).hWnd, 0, nDelta);
}
break;
}
}
}
::UpdateWindow(m_hWndParent); //now parent window needs a repaint
//...for previously invalidated regions
//Send notification if requested
if(m_bNotify)
{
NMHDR nmh;
nmh.hwndFrom = m_hWnd;
nmh.idFrom = GetDlgCtrlID();
nmh.code = nDelta;
ASSERT(m_hWndParent && nmh.idFrom);
::SendMessage(m_hWndParent, WM_NOTIFY, nmh.idFrom, (LPARAM)&nmh);
}
m_bUpdating = FALSE;
}
// Update position of splitter control
void CSplitterControl::MoveSplitter(int nDelta)
{
if(m_nType == TYPE_VERTICAL) ChangePos(m_hWnd, nDelta, 0);
else ChangePos(m_hWnd, 0, nDelta);
}
// Change width of a control
void CSplitterControl::ChangeWidth(HWND hWnd, int dx, int nAlignPane)
{
ASSERT(hWnd);
ASSERT(m_hWndParent == ::GetParent(hWnd));
ASSERT(nAlignPane==PANE_LEFT || nAlignPane==PANE_RIGHT);
RECT rect;
::GetWindowRect(hWnd, &rect);
::ScreenToClient(m_hWndParent, (LPPOINT)&rect);
::ScreenToClient(m_hWndParent, ((LPPOINT)&rect)+1);
if(dx<0) ::InvalidateRect(m_hWndParent, &rect, TRUE);
if(nAlignPane == PANE_LEFT) rect.right += dx;
else rect.left -= dx;
::MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, FALSE);
::InvalidateRect(m_hWndParent, &rect, FALSE);
}
void CSplitterControl::ChangeWidth(CWnd* pWnd, int dx, int nAlignPane)
{
ChangeWidth(pWnd->GetSafeHwnd(), dx, nAlignPane);
}
// Change height of a control
void CSplitterControl::ChangeHeight(HWND hWnd, int dy, int nAlignPane)
{
ASSERT(hWnd);
ASSERT(m_hWndParent == ::GetParent(hWnd));
ASSERT(nAlignPane==PANE_TOP || nAlignPane==PANE_BOTTOM);
RECT rect;
::GetWindowRect(hWnd, &rect);
::ScreenToClient(m_hWndParent, (LPPOINT)&rect);
::ScreenToClient(m_hWndParent, ((LPPOINT)&rect)+1);
if(dy<0) ::InvalidateRect(m_hWndParent, &rect, TRUE);
if(nAlignPane == PANE_TOP) rect.bottom += dy;
else rect.top -= dy;
::MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, FALSE);
::InvalidateRect(m_hWndParent, &rect, FALSE);
}
void CSplitterControl::ChangeHeight(CWnd *pWnd, int dy, int nAlignPane)
{
ChangeHeight(pWnd->GetSafeHwnd(), dy, nAlignPane);
}
// Change position of a control
void CSplitterControl::ChangePos(HWND hWnd, int dx, int dy)
{
ASSERT(hWnd);
ASSERT(m_hWndParent == ::GetParent(hWnd));
RECT rect;
::GetWindowRect(hWnd, &rect);
::ScreenToClient(m_hWndParent, (LPPOINT)&rect);
::ScreenToClient(m_hWndParent, ((LPPOINT)&rect)+1);
::InvalidateRect(m_hWndParent, &rect, TRUE);
::OffsetRect(&rect, dx, dy);
::MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, FALSE);
::InvalidateRect(m_hWndParent, &rect, FALSE);
}
void CSplitterControl::ChangePos(CWnd* pWnd, int dx, int dy)
{
ChangePos(pWnd->GetSafeHwnd(), dx, dy);
}
|
|
|
|

|
int nSplitterPos = x; //equivalent to GetSplitterPos() here
It's not equivalent, this works only for vertical splitters, therefore it should be nSplitterPos = GetSplitterPos();
Thaks for the control
|
|
|
|

|
Hi Moak,
I'm also interested in your version.
Thanks,
Thomas
|
|
|
|

|
Hi, on using your splitter control (which saved me a lot of time) together with cdxCDynamicCDialog I had to fix SetRange as follows: if (nRoot < 0) { CRect rcWnd; GetWindowRect(rcWnd); GetParent()->ScreenToClient(rcWnd); // <-- Fix: Need to work in client coordinates!! if (m_nType == SPS_VERTICAL) nRoot = rcWnd.left + rcWnd.Width() / 2; else // if m_nType == SPS_HORIZONTAL nRoot = rcWnd.top + rcWnd.Height() / 2; } Ciao, Andreas
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
A very simple splitter control for dialogs
| Type | Article |
| Licence | |
| First Posted | 12 Jul 2002 |
| Views | 217,658 |
| Bookmarked | 122 times |
|
|