半透明信息显示浮动窗口的实现

快乐鹦鹉 原文 半透明信息显示浮动窗口的实现

   实现目的

在一些画图软件中,经常需要向用户展示鼠标移动到的位置的对象的一些参数信息。此时,完成一个交互性友好的信息显示界面就相当的重要了。因为一个软件的好坏,在用户的眼中,第一感觉甚至是第一重要的就是视觉效果和可操作性。当然,软件本身的稳定性和效率也很重要。特别对于产品性的软件,在用户展示时,一个优秀的界面效果可以大大加深软件在用户心里的印象分。

       功能简介

本功能是作者根据自身软件在用户实际使用过程中对交互性的更高要求而开发的。浮动窗口其实是一个对话框,设置为无标题的风格,然后进行自绘制而成。能够根据需要显示的内容自动调整窗口的大小,保证正好能够容纳需要显示的内容。如以下效果:

图中黄色条为栏目分隔,同时显示其下内容所属的信息域。如图中表示下方信息为台风即时预报信息的内容。下方信息分成两栏,栏间绘制线条进行分割。左侧为信息标题,右侧为信息内容。比如台风名称是奥麦斯。

如果鼠标移动的位置,同时选中多项内容,那么,提示信息可以同时包括多个栏目,如下图:

本图中包括两个栏目,即本公司船位置信息和美气导的等压线信息。从理论上讲,本功能可以支持任意多的栏目,前提是你的显示器能够容纳得下。

整个界面的效果是半透明的。包括透明度,字体大小和信息内容的颜色都可以由用户自行定义,配置界面如下:

透明度范围为0-255;背景色列表框是作者在博客中已提供的一个自定义的线条、填充和颜色选择列表控件。

修改完成后,可以改变浮动窗口的显示效果,比如一种修改后的效果:

这样,即使用户对浮动窗口内容的展示方式不满意,那么也完全可以由用户自行定义自己喜欢的风格。当然,在此基础上,可以进一步扩大自定义的范围。本例中,栏目分割条的背景色和信息内容标题文字的颜色是固定的,信息内容文字的颜色是背景色的反色,这些都可以考虑加入到自定义项中。

代码实现

以下是消息提示项的数据结构:

typedef struct _MSG_TIP//消息提示数据项描述信息
{
   CString sMsgItemName;  //信息标题
   CString sMsgItemInfo;   //信息内容
   COLORREF nInfoShowColor; //显示颜色(此项值目前暂时没有使用,原意是使每个栏目的文字颜色均可自定义;因为考虑到设定的颜色可能和背景色冲突,因此目前文字颜色使用背景色的反色)
} MSG_TIP;  

typedef CArray<MSG_TIP,MSG_TIP&> CMsgTipArray;//消息提示内容  

头文件,MsgTipDlg.h

#if !defined(AFX_MSGTIPDLG_H__9759E9A8_93DE_4003_BCB2_87F390641270__INCLUDED_)
#define AFX_MSGTIPDLG_H__9759E9A8_93DE_4003_BCB2_87F390641270__INCLUDED_  

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// MsgTipDlg.h : header file
//  

/////////////////////////////////////////////////////////////////////////////
// CMsgTipDlg dialog  

class CMsgTipDlg : public CDialog
{
// Construction
public:
    CMsgTipDlg(CWnd* pParent = NULL);   // standard constructor
    CSize ShowMsgTip(CMsgTipArray &arMsgTip); //供外部函数传递需要显示的内容,并计算尺寸
    void UpdateTrans();     //修改透明度等配置参数
    void DrawMsgTip();      //绘制提示信息
// Dialog Data
    //{{AFX_DATA(CMsgTipDlg)
    enum { IDD = IDD_MSGTIP_DLG };
    //}}AFX_DATA  

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CMsgTipDlg)
    public:
    virtual BOOL PreTranslateMessage(MSG* pMsg);
    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //}}AFX_VIRTUAL  

// Implementation
protected:
    CMsgTipArray m_arMsgTip;    //提示信息数组
    CFont   m_MsgTipFont;       //用于消息提示的字体
    int     m_nTipNameWidth;    //标题名最大宽度,决定竖线绘制位置
    int     m_nRowHeight;       //行高,决定横线绘制位置
private:
    void DrawGrid(CDC *pDC,CRect &rc);                      //绘制网格
    void DrawMsgTip(CDC *pDC,CPoint topPos,MSG_TIP &mt);    //绘制提示信息(某一条)
    void DrawMsg(CDC *pDC);                                 //绘制信息  

    // Generated message map functions
    //{{AFX_MSG(CMsgTipDlg)
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};  

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.  

#endif // !defined(AFX_MSGTIPDLG_H__9759E9A8_93DE_4003_BCB2_87F390641270__INCLUDED_)  

CPP文件,MsgTipDlg.cpp

    // MsgTipDlg.cpp : implementation file
//  

#include "stdafx.h"
#include "MsgTipDlg.h"  

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif  

/////////////////////////////////////////////////////////////////////////////
// CMsgTipDlg dialog  

CMsgTipDlg::CMsgTipDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CMsgTipDlg::IDD, pParent)
{
    //{{AFX_DATA_INIT(CMsgTipDlg)
    //}}AFX_DATA_INIT
}  

void CMsgTipDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CMsgTipDlg)
    //}}AFX_DATA_MAP
}  

BEGIN_MESSAGE_MAP(CMsgTipDlg, CDialog)
    //{{AFX_MSG_MAP(CMsgTipDlg)
    ON_WM_PAINT()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()  

/////////////////////////////////////////////////////////////////////////////
// CMsgTipDlg message handlers  

BOOL CMsgTipDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    m_MsgTipFont.CreateFont(
        theApp.m_sysINIFile.GetMsgTipConfig().nMsgTipFontSize,// nHeight
        0,                         // nWidth
        0,                         // nEscapement
        0,                         // nOrientation
        FW_SEMIBOLD,                  // nWeight
        FALSE,                     // bItalic
        FALSE,                     // bUnderline
        0,                         // cStrikeOut
        DEFAULT_CHARSET,              // nCharSet
        OUT_DEFAULT_PRECIS,        // nOutPrecision
        CLIP_DEFAULT_PRECIS,       // nClipPrecision
        DEFAULT_QUALITY,           // nQuality
        DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily
        "Times New Roman");//"MS Sans Serif");//Arial
    m_nTipNameWidth = 0;
    m_nRowHeight = 0;     

    SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,
    GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)^0x80000);
    //WS_EX_LAYERED
    HINSTANCE hInst = LoadLibrary("User32.DLL");
    if(hInst)
    {
        typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);
        MYFUNC fun = NULL;
        //取得SetLayeredWindowAttributes函数指针
        fun=(MYFUNC)GetProcAddress(hInst, "SetLayeredWindowAttributes");
        if(fun)
            //fun(this->GetSafeHwnd(),0,400,2);
            fun(this->GetSafeHwnd(),0,theApp.m_sysINIFile.GetMsgTipConfig().nMsgTipTrans,2);
        FreeLibrary(hInst);
    }  

    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}  

//供外部函数传递需要显示的内容,同时获得需要显示的内容的尺寸,以决定本界面的大小
CSize CMsgTipDlg::ShowMsgTip(CMsgTipArray &arMsgTip)
{
    CSize size(0,0);
    //////////////////////////////////////
    m_arMsgTip.Copy(arMsgTip);  

    int nSize = m_arMsgTip.GetSize();
    if(nSize == 0)
        return size;  

    CDC *pDC = GetDC();
    pDC->SelectObject(&m_MsgTipFont);
    int nMaxNameLen = 0;
    int nMaxInfoLen = 0;
    int nMaxTitleLen = 0;
    CSize sz;
    for(int i=0; i<nSize; i++)
    {
        MSG_TIP mt = m_arMsgTip.GetAt(i);
        if(mt.nInfoShowColor == -1)
        {
            ::GetTextExtentPoint32(pDC->m_hDC, mt.sMsgItemName,mt.sMsgItemName.GetLength(),&sz);
            if(sz.cx > nMaxTitleLen)
                nMaxTitleLen = sz.cx;
            continue;
        }
        ::GetTextExtentPoint32(pDC->m_hDC, mt.sMsgItemName,mt.sMsgItemName.GetLength(),&sz);
        if(sz.cx > nMaxNameLen)
            nMaxNameLen = sz.cx;
        if(mt.sMsgItemInfo.GetLength() == 0)
            continue;
        else
        {
            ::GetTextExtentPoint32(pDC->m_hDC, mt.sMsgItemInfo,mt.sMsgItemInfo.GetLength(),&sz);
            if(sz.cx > nMaxInfoLen)
                nMaxInfoLen = sz.cx;
        }  

    }
    m_nTipNameWidth = nMaxNameLen + 8;
    m_nRowHeight = sz.cy + 4;
    //
    int nTitleWidth = nMaxTitleLen;
    size.cx = m_nTipNameWidth + nMaxInfoLen+5;
    if(size.cx < nTitleWidth)
        size.cx = nTitleWidth;
    size.cx += 5;
    size.cy = m_nRowHeight * nSize;
    ReleaseDC(pDC);
    return size;
}  

void CMsgTipDlg::OnPaint()
{
    CPaintDC dc(this); // device context for painting  

    DrawMsg(&dc);  

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

void CMsgTipDlg::DrawMsg(CDC *pDC)
{
    CRect rc;
    GetClientRect(&rc);
    CDC memDC;
    memDC.CreateCompatibleDC(pDC);
    CBitmap bmp;
    bmp.CreateCompatibleBitmap(pDC,rc.Width(),rc.Height());
    CBitmap *pOldBmp = memDC.SelectObject(&bmp);
    memDC.FillSolidRect(rc,theApp.m_sysINIFile.GetMsgTipConfig().nMsgTipBkColor);
    CFont *pOldFont = memDC.SelectObject(&m_MsgTipFont);  

    DrawGrid(&memDC,rc);
    //
    int nSize = m_arMsgTip.GetSize();
    CPoint topPos(0,0);
    for(int i=0; i<nSize; i++)
    {
        MSG_TIP mt = m_arMsgTip.GetAt(i);
        DrawMsgTip(&memDC,topPos,mt);
        topPos.y += m_nRowHeight;
    }
    pDC->BitBlt(0, 0, rc.Width(),rc.Height(), &memDC, 0, 0, SRCCOPY) ;
    pDC->SelectObject(pOldBmp);
    pDC->SelectObject(pOldFont);
    bmp.DeleteObject();
    memDC.DeleteDC();
}  

void CMsgTipDlg::DrawMsgTip(CDC *pDC, CPoint topPos, MSG_TIP &mt)
{
    int nBkMode = pDC->SetBkMode(TRANSPARENT);
    if(mt.nInfoShowColor == -1)
    {
        CRect rc;
        GetClientRect(&rc);
        CRect rowRc(topPos,CSize(rc.Width(),m_nRowHeight+2));
        pDC->FillSolidRect(rowRc,COLOR_BUFF);
        COLORREF nTC = pDC->SetTextColor(COLOR_MAGENTA);
        pDC->TextOut(topPos.x+4,topPos.y+4,mt.sMsgItemName);
        pDC->SetTextColor(nTC);
    }
    else
    {
        COLORREF nTC = pDC->SetTextColor(COLOR_MAGENTA);
        pDC->TextOut(topPos.x+4,topPos.y+4,mt.sMsgItemName);
        nTC = pDC->SetTextColor(COLOR_WHITE-theApp.m_sysINIFile.GetMsgTipConfig().nMsgTipBkColor);//mt.nInfoShowColor);//
        pDC->TextOut(topPos.x+m_nTipNameWidth+8,topPos.y+4,mt.sMsgItemInfo);
        pDC->SetTextColor(nTC);
    }
    pDC->SetBkMode(nBkMode);
}  

void CMsgTipDlg::DrawGrid(CDC *pDC,CRect &rc)
{
    CPen pen(PS_SOLID,1,COLOR_BUFF);
    CPen *pOldPen = pDC->SelectObject(&pen);
    CPoint ptTop(m_nTipNameWidth,0);
    CPoint ptBottom(m_nTipNameWidth,rc.bottom);
    pDC->MoveTo(ptTop);
    pDC->LineTo(ptBottom);
    //
    int nSize = m_arMsgTip.GetSize();
    for(int i=0; i<nSize-1; i++)
    {
        CPoint ptLeft(0,(m_nRowHeight)*(i+1)+2);
        CPoint ptRight(rc.right,(m_nRowHeight)*(i+1)+2);
        pDC->MoveTo(ptLeft);
        pDC->LineTo(ptRight);
    }
    pDC->SelectObject(pOldPen);
}  

void CMsgTipDlg::UpdateTrans()
{
    HINSTANCE hInst = LoadLibrary("User32.DLL");
    if(hInst)
    {
        typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);
        MYFUNC fun = NULL;
        //取得SetLayeredWindowAttributes函数指针
        fun=(MYFUNC)GetProcAddress(hInst, "SetLayeredWindowAttributes");
        if(fun)
            //fun(this->GetSafeHwnd(),0,400,2);
            fun(this->GetSafeHwnd(),0,theApp.m_sysINIFile.GetMsgTipConfig().nMsgTipTrans,2);
        FreeLibrary(hInst);
    }
    if(m_MsgTipFont.m_hObject != NULL)
    {
        m_MsgTipFont.DeleteObject();
        m_MsgTipFont.CreateFont(
            theApp.m_sysINIFile.GetMsgTipConfig().nMsgTipFontSize,// nHeight
            0,                         // nWidth
            0,                         // nEscapement
            0,                         // nOrientation
            FW_SEMIBOLD,                  // nWeight
            FALSE,                     // bItalic
            FALSE,                     // bUnderline
            0,                         // cStrikeOut
            DEFAULT_CHARSET,              // nCharSet
            OUT_DEFAULT_PRECIS,        // nOutPrecision
            CLIP_DEFAULT_PRECIS,       // nClipPrecision
            DEFAULT_QUALITY,           // nQuality
            DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily
            "Times New Roman");//Arial  }
    }
}  

void CMsgTipDlg::DrawMsgTip()
{
    CDC *pDC = GetDC();
    DrawMsg(pDC);
    ReleaseDC(pDC);
}  

//鼠标不能移动到本界面,一旦移动到本界面,立即隐藏
BOOL CMsgTipDlg::PreTranslateMessage(MSG* pMsg)
{
    if(pMsg->message == WM_MOUSEMOVE)
    {
        ShowWindow(SW_HIDE);
    }
    return CDialog::PreTranslateMessage(pMsg);
}  

那么,剩下的工作,就是需要在每个实体类中增加类似这样的函数:

CMsgTipArray arMsgTip;
    pDoc->m_pShipMan->GetMsgTipInfo(arMsgTip);
在OnMouseMove事件中,从各个实体类中获取对象选中的情况。

如下是一个例子:

     void CShip::GetMsgTipInfo(CMsgTipArray &arMsgTip)
{
    MSG_TIP mt;
    mt.nInfoShowColor = -1;
    mt.sMsgItemName = "它船位置信息";
    mt.sMsgItemInfo = "";
    arMsgTip.Add(mt);
    mt.sMsgItemInfo = m_ShipBaseInfo.sEngName;
    mt.sMsgItemName = "船舶名称";
    mt.nInfoShowColor = COLOR_GRASS;
    arMsgTip.Add(mt);
    mt.sMsgItemInfo = m_ShipBaseInfo.sMMSIID;
    mt.sMsgItemName = "MMSI";
    arMsgTip.Add(mt);
    CString sDate = m_ShipBaseInfo.tNowReptTime.Format("%Y-%m-%d %H:%M:%S");
    mt.sMsgItemInfo = sDate;
    mt.sMsgItemName = "报告时间";
    arMsgTip.Add(mt);
    CString sLat = CoordFormatDouble2Str(m_ShipBaseInfo.nowPos.dLatitude,true,theApp.m_sysINIFile.GetLatLongConfig().eLatLongFmt);
    CString sLong = CoordFormatDouble2Str(m_ShipBaseInfo.nowPos.dLongitude,false,theApp.m_sysINIFile.GetLatLongConfig().eLatLongFmt);
    mt.sMsgItemInfo = sLat;
    mt.sMsgItemName = "纬度";
    arMsgTip.Add(mt);
    mt.sMsgItemInfo = sLong;
    mt.sMsgItemName = "经度";
    arMsgTip.Add(mt);
    if(m_ShipBaseInfo.fNowCOG > 359.9)
        mt.sMsgItemInfo = "NA";
    else
        mt.sMsgItemInfo.Format("%.1f°",m_ShipBaseInfo.fNowCOG);
    mt.sMsgItemName = "航向";
    arMsgTip.Add(mt);
    if(m_ShipBaseInfo.fNowSOG > 102)
        mt.sMsgItemInfo = "NA";
    else
        mt.sMsgItemInfo.Format("%.1f",m_ShipBaseInfo.fNowSOG);
    mt.sMsgItemName = "航速";
    arMsgTip.Add(mt);
    if(m_ShipBaseInfo.wTrueHeading == 511)
        mt.sMsgItemInfo = "NA";
    else
        mt.sMsgItemInfo.Format("%d°",m_ShipBaseInfo.wTrueHeading);
    mt.sMsgItemName = "船首向";
    arMsgTip.Add(mt);
    if(abs(m_ShipBaseInfo.lROT) >= 720)
        mt.sMsgItemInfo = "NA";
    else
        mt.sMsgItemInfo.Format("%d°/分",m_ShipBaseInfo.lROT);
    mt.sMsgItemName = "转向率";
    arMsgTip.Add(mt);
    mt.sMsgItemName = "船位来源";
    mt.sMsgItemInfo = m_ShipBaseInfo.sPosFromName;
    if(mt.sMsgItemInfo.GetLength() == 0)
        mt.sMsgItemInfo = m_ShipBaseInfo.sPosFromCode;
    arMsgTip.Add(mt);
}  

以上工作即可实现一个浮动的鼠标移动选中的对象的半透明的提示窗。当鼠标移开或者移动到提示窗上时,提示窗将立刻隐藏。

时间: 2024-10-01 06:41:27

半透明信息显示浮动窗口的实现的相关文章

简单的浮动窗口

简单的浮动代码,纯js代码,但不符合w3c标准,在有些HTML规范下不可行, 例如: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 具体原因不清楚,待大神告知. 完整代码如下(字母为背景测试): <html> <head> <me

(转)JS浮动窗口(随浏览器滚动而滚动)

原文:http://hi.baidu.com/aiyayaztt/item/4201c55a6b729dced2e10c79 JS浮动窗口(随浏览器滚动而滚动) 往往用于一些联系方式,互动平台模块,随着浏览器的滚动而滚动. <div id="AdLayer"> <p>窗口中的内容</p> </div> 放在<body>下面(页面最上面) JS代码 window.onload=function(){ var n=0;//top值

Delphi 悬浮窗口、浮动窗口的实现

源:Delphi 悬浮窗口.浮动窗口的实现 浮动窗体的实现 http://blog.tianya.cn/blogger/post_show.asp?BlogID=68097&PostID=806089 需要一个这样的窗体:a:没有标题栏; b:可以改变大小; c:不在任务栏上显示图标; d:如果不是主窗体,它的最小化不受主窗体的限制; e:在最顶端显示 处理:a:不在任务栏出现的,只要设置窗体的属性为ToolWindow就可以了.b:要想使窗体脱离主窗体的限制,必须修改它的ParentWnd,使

Android浮动窗口的实现

1.浮动窗口的实现原理 看到上图的那个小Android图标了吧,它不会被其他组建遮挡,也可以响应用户的点击和拖动事件,它的显示和消失由WindowManager直接管理,它就是Android浮动窗口.Android浮动窗口的实现主要是靠WindowManager这个类.通过WindowManager类的addView(),updateViewLayout(),removeView()这几个方法,我们可以直接在Window中添加,更新,移除View. 2.浮动窗口实现的具体步骤 1)既然浮动窗口的

android 浮动窗口学习笔记及个人理解(仿360手机助手)

非常感谢原文作者 http://blog.csdn.net/guolin_blog/article/details/8689140 经自己理解 程序运行界面如下图: 1.程序入口界面 2.小浮动窗口 3.大浮动窗口 由上图可看出,可以看出我们基本需要: 1.一个主Activity 2.小浮动窗口view界面 3.大浮动窗口view界面 对于浮动窗口的管理我们还需要 4.一个Service(在后台监控管理浮动窗口的状态) 5.窗口管理类(创建/消除浮动窗口) 代码: package com.ww.

极简的css浮动窗口

<!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="renderer" content="webkit"> <meta name="vi

appcan 多窗口机制 主窗口与浮动窗口通讯

1:打开一个浮动窗口并命名为addGoods_0 uexWindow.openPopover( "addGoods_0","1","addGoods_0_content.html","",0,titHeight,$("#content").width(), $("#content").height(),32,"0" ); 2 执行浮动窗口定义的函数 uexWindow

delphi浮动窗口

像Photoshop一样的面板窗体,面板窗体与主窗体都处于激活状态. 用Spy & Capture查看一下就不难发现,这些面板窗体的Parent Window都是Photoshop的主窗体(以Photoshop CS为例,主窗体的Handle是001906D8,所有面板的Parent Window指向的就是001906D8),而一般我们创建的窗体的Parent是为None的. 下面的代码就为实现这种浮动窗口的示范: var Form1: TForm1; implementation uses U

QT+ 状态栏+核心控件+浮动窗口

#include "mainwindow.h" #include <QStatusBar> #include <QLabel> #include<QTextEdit> #include <QDockWidget>//浮动窗口所需的头文件 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { this ->setFixedSize(520,590); //状态栏