MFC带边框滚动字幕

最终效果:
这里写图片描述

方法概述:
1:先新建一个普通对话框工程,使用CRgn,CBrush对象等在对话框中画出需要的字符串显示的边框,如上图中的紫色边框就是由一个矩形CRgn和一个圆角矩形CRgn组合而成,然后截取下来,保存为位图,如需该图片,可在附件res目录下找到
2:新建Marquee工程,将上个工程里制作的位图导入到资源中当做背景图,接下来就是如下代码
3:在代码中会使用内存画布,将位图和文字全部先写到内存DC中,再输出到屏幕

过程:
一:新建一个基于对话框的MFC程序,在对话框头文件中加入以下变量,并且添加一个析构函数

class CMarqueeDlg : public CDialog
{
// Construction
public:
    CString m_str;          //跑马灯文字
    CSize m_size;           //跑马灯字符串的高宽

    CRgn m_RoundRectRgn;        //字符串显示位置的圆角矩形边框
    CRgn m_RectangleRgn;        //字符串显示位置的矩形边框,与圆角圆角语句进行组合
    CRgn m_RdRtCombineRgn;      //圆角矩形与矩形的组合区域
    CRgn m_StRdRtCbCombineRgn;  //m_RdRtCombineRgn区域与文字矩形区域的相交区域
    CRgn m_StRtCombineRgn;      //m_RectangleRgn区域与文字矩形区域的组合区域(组合方式看代码)

    CRect m_rtClient;           //窗口客户区
    CRect m_rtStatic;           //跑马灯文字的矩形区域
    CRect m_rtRoundRect;        //圆角矩形边框的矩形
    CRect m_rtRectangle;        //矩形边框

public:
    CMarqueeDlg(CWnd* pParent = NULL);  // standard constructor
    ~CMarqueeDlg();
// Dialog Data
    //{{AFX_DATA(CMarqueeDlg)
    enum { IDD = IDD_MARQUEE_DIALOG };
        // NOTE: the ClassWizard will add data members here
    //}}AFX_DATA

    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CMarqueeDlg)
    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //}}AFX_VIRTUAL

// Implementation
protected:
    HICON m_hIcon;

    // Generated message map functions
    //{{AFX_MSG(CMarqueeDlg)
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    afx_msg void OnTimer(UINT nIDEvent);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

二:修改对话框的OnPaint(..)函数

void CMarqueeDlg::OnPaint() 
{
    CPaintDC dc(this);
    m_str=TEXT("这是要在跑马灯上显示的字符串");
    SetTimer(1,200,NULL);
    m_size=dc.GetTextExtent(m_str);//获取字符串的高宽


    GetClientRect(&m_rtClient);

    //设置圆角矩形边框
    m_rtRoundRect.left=m_rtClient.left+70;
    m_rtRoundRect.right=m_rtClient.right-70;
    m_rtRoundRect.top=m_rtClient.top+(m_rtClient.Height()-m_size.cy)/2-10;
    m_rtRoundRect.bottom=m_rtRoundRect.top+45;

    //设置矩形边框
    m_rtRectangle.left=m_rtRoundRect.left-20;
    m_rtRectangle.right=m_rtRoundRect.right+20;
    m_rtRectangle.top=m_rtRoundRect.top-5;
    m_rtRectangle.bottom=m_rtRoundRect.bottom+5;

    //创建圆角矩形边框CRgn
    m_RoundRectRgn.CreateRoundRectRgn(m_rtRoundRect.left,m_rtRoundRect.top,m_rtRoundRect.right,m_rtRoundRect.bottom,10,10);

    //创建矩形边框CRgn
    m_RectangleRgn.CreateRectRgn(m_rtRectangle.left,m_rtRectangle.top,m_rtRectangle.right,m_rtRectangle.bottom);

    //创建圆角矩形边框和矩形边框的组合区域CRgn
    m_RdRtCombineRgn.CreateRectRgn(0,0,0,0);
    m_RdRtCombineRgn.CombineRgn(&m_RoundRectRgn,&m_RectangleRgn,RGN_XOR);

    //设置显示字符串的矩形边界
    m_rtStatic.left=m_rtClient.left+(m_rtClient.Width()-m_size.cx)/2;
    m_rtStatic.right=m_rtStatic.left+m_size.cx;
        //不清楚什么原因,如果不稍微加大矩形的的高度,用背景色覆盖文字的时候还是会露一点点出来,所以这里+(-)2;
    m_rtStatic.top=m_rtClient.top+(m_rtClient.Height()-m_size.cy)/2-2;//
    m_rtStatic.bottom=m_rtStatic.top+m_size.cy+2;
}

三:为对话框添加定时器消息函数OnTimer(..)

void CMarqueeDlg::OnTimer(UINT nIDEvent) 
{
    // TODO: Add your message handler code here and/or call default

    //创建创建兼容DC,并从资源中加载位图,选进兼容DC中
    CClientDC dc(this);//这个函数中只能用CClientDC而不能用CPaintDC
    CBitmap bmp;
    if(!bmp.LoadBitmap(IDB_BITMAP1))
        return;
    CDC mdc;    //客户区的兼容内存DC
    mdc.CreateCompatibleDC(&dc);
    CBitmap* pOldBmp=(CBitmap*)mdc.SelectObject(&bmp);


    //在兼容内存DC中写上文字
    mdc.SetBkMode(TRANSPARENT);
    mdc.SetTextColor(RGB(0,0,0));
//  m_mdc.DrawText(m_str,-1,&m_rtStatic,DT_CENTER);
    mdc.DrawText(m_str,m_str.GetLength(),&m_rtStatic,DT_CENTER);


    //创建m_StRtCombineRgn,即文本框超出矩形边框的部分,当文字区域滑动到边框外面时,用背景色覆盖掉超出边框的这一部分,不让文字显示
    //需要先把文字矩形框转换为CRgn类,才能与矩形边框进行组合,方便在这一部分填充背景色
    CRgn StaticRgn;
    StaticRgn.CreateRoundRectRgn(m_rtStatic.left,m_rtStatic.top,
        m_rtStatic.right,m_rtStatic.bottom,10,10);

        //真正创建m_StRtCombineRgn
    if(m_StRtCombineRgn.GetSafeHandle())
        m_StRtCombineRgn.DeleteObject();
    m_StRtCombineRgn.CreateRectRgn(0,0,0,0);
        //注意第三个参数,决定组合方式,这里组合得到的区域是只属于文本区域而不属于矩形边框区域
    m_StRtCombineRgn.CombineRgn(&StaticRgn,&m_RectangleRgn,RGN_DIFF);
        //给这一部分上色,颜色与背景色相同,用来覆盖掉这一部分的文字
        //本意:使用加载的位图即背景来覆盖掉这一部分,但使用出错,只能自己指定背景颜色即白色来填充,有知道如何改进的网友
        //如果能指点我一下怎么修改这  使用背景位图来覆盖一部分文本区域   这一部分,感激不尽!*/
    /*CBrush brTrans;
    LOGBRUSH lb={1};
    brTrans.CreateBrushIndirect(&lb);
    m_mdc.FillRgn(&m_StRtCombineRgn,&brTrans);*/
    CBrush brtmp1(RGB(255,255,255));
    mdc.FillRgn(&m_StRtCombineRgn,&brtmp1);


    /*创建m_StRdRtCbCombineRgn,即文本区域与自绘的边框的区域的组合,当文字区域滑动到这一部分的时候,
    使用边框的颜色覆盖点与边框重合的这一部分文字区域,使文字无法显示出来
    本意:边框是以位图的形式载入的,本意是无论是文字区域滑动到边框区域还是边框外部区域,都统一使用用载入的背景位图来覆盖掉与边框重合
            和超出边框的两部分文字区域,在边框内部的区域仍然显示文字,但不知道如何统一使用背景位图覆盖这两部分,只能自己根据载入的位图
            的颜色来选择这两部分的颜色,然后分别用这两个颜色填充两个区域
    如果有网友知道如何使用背景位图来覆盖掉不要的那部分文字区域,如果能指导我一下,感激不尽!*/
    if(m_StRdRtCbCombineRgn.GetSafeHandle())
        m_StRdRtCbCombineRgn.DeleteObject();
    m_StRdRtCbCombineRgn.CreateRectRgn(0,0,0,0);
    m_StRdRtCbCombineRgn.CombineRgn(&m_RdRtCombineRgn,&StaticRgn,RGN_AND);
    CBrush brtmp2(RGB(0,0,255));//原本这是边框区域的颜色,现在也用来填充与边框区域重合部分的文字区域
    mdc.FillRgn(&m_StRdRtCbCombineRgn,&brtmp2);

    //删除GDI对象
    brtmp1.DeleteObject();
    brtmp2.DeleteObject();

    //最重要的部分,将内存画布里的东西全部滑到屏幕上
    dc.BitBlt(0,0,m_rtClient.Width(),m_rtClient.Height(),&mdc,0,0,SRCCOPY);

    //控制文本区域的移动
    if(m_rtStatic.right<=m_rtRoundRect.left)
    {
        m_rtStatic.left=m_rtRoundRect.right;
        m_rtStatic.right=m_rtStatic.left+m_size.cx;
    }
    else
    {
        m_rtStatic.left-=5;
        m_rtStatic.right-=5;
    }
    CDialog::OnTimer(nIDEvent);
}

四:程序遗留问题
因为本人也是初学者,在处理文字的移动时,想要使用位图背景来覆盖掉 已超出边框的文字部分 以及 文字部分和边框部分重合的部分 ,但不知道怎么使用位图背景来覆盖掉超出文字,所以只是手动的根据位图的颜色,自己创建了两个区域(文字区域超出边框的部分,文字区域与边框重合的部分),然后手动使用了两种对应这两个区域的颜色来覆盖掉这两部分,感觉是很大的缺陷,如果能够统一地使用位图背景来统一覆盖掉这两部分,就会少很多问题,如果有网友知道怎么使用位图背景来覆盖掉这两部分,希望能在评论里或写信指导本人一下,感激不尽!!

=源程序代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值