开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 355|回复: 1
收起左侧

[闲聊] 搓了个界面向导的轮子

[复制链接]

结帖率:91% (10/11)
发表于 2024-12-15 03:05:48 | 显示全部楼层 |阅读模式   重庆市重庆市
本帖最后由 神女软件定制 于 2024-12-15 03:22 编辑

QQ20241215-025931.jpg

之前发布了个软件,结果界面有点杂乱,很多人不会用

搓了个界面向导的轮子,让用户方便理解一点
功能描述:
[C++] 纯文本查看 复制代码
CUITour tour;
//添加一个帧,有两个高亮区域
tour.AddFrame(_T("第1个页面的说明"), FALSE, CRect(0, 0, 100, 24), CRect(0, 32, 100, 56));
//添加一个帧,有3个高亮区域,分别绑定到指定的子控件句柄
tour.AddFrame(_T("第2个页面的说明\r\n第二行,巴拉巴拉"), FALSE, btn1.GetSafeHwnd(),btn2.GetSafeHwnd(),btn3.GetSafeHwnd());
//添加一个帧,没有高亮区域
tour.AddFrame(_T("The End"), FALSE);
tour.CreateTour(this->GetSafeHwnd());

可以在指定窗口上,创建一个向导控件,创建之前,添加好说明和对应的高亮区域或者控件(使用AddFrame)
当它被创建的时候,它会让主窗口变灰,依次显示每个帧的说明,和对应的高亮区域

高亮区域可以指定固定的矩形,也可以指定窗口句柄(对于控件可能会移动或调解尺寸的情况),可以混合指定0个或多个
得益于C++的可变参数模板,可以接收任意数量任意类型的参数,template <typename... Types> void AddFrame(CString tip, BOOL bUnion = FALSE, Types... args)


vc开源:
头文件:

[C++] 纯文本查看 复制代码
#pragma once
#include <afxwin.h>
#include <list>
#include <vector>
using namespace std;
class CUITour :
    public CWnd
{
public:
    BOOL CreateTour(HWND hwndTarget);
    
    //目标窗口,移动,改变尺寸,需要通知过来
    void TargetChange();
    template <typename... Types> void AddFrame(CString tip, BOOL bUnion = FALSE, Types... args) {
        FRAME frame;
        frame.tip = tip;
        frame.bUnion = bUnion;
        AddToList(frame.lsItems, args...);
        m_frames.push_back(frame);
    }
    void Step(int n);
    afx_msg void OnPaint();
    afx_msg void OnDestroy();
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
    afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
    afx_msg void OnKillFocus(CWnd* pNewWnd);
private:
    HWND m_hwndTarget;
    int m_cx;
    int m_cy;
    int m_nStep;
    CDC m_dcOrgin;
    CBitmap m_mapOrgin;
    CDC m_dcGray;
    CBitmap m_mapGray;
    CFont m_font;

    struct ITEM {
        enum {
            Rect, Handle,
        }type;
        CRect rc;
        HWND hwnd;
    };
    struct FRAME {
        CString tip;
        BOOL bUnion;
        vector<ITEM> lsItems;
    };
    vector<FRAME> m_frames;
    inline void AddToList(vector<ITEM>& ls) {

    }
    inline void AddToList(vector<ITEM>& ls, HWND hwnd) {
        ls.push_back({ ITEM::Handle,CRect(),hwnd });
    }
    inline void AddToList(vector<ITEM>& ls,CRect rc) {
        ls.push_back({ ITEM::Rect,rc,NULL });
    }
    template <typename T, typename... Types>void AddToList(vector<ITEM>& ls, T value, Types... args) {
        AddToList(ls, value);
        AddToList(ls, args...);
    }

    DECLARE_MESSAGE_MAP()
    };


源文件:
[C++] 纯文本查看 复制代码
#include "pch.h"
#include "CUITour.h"

BEGIN_MESSAGE_MAP(CUITour, CWnd)
        ON_WM_PAINT()
        ON_WM_DESTROY()
        ON_WM_LBUTTONDOWN()
        ON_WM_RBUTTONDOWN()
        ON_WM_KEYDOWN()
        ON_WM_MOUSEWHEEL()

        ON_WM_KILLFOCUS()
        
END_MESSAGE_MAP()

BOOL CUITour::CreateTour(HWND hwndTarget)
{
        if (!GetSafeHwnd()) {
                if (CWnd* pTarget = CWnd::FromHandle(hwndTarget)) {
                        CRect rc;
                        pTarget->GetClientRect(&rc);
                        pTarget->ClientToScreen(&rc);
                        static LPCTSTR clsName = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW));
                        if (CreateEx(0, clsName, _T("UITour"), WS_VISIBLE | WS_POPUP, rc, pTarget, 0)) {
                                m_hwndTarget = hwndTarget;
                                m_nStep = 0;
                                CClientDC dcTemplate(this);
                                int cxScreen = GetSystemMetrics(SM_CXSCREEN);
                                int cyScreen = GetSystemMetrics(SM_CYSCREEN);
                                m_dcOrgin.CreateCompatibleDC(&dcTemplate);
                                m_mapOrgin.CreateCompatibleBitmap(&dcTemplate, cxScreen, cyScreen);
                                m_dcOrgin.SelectObject(m_mapOrgin);

                                m_dcGray.CreateCompatibleDC(&dcTemplate);
                                m_mapGray.CreateCompatibleBitmap(&dcTemplate, cxScreen, cyScreen);
                                m_dcGray.SelectObject(m_mapGray);

                                m_font.CreatePointFont(120, _T("SimHei"));
                                TargetChange();
                                ShowWindow(SW_SHOW);
                                return TRUE;
                        }
                }
        }
        return FALSE;
}

void CUITour::TargetChange()
{
        if (GetSafeHwnd()) {
                if (CWnd* pTarget = CWnd::FromHandle(m_hwndTarget)) {
                        CRect rc;
                        pTarget->GetClientRect(&rc);
                        pTarget->ClientToScreen(&rc);


                        m_cx = rc.Width();
                        m_cy = rc.Height();
                        pTarget->PrintWindow(&m_dcOrgin, PW_CLIENTONLY);
                        m_dcGray.PatBlt(0, 0, m_cx, m_cy, BLACKNESS);
                        BLENDFUNCTION blf{ 0 };
                        blf.BlendOp = AC_SRC_OVER;
                        blf.SourceConstantAlpha = 38;//36
                        m_dcGray.AlphaBlend(0, 0, m_cx, m_cy,
                                &m_dcOrgin, 0, 0, m_cx, m_cy, blf);
                        
                        
                        MoveWindow(rc);
                        InvalidateRect(NULL);//有可能并没有实际移动
                }
                else {//目标丢失
                        DestroyWindow();
                }
        }
}

void CUITour::Step(int nAdd)
{
        m_nStep += nAdd;
        if (m_nStep >= 0 && m_nStep < m_frames.size()) {
                InvalidateRect(NULL);
        }
        else {
                DestroyWindow();
        }
}


void CUITour::OnPaint()
{
        CPaintDC dc(this); // device context for painting
        // TODO: 在此处添加消息处理程序代码
        // 不为绘图消息调用 CWnd::OnPaint()
        dc.BitBlt(0, 0, m_cx, m_cy, &m_dcGray, 0, 0, SRCCOPY);
        CWnd* pTarget = CWnd::FromHandle(m_hwndTarget);
        if (m_nStep < m_frames.size()) {
                vector<CRect> rcItems;
                for (auto& e : m_frames[m_nStep].lsItems) {//采集所有矩形
                        if (e.type == ITEM::Handle) {
                                e.rc.SetRectEmpty();
                                if (CWnd* pWnd = CWnd::FromHandle(e.hwnd)) {
                                        pWnd->GetWindowRect(e.rc);
                                        ScreenToClient(e.rc);
                                }
                        }
                        rcItems.push_back(e.rc);
                }
                if (m_frames[m_nStep].bUnion) {//判断是不是联合
                        CRect rcUnion;
                        for (auto& e : rcItems) {
                                rcUnion.UnionRect(rcUnion, e);
                        }
                        rcItems.resize(1);
                        rcItems[0] = rcUnion;
                }
                

                for (auto& e : rcItems) {
                        dc.BitBlt(e.left, e.top, e.Width(), e.Height(), &m_dcOrgin, e.left, e.top, SRCCOPY);
                }
                
                list<CRect> rcLeft = { CRect(0,0,m_cx,m_cy) };
                for (auto& e : rcItems) {//分裂矩形,求出所有剩下的小矩形区域
                        for (auto& itor = rcLeft.begin(); itor != rcLeft.end();) {
                                CRect rcTemp;
                                if (rcTemp.IntersectRect(*itor, e)) {//有交集
                                        //itor表示大矩形

                                        if (itor->left < e.left) {//左
                                                rcLeft.push_back(CRect(itor->left, itor->top, e.left, itor->bottom));
                                        }
                                        if (itor->right > e.right) {//右
                                                rcLeft.push_back(CRect(e.right, itor->top, itor->right, itor->bottom));
                                        }

                                        if (itor->top < e.top) {//上
                                                rcLeft.push_back(CRect(itor->left, itor->top, itor->right, e.top));
                                        }
                                        if (itor->bottom > e.bottom) {//下
                                                rcLeft.push_back(CRect(itor->left, e.bottom, itor->right, itor->bottom));
                                        }

                                        rcLeft.erase(itor++);
                                        //break;
                                }
                                else {
                                        itor++;
                                }
                        }
                }
#if false
                TCHAR buff[128];
                wsprintf(buff, _T("空白区域数量: %d \r\n"), rcLeft.size());
                OutputDebugString(buff);
                
                for (auto& rc : rcLeft) {
                        CPen pen;
                        pen.CreatePen(PS_DASH, 1, rand());
                        dc.SelectObject(pen);
                        dc.SelectObject(::GetStockObject(NULL_BRUSH));
                        dc.Rectangle(rc);
                        dc.MoveTo(rc.left, rc.top);
                        dc.LineTo(rc.right, rc.bottom);
                        dc.MoveTo(rc.right, rc.top);
                        dc.LineTo(rc.left, rc.bottom);
                }
#endif
                CString strTip;
                strTip.Format(_T("%s\r\n\r\n< UI向导 %d/%lld >"), m_frames[m_nStep].tip, m_nStep + 1, m_frames.size());
                dc.SetBkMode(TRANSPARENT);
                dc.SetTextColor(RGB(255, 255, 255));
                dc.SelectObject(m_font);
                auto &itor=max_element(rcLeft.begin(), rcLeft.end(), [](const CRect& a, const CRect& b) {
                        return b.Width() * b.Height() > a.Width() * a.Height();
                        if (min(b.Width(), b.Height()) > min(a.Width(), a.Height())) {
                                return true;
                        }
                        else if (min(b.Width(), b.Height()) == min(a.Width(), a.Height())) {
                                if (max(b.Width(), b.Height()) > max(a.Width(), a.Height())) {
                                        return true;
                                }
                        }
                        return false;
                        });
                CRect rcOrigon = *itor; 
                CRect rc= rcOrigon;
                UINT nFormat = DT_CENTER | DT_WORDBREAK | DT_END_ELLIPSIS;
                dc.DrawText(strTip, &rc, nFormat | DT_CALCRECT);
                if (rcOrigon.Height() > rc.Height()) {
                        rcOrigon.top += (rcOrigon.Height() - rc.Height()) / 2;
                }
                
                dc.DrawText(strTip, &rcOrigon, nFormat);
        }
}

void CUITour::OnDestroy()
{
        CWnd::OnDestroy();

        // TODO: 在此处添加消息处理程序代码
        m_dcOrgin.DeleteDC();
        m_mapOrgin.DeleteObject();
        m_dcGray.DeleteDC();
        m_mapGray.DeleteObject();
        m_font.DeleteObject();
}

void CUITour::OnLButtonDown(UINT nFlags, CPoint point)
{
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        Step(1);
        CWnd::OnLButtonDown(nFlags, point);
}


void CUITour::OnRButtonDown(UINT nFlags, CPoint point)
{
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        Step(-1);
        CWnd::OnRButtonDown(nFlags, point);
}



void CUITour::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        int nAdd = 0;
        switch (nChar)
        {
        case VK_ESCAPE:
                DestroyWindow();
                return;
        case VK_LEFT:
        case VK_UP:
        case VK_PRIOR:
                nAdd = -1;
                break;
        case VK_RIGHT:
        case VK_DOWN:
        case VK_NEXT:
        case VK_SPACE:
        case VK_RETURN:
                nAdd = 1;
                break;
        case VK_HOME:
                nAdd = -m_nStep;
                break;
        case VK_END:
                nAdd = m_frames.size() - m_nStep - 1;
                break;
        default:
                break;
        }
        if(nAdd)
                Step(nAdd);
        CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}

BOOL CUITour::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        Step(zDelta < 0 ? 1 : -1);
        return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}

void CUITour::OnKillFocus(CWnd* pNewWnd)
{
        CWnd::OnKillFocus(pNewWnd);

        // TODO: 在此处添加消息处理程序代码
        //SetFocus();
#if false
        if (pNewWnd->GetSafeHwnd() == m_hwndTarget) {
                OutputDebugString(_T("主窗口得到焦点\r\n"));
        }
        else if (pNewWnd->GetSafeHwnd() == GetSafeHwnd()) {
                OutputDebugString(_T("自己得到焦点\r\n"));
        }
        else {
                TCHAR buff[128];
                wsprintf(buff, _T("焦点: %d \r\n"), pNewWnd->GetSafeHwnd());
                OutputDebugString(buff);
        }
#endif
}


有空了再转成易语言


结帖率:100% (17/17)

签到天数: 9 天

发表于 2024-12-15 16:43:16 | 显示全部楼层   河南省新乡市
有意思啊,快快转!这个好有用!
回复 支持 1 反对 0

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表