SetWindowOrg

 

在用与设备环境(DC)有关的类时,经常对窗口坐标和视口坐标之间的关系有点搞不清楚,在网上查了一下,发现下面这篇文章挺有用的

(http://embeded-life.blog.163.com/blog/static/105932519200942511331799/),这里贴出来分享一下。

另外参考http://www.360doc.com/content/10/0803/10/1066008_43320327.shtml和百度文库文章(http://wenku.baidu.com/view/1e1409284b73f242336c5f6c.html?from=rec&pos=1&weight=3&lastweight=1&count=5,这篇文章用图形化的方法直观而深入浅出地介绍了窗口与视口的映射关系,让人茅塞顿开)

CDC::SetWindowOrg

CPoint SetWindowOrg(int x ,int y );
CPoint SetWindowOrg(POINT point )

返回值:CPoint对象,是窗口初始位置的前一次取值(逻辑单位)。

参数: x 指定窗口初始位置的X逻辑坐标。
y 指定视图端口初始位置的Y逻辑坐标。
point 指定窗口初始位置。其值必须在设备坐标系统范围内。可以为该参数传递POINT结构或CPoint对象。

说明:
设置设备上下文的窗口初始位置。它和设备上下文窗口一起说明了GDI如何将逻辑坐标中的点映射到实际设备坐标中。换言之,它们说明了GDI如何将逻辑坐标转换为设备坐标。窗口初始位置表明在设备坐标系统中的点,GDI将视图端口初始位置与该点映射。窗口初始位置是由SetWindowOrg成员函数在逻辑坐标系统中指定的。GDI在映射其它点时遵从同样的过程,这需要窗口初始位置与视图端口初始位置的映射。例如,所有以窗口初始位置为中心的圆周上的点同样是以视图端口初始位置为中心的圆周上的点。同样地,通过窗口初始位置的直线上的所有点也将形成一条通过视图端口初始位置的直线。

在CMyStatic::OnPaint() 函数中测试

CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here
CRect rect;
GetClientRect(&rect);//取得控件大小
dc.SetMapMode(MM_ANISOTROPIC);//设置成可以改变作标轴方向和比例的,但下面没改变比例

1.先讨论X轴正向向左,Y轴正向向下的情况,即默认的状态


dc.SetWindowExt(100, 100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,200);//设备是长200像素,宽200像素

即一个逻辑单位 = 200 / 100 = 2个像素

如果上面第一行改成dc.SetWindowExt(100, 200);//那么,一个逻辑长单位就是2像素,逻辑高单位就是1个像素

同样的dc.Rectangle(0, 0, 10, 10);第一个就是正方形,而第二个就是长是宽2倍的长方形

2.现在谈谈关于设备原点和逻辑原点的问题。

我的理解是,设备原点永远在左上角,也就是最初的(0,0)的位置

逻辑原点是可以移动的,Y轴的正向是向上还是向下是可以改变的

现在考虑Y轴向下为正向时,逻辑原点的移动问题。

dc.SetWindowOrg(0, 0);//这个可省略
dc.SetViewportOrg(rect.right, rect.bottom);

这个就是把设备的(rect.right, rect.bottom)映射为逻辑原点(0, 0)

那就就相当于,把逻辑原点从原来设备原点(0,0)平移到了(rect.right, rect.bottom)

即整个作标系,向右平移了rect.right,向下平移了rect.bottom.

dc.SetViewportOrg(0, 0);//这个可省略
dc.SetWindowOrg(rect.right, rect.bottom);

这个是把逻辑的(rect.right, rect.bottom)映射为设备的(0, 0);也可以说是把设备的(0, 0)映射为逻辑的(rect.right, rect.bottom)

可以想像,设备原点(0, 0)永远在左上角,由于Y轴是向下为正,X轴向右为正,那逻辑原点就被向上移动了rect.bottom,向左移动了rect.right,这样,刚好逻辑坐标(rect.right, rect.bottom)落在了右上角的位置。

然而dc.Rectangle(0, 0, rect.right, rect.bottom);画出的长方形,就正好完全落在窗口这外了

要想和dc.SetViewportOrg(rect.right, rect.bottom);达到相同的效果,

必须要把逻辑原点向右和向下移动,也就是,移动动后的逻辑坐标(-rect.right, -rect.bottom);落在设备原点上,即把逻辑的(-rect.right, -rect.bottom)映射为设备的(0, 0);

dc.SetWindowOrg(-rect.right, -rect.bottom);

如果SetWindowOrg和SetViewportOrg同时使用,例如

dc.SetWindowOrg(x, y);
dc.SetViewportOrg(x1, y1);//不推荐同时用,因为通常会把自己给搞糊涂

即把逻辑坐标(x, y)映射为设备坐标(x1, y1),就相当于把逻辑原点向右移(x1 - x2), 向下移(y1 - y2)

综上所述,X轴正向向右,Y轴正向向下的情况下,

dc.SetWindowOrg(-rect.right, -rect.bottom);等价于dc.SetViewportOrg(rect.right, rect.bottom);

而dc.SetViewportOrg(rect.right, rect.bottom);和dc.SetViewportOrg(rect.right, rect.bottom);刚互补,同时运行则效果抵消。

3.现在讨论X轴正向向左,Y轴正向向上的情况

dc.SetWindowExt(100, -100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,200);//设备是长200像素,宽200像素

dc.SetWindowExt(100, 100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,-200);//设备是长200像素,宽200像素

如果

dc.SetWindowExt(100, -100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,-200);//设备是长200像素,宽200像素

则Y轴仍向下

以下面为例:

dc.SetWindowExt(100, -100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,200);//设备是长200像素,宽200像素

dc.SetWindowOrg(0, 0);//这个可省略
dc.SetViewportOrg(0, rect.bottom);

设备(0, rect.bottom)映射为逻辑原点(0, 0),即把逻辑原点,移到左下角了

dc.SetViewportOrg(0, 0);//这个可省略
dc.SetWindowOrg(0, rect.bottom);

逻辑(0, rect.bottom);映身为设备原点(0,0),即设备原点(0, 0)映射为逻辑(0, rect.bottom);由于Y轴向上,所以相当于把逻辑原点由设备原点,向下移动rect.bottom,这样,逻辑(0, rect.bottom)刚好落在设备原点(0,0)上。即逻辑原点移动到了左下角。

这时可以发现,dc.SetViewportOrg(0, rect.bottom);与dc.SetWindowOrg(0, rect.bottom);等价了

推广开来,即dc.SetViewportOrg(x, y);与dc.SetWindowOrg(-x,   y);等价

总结:保持X轴正向向右不变,当Y轴正向向下时

dc.SetViewportOrg(x, y);与dc.SetWindowOrg(-x,   -y);等价

当Y轴正向向上时

dc.SetViewportOrg(x, y);与dc.SetWindowOrg(-x,   y);等价

需要强调一点,设备原点永远不会移动,保持在(0,0),设备的坐标系永远不会变,变的是逻辑原点在设备坐标系中的位置和逻辑坐标轴的方向。


// GradientProgressCtrl.cpp : implementation file // #include "stdafx.h" #include "VProgressCtrl.h" //#include "MemDC.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif class CMemDC : public CDC { public: CMemDC(CDC* pDC); CMemDC* operator->(){return this;} operator CMemDC*(){return this;} virtual ~CMemDC(); private: CBitmap m_bitmap; CBitmap* m_pOldBitmap; CDC* m_pDC; CRect m_rect; BOOL m_bMemDC; }; CMemDC::CMemDC(CDC* pDC):CDC() { ASSERT(pDC!=NULL); m_pDC=pDC; m_pOldBitmap=NULL; m_bMemDC=!pDC->IsPrinting(); // if(m_bMemDC) { pDC->GetClipBox(&m_rect); CreateCompatibleDC(pDC); m_bitmap.CreateCompatibleBitmap(pDC,m_rect.Width(),m_rect.Height()); m_pOldBitmap=SelectObject(&m_bitmap); SetWindowOrg(m_rect.left,m_rect.top); } else //为相关的现有设备准备打印 { m_bPrinting=pDC->m_bPrinting; m_hDC=pDC->m_hDC; m_hAttribDC=pDC->m_hAttribDC; } } CMemDC::~CMemDC() { if(m_bMemDC) { m_pDC->BitBlt(m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height(), this,m_rect.left,m_rect.top,SRCCOPY); SelectObject(m_pOldBitmap); } else { m_hDC=m_hAttribDC=NULL; } } ///////////////////////////////////////////////////////////////////////////// // CVProgressCtrl CVProgressCtrl::CVProgressCtrl() : m_nTextType(TextCustom) , m_strFormat(_T("%s")) , m_nLower(0) , m_nUpper(100) , m_nCurrentPosition(0) , m_nStep(1) , m_clrStart(RGB(255,0,0)) , m_clrEnd(RGB(0,0,255)) , m_clrBkGround(GetSysColor(COLOR_3DFACE)) , m_clrText(RGB(255,255,255)) { m_nLower=0; m_nUpper=100; m_nCurrentPosition=0; m_nStep=10; } CVProgressCtrl::~CVProgressCtrl() { } BEGIN_MESSAGE_MAP(CVProgressCtrl, CProgressCtrl) //{{AFX_MSG_MAP(CVProgressCtrl) ON_WM_ERASEBKGND() ON_WM_PAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CVProgressCtrl message handlers void CVProgressCtrl::SetRange(int nLower,int nUpper) { //This Function is to Set Range of the progress m_nLower=nLower; m_nUpper=nUpper; m_nCurrentPosition=nLower; CProgressCtrl::SetRange(nLower,nUpper); } int CVProgressCtrl::SetStep(int nStep) { m_nStep=nStep; return (CProgressCtrl::SetStep(nStep)); } BOOL CVProgressCtrl::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default return TRUE;//CProgressCtrl::OnEraseBkgnd(pDC); } void CVProgressCtrl::DrawGradient(CPaintDC *pDC, const RECT &rectClient, const int &nMaxWidth) { RECT rectFill; //显示区域 float fStep; //每一步的幅宽 CBrush brush; //显示的颜色画刷 CMemDC memDC(pDC); int r,g,b; float rStep,gStep,bStep; //得到不同颜色并相减,返回颜色之间的最大差值 r=(GetRValue(m_clrEnd)-GetRValue(m_clrStart)); g=(GetGValue(m_clrEnd)-GetGValue(m_clrStart)); b=(GetBValue(m_clrEnd)-GetBValue(m_clrStart)); //使进程条显示的总数 等于最大的颜色差值 int nSteps=max(abs(r),max(abs(g),abs(b))); //确定每一颜色填充多大的矩形区域 fStep=(float)rectClient.right/(float)nSteps; //设置每一颜色填充的步数 rStep=r/(float)nSteps; gStep=g/(float)nSteps; bStep=b/(float)nSteps; r=GetRValue(m_clrStart); g=GetGValue(m_clrStart); b=GetBValue(m_clrStart); //绘制颜色渐变的进程条 for(int iOnBand=0;iOnBand<nSteps;iOnBand++) { ::SetRect(&rectFill, //以下为填充矩形区域的左上角x,y和右下角x,y (int)(iOnBand*fStep), 0, (int)((iOnBand+1)*fStep), rectClient.bottom+1); VERIFY(brush.CreateSolidBrush(RGB(r+rStep*iOnBand,g+gStep*iOnBand,b+bStep*iOnBand))); memDC.FillRect(&rectFill,&brush); VERIFY(brush.DeleteObject()); //在结束绘制之前,使用背景色填充乘下的的客户区域 if(rectFill.right>nMaxWidth) { ::SetRect(&rectFill,rectFill.right,0,rectClient.right,rectClient.bottom); VERIFY(brush.CreateSolidBrush(m_clrBkGround)); memDC.FillRect(&rectFill,&brush); VERIFY(brush.DeleteObject()); return; } } } void CVProgressCtrl::DrawProgressText(CPaintDC& dc,RECT& rectClient) { //根据文本类型,设置显示的文本 switch(m_nTextType) { case TextPercent: m_strText.Format(m_strFormat,(int)(100*(float)m_nCurrentPosition/m_nUpper)); break; case TextPos: m_strText.Format(m_strFormat,m_nCurrentPosition); break; default://TextCustom break; } //设置文本颜色 dc.SetTextColor(m_clrText); dc.SetBkMode(TRANSPARENT); //绘制文本 dc.DrawText(m_strText,&rectClient,DT_VCENTER|DT_CENTER|DT_SINGLELINE); } void CVProgressCtrl::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here if(m_nCurrentPosition<=m_nLower||m_nCurrentPosition>=m_nUpper) { CRect rect; GetClientRect(rect); CBrush brush; brush.CreateSolidBrush(::GetSysColor(COLOR_3DFACE)); dc.FillRect(&rect,&brush); VERIFY(brush.DeleteObject()); return; } CRect rectClient; GetClientRect(rectClient); float maxWidth((float)m_nCurrentPosition/(float)m_nUpper*(float)rectClient.right); //绘制进度条 DrawGradient(&dc,rectClient,(int)maxWidth); //绘制进度条文本 DrawProgressText(dc,rectClient); // Do not call CProgressCtrl::OnPaint() for painting messages } int CVProgressCtrl::SetPos(int nPos) { //Set the Position of the Progress m_nCurrentPosition = nPos; return (CProgressCtrl::SetPos(nPos)); } void CVProgressCtrl::SetText(LPCTSTR lpcszText) { m_strText.Format(m_strFormat,lpcszText); this->Invalidate(); } void CVProgressCtrl::SetTextType(int nTextType,LPCTSTR lpszFormat) { m_nTextType = nTextType;//文本类型 m_strFormat = lpszFormat;//文本格式化 int nIndex;//内容在格式化中的位置 if (m_strFormat.GetLength() == 0) { nIndex = 0; } else { int nTemp = m_strFormat.Find(_T("%s")); if (nTemp == -1) { nIndex = m_strFormat.GetLength(); } else { nIndex = nTemp; m_strFormat.Delete(nTemp,2); while ((nTemp = m_strFormat.Find(_T("%"),nTemp)) > -1) { m_strFormat.Insert(nTemp,_T("%")); nTemp += 2; } } } //根据文本类型不同,设置格式化字符串 switch(m_nTextType) { case TextPercent: m_strFormat.Insert(nIndex,_T("%d%%")); break; case TextPos: m_strFormat.Insert(nIndex,_T("%d")); break; default: m_strFormat.Insert(nIndex,_T("%s")); break; } } 这个代码中为什么进度条到达100%后会重置为0
最新发布
06-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值