| 网站首页 | 技术文章 | 下载频道 | 博客 | 编程论坛 |
 
| 技术教程首页 | 开发语言 | WEB开发 | .NET技术 | 数据库 | 操作系统 | 网页制作 |
 
 
您现在的位置: 编程中国 >> 技术教程 >> 开发语言 >> VC++ >> VC技术资料 >> 正文
  ►  使用ActiveMovie控件自制播放器心得(新手第一帖,请支持,谢谢)
使用ActiveMovie控件自制播放器心得(新手第一帖,请支持,谢谢)
作者:赵博闻    阅读人次:……    文章来源:本站原创    发布时间:2007/10/30    网友评论()条
 

原帖及讨论:http://bbs.bccn.net/thread-181665-1-1.html

*/ --------------------------------------------------------------------------------------
*/ 出自: 编程中国  http://www.bccn.net
*/ 作者: 赵博闻        
*/ 时间: 2007-10-29  编程论坛首发
*/ 声明: 尊重作者劳动,转载请保留本段文字
*/ --------------------------------------------------------------------------------------
我是一个VC++初学者,并没有什么了不起的建树。只是根据我一段时期的学习,和禅悦的资料自己设计了一个播放器。
想把其中的一些技术要点与各位分享。理由是,现在网络中关于这方面的事例多半残缺不全,我设计的这个虽谈不上新意,但相对全面,相信有一定的借鉴价值。另外,还有部分功能没有及时实现,还望高手们看后能给与指教,丰富其功能。在此一并谢过了。
要点:本播放器包括
功能方面:文件打开,播放,暂停,停止,全屏显示,声音控制,播放进度,播放列表
外观方面:不规则外观(可根据位图任意绘制),不规则按钮,圆形,和XP风格
文末附加存在的问题。(成果物是样式贴图,可好像显示有点问题,还请见谅)

正文:

简易播放器
第一部分:功能
1打开文件
初始化一些变量值:
CObjectPlayerDlg::CObjectPlayerDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CObjectPlayerDlg::IDD, pParent)
{
    //{{AFX_DATA_INIT(CObjectPlayerDlg)
    //}}AFX_DATA_INIT
    // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_Enable = false;
    ISPause=false;
}

利用ACTIVEMOVIECONTROL控件建立播放器,并逐步添加功能。
当点击“打开文件”时选择要播放的文件,该文件名会显示在播放列表中。原来显示的是全路经,我现在该位只显示文件名了。
void CObjectPlayerDlg::OnBtnOpen()
{
    // TODO: Add your control notification handler code here
    CString pathName,str1;
    bool bobtn = true;
    char szFileFilter[]=
        "Mp3 File(*.mp3)|*.mp3|"
        "Wma File(*.wma)|*.wma|"
        "Video File(*.dat)|*.dat|"
        "Wave File(*.wav)|*.wav|"
        "AVI File(*.avi)|*.avi|"
        "Movie File(*.mov)|*.mov|"
        "Media File(*.mmm)|*.mmm|"
        "Mid File(*.mid;*,rmi)|*.mid;*.rmi|"
        "MPEG File(*.mpeg)|*.mpeg|"
        "All File(*.*)|*.*||";
    CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,szFileFilter);
    if (dlg.DoModal()==IDOK) {
        pathName = dlg.GetPathName();
        pathName.MakeUpper();//大写
        m_ActiveMovie.SetFileName(pathName);
        int Istr;
        for (int i=0;i<pathName.GetLength();i++)
        {            
            Istr=pathName.Find(_T("\\"),i);
            if (Istr==-1)
            {    //但如果遇到路经中夹带中文就不好用了,比如“测”字中第一个字节信息会被误认为是“\”从而出错,所以路经中除了目标文件名外最后一个“\”前的字符最好都用英文
                str1=pathName.Mid(i);
                break;    
            }            
        }
    //    m_ListB.AddString(pathName);
        m_ListB.AddString(str1);
        UpdateData(FALSE);
        bobtn = false;
//如果有文件加载,使按钮有效
        m_Enable = true;
        if (m_Enable) {
            m_BtnRun.EnableWindow(TRUE);
            m_BtnUpRadio.EnableWindow(TRUE);
            m_BtnDownRadio.EnableWindow(TRUE);
            m_BtnShotStop.EnableWindow(TRUE);
            m_BtnStop.EnableWindow(TRUE);
            m_BtnAllShow.EnableWindow(TRUE);
            m_SliderRadion.EnableWindow(TRUE);        
            return;
        }
    }
    if (bobtn == true) {
        AfxMessageBox(_T("请选择要播放的文件!"));
        return;
    }
}
2全屏显示:
void CObjectPlayerDlg::OnBtnAllShow()
{
    // TODO: Add your control notification handler code here
    m_ActiveMovie.Pause();
    m_ActiveMovie.SetFullScreenMode(true);
    m_ActiveMovie.SetMovieWindowSize(SW_SHOWMAXIMIZED);
    m_ActiveMovie.Run();
}
3暂停
void CObjectPlayerDlg::OnBtnShotStop()
{
    // TODO: Add your control notification handler code here
    if (ISPause)
    {
        m_ActiveMovie.Run();
        ISPause=false;
    }
    else
    {
        m_ActiveMovie.Pause();
        ISPause=true;
    }
}
4停止
void CObjectPlayerDlg::OnBtnStop()
{
    // TODO: Add your control notification handler code here
    m_ActiveMovie.Stop();
    KillTimer(0);    
}
5增加音量
void CObjectPlayerDlg::OnBtnUpRadio()
{
    // TODO: Add your control notification handler code here
    long m_volume = m_ActiveMovie.GetVolume();
    m_ActiveMovie.Pause();
    m_ActiveMovie.SetVolume(m_volume+100);
    m_ActiveMovie.Run();    
}
6减小音量
void CObjectPlayerDlg::OnBtnDownRadio()
{
    // TODO: Add your control notification handler code here
    long m_Reduce = m_ActiveMovie.GetVolume();
    m_ActiveMovie.Pause();
    m_ActiveMovie.SetVolume(m_Reduce-100);
    m_ActiveMovie.Run();    
}
7 刚开始显示窗口时如果不加载要播放的文件就让这些按钮都失效
void CObjectPlayerDlg::OnShowWindow(BOOL bShow, UINT nStatus)
{
    CDialog::OnShowWindow(bShow, nStatus);
    
    // TODO: Add your message handler code here
    if (m_Enable==false)
    {
        m_BtnRun.EnableWindow(FALSE);
        m_BtnUpRadio.EnableWindow(FALSE);
        m_BtnDownRadio.EnableWindow(FALSE);
        m_BtnShotStop.EnableWindow(FALSE);
        m_BtnStop.EnableWindow(FALSE);
        m_BtnAllShow.EnableWindow(FALSE);
        m_SliderRadion.EnableWindow(FALSE);
    }
}
8 播放进度条。
其响应消息是
void MyPlayDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    CSliderCtrl *pSliderRun = (CSliderCtrl*)pScrollBar;
    int nRunTime = pSliderRun->GetPos();
    m_ActiveMovie.SetCurrentPosition(nRunTime);
    int MaxRange = m_ActiveMovie.GetCurrentPosition();
    m_SliderRun.SetRange(0,53);//需要得到文件长度
    m_SliderRun.SetTicFreq(10);
    m_SliderRun.SetLineSize(100);
    m_SliderRun.SetPageSize(10);


    CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
如果有多个进度条都可以在这里响应,它的执行先于控件事件。
第二部分:不规则窗体
利用CRgn类实现,真是很麻烦。
首先理清思路,我们想实现一个不规则窗体例如程序的应用界面,最好是可以按照位图形装任意创建窗体,这样更改窗体时,只要重新制作位图就可以了无需改动代码。
其次,在工程的主对话框实现该方法是不行的,因为那时整个对话框尚未生成,无法加载位图到一个不存在的区域中,
所以确定了这样的方案:
1设主对话框为A,让A调用对话框B(这时对话框以生成可以加载位图)。
2制作一个类DLGBase让B继承,原因是:要先画出图形在依图创建窗体,但是如果都写在一个对话框中会在调用Paint重绘之前先调用创建对话框的init初始化方法,无法正确实现(此处尚在讨论中)
3在DLGBase中实现位图加载,
4在B中实现CRgn创建窗体。
具体步骤:
1建立对话框B我的是IDD_MyPlayerDlg,文件名:MyPlayDlg把头文件引入主对话框中,在初始化方法中    MyPlayDlg dlg;
            dlg.DoModal();
加载位图。
2建立基于Dialog的空类DLGBase。定义一个位图对象CBitmap m_bmp;
一下需要手动该
。H
    DLGBase(UINT nID,CWnd* pParent = NULL);   // standard constructor
。Cpp
DLGBase::DLGBase(UINT nID,CWnd* pParent /*=NULL*/)
    : CDialog(nID, pParent)
{
    //{{AFX_DATA_INIT(DLGBase)
        // NOTE: the ClassWizard will add member initialization here
    //}}AFX_DATA_INIT
}

BOOL DLGBase::OnInitDialog()
{
    CDialog::OnInitDialog();
    
    // TODO: Add extra initialization here
        /*将窗体大小调整到位图大小一样*/
    m_bmp.LoadBitmap(IDB_BITMAP1);

    BITMAP bm;
    m_bmp.GetBitmap(&bm);

    CRect rtWindow;
    GetWindowRect(&rtWindow);
    rtWindow.right = rtWindow.left+bm.bmWidth;
    rtWindow.bottom =rtWindow.top +bm.bmHeight;
    MoveWindow(&rtWindow);
    
    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}

void DLGBase::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    
    // TODO: Add your message handler code here
    CDC picDC;    
    picDC.CreateCompatibleDC (&dc);
    
    CBitmap *pOldBmp;
    pOldBmp = picDC.SelectObject (&m_bmp);
    
    BITMAP bm;
    m_bmp.GetBitmap(&bm);
    //加载
       dc.BitBlt (0,0,bm.bmWidth ,bm.bmHeight,&picDC,0,0,SRCCOPY);
    dc.SelectObject(pOldBmp);        

    // Do not call CDialog::OnPaint() for painting messages
}

UINT DLGBase::OnNcHitTest(CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    //鼠标可以点击任意处移动窗体
    UINT nResult = CDialog::OnNcHitTest(point);    
    return nResult == HTCLIENT ? HTCAPTION : nResult;
}
3让MyPlayDlg继承DLGBase,注意MyPlayDlg::MyPlayDlg(CWnd* pParent /*=NULL*/)
    : DLGBase(MyPlayDlg::IDD, pParent)

void MyPlayDlg::DoDataExchange(CDataExchange* pDX)
{
    DLGBase::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(MyPlayDlg, DLGBase)
尤其是:DLGBase::OnInitDialog();//如果不把CDialog改掉仍然会先访问这里
代码:
BOOL MyPlayDlg::OnInitDialog()
{
    DLGBase::OnInitDialog();
    
    // TODO: Add extra initialization here
    CClientDC dc(this);
SetupRegion(&dc,m_bmp,RGB(255,255,255));
Return Ture;
}

void MyPlayDlg::SetupRegion(CDC *pDC, CBitmap &cBitmap, COLORREF TransColor)
{
    CDC memDC;
    //创建与传入DC兼容的临时DC
    memDC.CreateCompatibleDC(pDC);
    
    CBitmap *pOldMemBmp=NULL;
    //将位图选入临时DC
    pOldMemBmp=memDC.SelectObject(&cBitmap);
    
    CRgn wndRgn;
    //创建总的窗体区域,初始region为0
    wndRgn.CreateRectRgn(0,0,0,0);
    
    
    BITMAP bit;  
    cBitmap.GetBitmap (&bit);//取得位图参数,这里要用到位图的长和宽    
    
    int y;
    for(y=0;y<=bit.bmHeight  ;y++)
    {
        CRgn rgnTemp; //保存临时region
        
        int iX = 0;
        do
        {
            //跳过透明色找到下一个非透明色的点.
            while (iX <= bit.bmWidth  && memDC.GetPixel(iX, y) == TransColor)
                iX++;
            
            //记住这个起始点
            int iLeftX = iX;
            
            //寻找下个透明色的点
            while (iX <= bit.bmWidth  && memDC.GetPixel(iX, y) != TransColor)
                ++iX;
            
            //创建一个包含起点与重点间高为1像素的临时“region”
            rgnTemp.CreateRectRgn(iLeftX, y, iX, y+1);
            
            //合并到主"region".
            wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_OR);
            
            //删除临时"region",否则下次创建时和出错
            rgnTemp.DeleteObject();
        }while(iX <bit.bmWidth );
        iX = 0;
    }
    
    if(pOldMemBmp)
        memDC.SelectObject(pOldMemBmp);
    
    CWnd * pWnd = pDC->GetWindow();
    pWnd->SetWindowRgn(wndRgn,TRUE);    
    pWnd->SetForegroundWindow();    
        
}
效果图:

第二部分:其它
一 退出界面设计
软件要退出时应该有个退出界面,让用户确认是否真的要退出
效果图

步骤:
1在主干文件中定义退出变量,就是和工程同名的那个文件我的是ObjectPlayer。Cpp
Public:
BOOL ExitFlag;//退出标志符
2建立基于退出对话框的相关类,因为要用到程序对象App,所以在程序开始处引用对象extern CObjectPlayerApp theApp;
填写相应方法
void ExitDlg::OnOK()
{
    // TODO: Add extra validation here
    theApp.ExitFlag = TRUE;
    CDialog::OnOK();
}

void ExitDlg::OnCancel()
{
    // TODO: Add extra cleanup here
    theApp.ExitFlag = false;
    CDialog::OnCancel();
}
3在需要调用该对话框的文件中(.cpp)引入#include "ExitDlg.h"
extern CObjectPlayerApp theApp;
调用WM_Close消息
void CObjectPlayerDlg::OnClose()
{
    // TODO: Add your message handler code here and/or call default
    ExitDlg dlgExit;
    dlgExit.DoModal();//调用对话框
    if (theApp.ExitFlag) //判断标志位
    {
        CDialog::OnClose();
    }
}
二屏幕缩放
主要使用了SetMovieWindowSize();函数
属性:MovieWindowSize
说明:获得或设置电影窗口尺寸。取值为:
0 amvOriginalSize
1 amvadaoubleaoariginaSize
2 amvOneSixteenthSreen
3 amvOneFourthScreen
4 amvOneHalfScreen

三不规则按钮
查了很多资料,做得还是不理想,现在把一些重要资料节录:
二、实现原理及难点
下面我们开始类的创建,在Workspace的ClassView页中右击列表树的根结点,选择New Class…

在弹出窗口中进行派生类的定义,如下图所示,注意,你需要填写的只有Name和Base class两项,其余的选项保持默认值就可以了。

按OK按钮退出之后,我们可以在ClassView里面看到新创建的类的名字。接下来我们可以为CXPButton类添加各种成员变量。因为自绘控件说穿了就是画图,所以在成员变量中可以看到各种与画图有关的数据类型,一般来说成员变量会在类的构造函数中初始化,在类的析构函数中销毁。详细代码请参见本篇附带的源程序。
下面简要叙述一下按钮的实现原理:
1. 在控件初始化时为按钮添加Owner Draw的属性。这是因为在MFC中,要想激活控件的自绘功能,要求该控件的属性中必须包含属性值BS_OWNERDRAW,这一步我们可以通过类向导为CXPButton类添加PreSubclassWindow()函数,在该函数中完成属性值的设置。当激活控件的自绘功能之后,每次控件状态改变的时候都会运行函数DrawItem(),该函数的作用就是绘制控件在各种状态下的外观。
2. 添加WM_MOUSELEAVE消息函数,当鼠标指针离开按钮时,触发该消息函数,我们在函数中添加代码,通知DrawItem函数鼠标指针已经离开了,让按钮重绘。
3. 添加WM_MOUSEHOVER消息函数,当鼠标指针位于按钮之上时,触发该消息函数,我们在函数重添加代码,通知DrawItem函数鼠标指针现在正在按钮的上面,让按钮重绘。
4. 添加DrawItem函数。在DrawItem中根据按钮当前的状态绘制按钮的外观。可以说自绘控件的大部分功能都是在这个函数中实现的。DrawItem函数包含了一个LPDRAWITEMSTRUCT的指针,本篇会在稍后予以讲解。
了解了基本的设计思路之后,剩下就看我们怎么去实现了。我本人觉得这里有两个难点,首先是WM_MOUSELEAVE和WM_MOUSEHOVER不是标准的Windows消息函数,它们不能通过类向导来添加,所有的添加工作都需要通过手工输入代码来完成。另一个难点是DrawItem中的LPDRAWITEMSTRUCT指针,它指向了一个DRAWITEMSTRUCT的结构,这个结构中包含了控件的各种细节,为我们提供了实现自绘功能的必要信息。
难点一:
事实上WM_MOUSELEAVE和WM_MOUSEHOVER两个Windows消息是通过WM_MOUSEMOVE消息触发的,而WM_MOUSEMOVE是标准的Windows消息,因此我们可以通过类向导来为CXPButton类添加WM_MOUSEMOVE消息函数。

函数的代码见如下,这段代码非常有用,在其它的自绘控件中,如果想触发WM_MOUSELEAVE和WM_MOUSEHOVER消息,也是使用类似的方法实现的。
void CXPButton::OnMouseMove(UINT nFlags, CPoint point)
{
       // TODO: Add your message handler code here and/or call default
       if (!m_bTracking)
       {
              TRACKMOUSEEVENT tme;
              tme.cbSize = sizeof(tme);
              tme.hwndTrack = m_hWnd;
              tme.dwFlags = TME_LEAVE | TME_HOVER;
              tme.dwHoverTime = 1;
              m_bTracking = _TrackMouseEvent(&tme);
       }
       CButton::OnMouseMove(nFlags, point);
}
我们接着添加WM_MOUSELEAVE和WM_MOUSEHOVER消息消息函数。在CXPButton类的声明中(即在XPButton.h文件中)找到afx_msg void OnMouseMove(UINT nFlags, CPoint point);的函数声明,紧接其下输入
afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);
然后在XPButton.cpp文件中找到ON_WM_MOUSEMOVE(),紧接其后输入
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
当然最后还有函数的实现了,详细代码可见本篇提供的源程序,这里就不再重复了。

难点二:
下面我们看看DRAWITEMSTRUCE结构为我们提供了哪些有用信息呢?
DRAWITEMSTRUCT结构的定义如下:
typedef struct tagDRAWITEMSTRUCT {
    UINT   CtlType;                       //控件类型
    UINT   CtlID;                          //控件ID
    UINT   itemID;                        //菜单项、列表框或组合框中某一项的索引值
    UINT   itemAction;                   //控件行为
    UINT   itemState;                     //控件状态
    HWND   hwndItem;                 //父窗口句柄或菜单句柄
    HDC    hDC;                           //控件对应的绘图设备句柄
    RECT   rcItem;                        //控件所占据的矩形区域
    DWORD  itemData;                  //列表框或组合框中某一项的值
} DRAWITEMSTRUCT, *PDRAWITEMSTRUCT, *LPDRAWITEMSTRUCT;
其实不仅是按钮控件,其它控件,如ComboBox、ListBox、StaticText等都是通过DRAWITEMSTRUCT来记录控件信息的。关于这个结构的详细文档可参考本篇的附录。

也许你早已看到许多自绘按钮的例子,实际上自绘按钮本身的函数结构都是差不多的,它们显示效果的区别主要取决于代码编写者对GDI作图函数的运用与掌握程度。有兴趣的朋友可以研究一下CXPButton类中DrawItem函数的数据结构,其实只要修改一下其中GDI绘图函数的部分代码,马上又能做出另一个自绘按钮控件了。

下面是我实现的代码:
在头文件里定义变量,在主干文件里为其赋初值
.H
Public:
    BOOL m_bTracking;    //在鼠标按下没有释放时该值为true
    BOOL m_bOver;    //鼠标位于按钮之上时该值为true,反之为flase
    BOOL m_bFocus;    //按钮为当前焦点所在时该值为true
    BOOL m_bSelected;    //按钮被按下是该值为true
.Cpp
DremBtn::DremBtn()
{
    m_bOver = m_bSelected  = m_bFocus = FALSE;
    m_bTracking = FALSE;
}
//调用BS_OWNERDRAW通知按钮自绘
void DremBtn::PreSubclassWindow()
{
    // TODO: Add your specialized code here and/or call the base class
    
    CButton::PreSubclassWindow();
    ModifyStyle(0, BS_OWNERDRAW);
}
//鼠标移动事件消息
void DremBtn::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    if (!m_bTracking) {
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(tme);
            tme.hwndTrack = m_hWnd;
            tme.dwFlags = TME_HOVER|TME_LEAVE;
            tme.dwHoverTime = 1;
            m_bTracking = _TrackMouseEvent(&tme);
        }
    CButton::OnMouseMove(nFlags, point);
}
LRESULT DremBtn::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
    m_bOver = FALSE;
    m_bTracking = FALSE;
//    m_bSelected = FALSE;
    InvalidateRect(NULL, FALSE);//更新region
    return 0;
}

LRESULT DremBtn::OnMouseHover(WPARAM wParam, LPARAM lParam)
{
    m_bOver = TRUE;
    
//    m_bSelected = TRUE;
    m_bTracking = TRUE;
    InvalidateRect(NULL);
    return 0;
}
//绘图
void DremBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    // TODO: Add your code to draw the specified item
        CDC BtnDC;
    CDC MemDC;
    CBitmap bmp;
    BITMAP bm;
    CRect rect;
    CDC *pDC=CDC::FromHandle(lpDrawItemStruct->hDC);
    UINT state = lpDrawItemStruct->itemState;
    rect =  lpDrawItemStruct->rcItem;
    int nSaveDC=pDC->SaveDC();
        TCHAR strText[MAX_PATH + 1];
    ::GetWindowText(m_hWnd, strText, MAX_PATH);

//圆形区域
//    CRgn rgn;
//    rgn.CreateEllipticRgnIndirect(rect);
//    SetWindowRgn(rgn,TRUE);
    //获取按钮的状态
    if (state & ODS_FOCUS)
    {
        m_bFocus = TRUE;
        m_bSelected = TRUE;
    }
    else
    {
        m_bFocus = FALSE;
        m_bSelected = FALSE;
    }    
    if (state & ODS_SELECTED || state & ODS_DEFAULT)
    {
        m_bFocus = TRUE;
    }
//    pDC->SelectObject(hOldPen);
    
    rect.DeflateRect(CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)));
    
    if (m_bTracking) //m_bOver
    {
        BtnDC.Attach(lpDrawItemStruct->hDC);
        MemDC.CreateCompatibleDC(&BtnDC);
        bmp.LoadBitmap(IDB_BITRun1);
        
        bmp.GetBitmap(&bm);
        CBitmap *pOld = MemDC.SelectObject(&bmp);
        //在按钮DC中绘制
        BtnDC.StretchBlt(rect.left,rect.top,rect.right,rect.bottom,
            &MemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
        //LoadTransBitmap(&MemDC,bmp,RGB(255,255,255));

        //还原旧GDI对象
        MemDC.SelectObject(pOld);
        bmp.DeleteObject();
    }
    else
    {
        BtnDC.Attach(lpDrawItemStruct->hDC);
        MemDC.CreateCompatibleDC(&BtnDC);
        bmp.LoadBitmap(IDB_BITRun2);
        bmp.GetBitmap(&bm);

        CBitmap *pOld = MemDC.SelectObject(&bmp);
        //在按钮DC中绘制
        BtnDC.StretchBlt(rect.left,rect.top,rect.right,rect.bottom,
            &MemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
        //LoadTransBitmap(&MemDC,bmp,RGB(255,255,255));

        //还原旧GDI对象
        MemDC.SelectObject(pOld);
        bmp.DeleteObject();
    }
//显示按钮的文本
    if (strText!=NULL)
    {
        CFont* hFont = GetFont();//获取字体
        CFont* hOldFont = pDC->SelectObject(hFont);
        CSize szExtent = pDC->GetTextExtent(strText, lstrlen(strText));

        CPoint pt( rect.CenterPoint().x - szExtent.cx / 2, rect.CenterPoint().y - szExtent.cy / 2);
        //移动
        if (state & ODS_SELECTED)
            pt.Offset(1, 1);
        int nMode = pDC->SetBkMode(TRANSPARENT);
        if (state & ODS_DISABLED)//被禁用时
            pDC->DrawState(pt, szExtent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL);
        else
            pDC->DrawState(pt, szExtent, strText, DSS_NORMAL, TRUE, 0, (HBRUSH)NULL);
        pDC->SelectObject(hOldFont);
        pDC->SetBkMode(nMode);
    }
    pDC->RestoreDC(nSaveDC);
    
}
//擦除背景
BOOL DremBtn::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Add your message handler code here and/or call default
    
    //return CButton::OnEraseBkgnd(pDC);
    return TRUE;
}
如果要实现以下效果需要对位图进行透明处理,否则会有边缘残留!
成果物:
    
四消除屏幕闪烁
ActiveMovie控件关心的只是起始点和拖拽大小无关,即控件左上角那点。
做法:我先得到一个区域,然后得到该区域的长和宽,最后在指定的区域内绘图。这样就不会因为重绘背景时在原来的控件寓于内闪烁了
BOOL MyPlayDlg::OnEraseBkgnd(CDC* pDC)
{
//椭圆形窗体
    CRgn m_rgn;
    m_rgn.CreateEllipticRgn(55,70,455,330);
    m_ActiveMovie.SetWindowRgn(m_rgn,TRUE);
    int nWindth,nHeight;
    nWindth = 405;
    nHeight = 260;
    CDC MenDC;
MenDC.CreateCompatibleDC(NULL);//背景画刷为空
    MenDC.FillSolidRect(0,0,nWindth,nHeight,RGB(0,222,0));//用绿色填充整片区域,但由于背景画刷已经射为空,其实是没用的
    MenDC.DeleteDC();
    return TRUE;
}
//初步解决得了闪烁问题但灵活性很差。
最后效果:
把播放器的启动函数
CMyPlayer dlg;
dlg.DoModal();
放在主程序的初始化函数中,使之一运行就跳过主对话框直接显示。
成果物:
  
第三部分 遗留问题和总结
1播放器中还希望加入截屏功能,可控件ActiveMovie在选择播放区域的问题上表现得并不好,没能找到很好的区域选择函数。实现后的效果是截下了整个屏幕。
2提高不规则按钮的设计。主要方向要放在鼠标响应事件的选取上。使之完善。
综上:是本次设计项目的简要概括和技术要点。不足之处还很多。
请大家发表意见,帮我提高,谢谢。

 

 
文章录入:编辑01    责任编辑:编辑01 
  • 上一篇文章:

  • 下一篇文章:

  •  
    相关文章
    原创地带
    24小时热门帖子