跳转至

windows程序

ObjectARX各版本免费下载

https://blog.csdn.net/lhxy24/article/details/101058720

ObjectARX 2020 Wizard

AutoCAD 2020 DotNet Wizard

ObjectARX2020 bug修改

1、C:\Program Files (x86)\Autodesk\ObjectARX 2020 Wizards\ArxAppWiz\Scripts\1033\default.js

从C:\Program Files (x86)\Autodesk\ObjectARX 2020 Wizards\ArxAppWiz\Templates\1033为新建项目的模板,拷贝这里面的文件,x64win32.vcxproj设置,要改变

Bash
1
2
3
4
5
6
7
<PropertyGroup Label="Globals">
    <ArxAppType>[!output PRJ_TYPE_APP]</ArxAppType>
    <RDS>[!output RDS_SYMB]</RDS>
    <TargetName Condition="'$(RDS)'!=''">$(RDS)$(ProjectName)</TargetName>
    <ProjectGuid>{F87873B0-2B5D-4D52-8FD5-BE1CDC50B2E3}</ProjectGuid>
    <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
  </PropertyGroup>

后面PropertyGroup 标签中的WindowsTargetPlatformVersion要考到前面来,否则找不到系统库。目标平台会被默认为win8.1,无法按win10SDK版本号导入目录。

2、C:\Program Files (x86)\Autodesk\ObjectARX 2020 Wizards\文件夹内外都有属性页设置,到底用的哪个还没整清楚。 应该是拷贝目录外面的,

Bash
1
2
3
CopyPropsFile ( 'Autodesk.arx-2020.props', strProjectName) ;
CopyPropsFile ( 'Autodesk.arx-2020-net.props', strProjectName) ;
CopyPropsFile ( 'crx.props', strProjectName) ;

因为crx.props在外面。

3、接2中,无论拷贝哪个属性页,目前是在项目中的vcxproj文件中,

合并上面这两个文件,之后 Autodesk.arx-2020.props文件会在合并下面6个文件 前三个

Bash
1
2
3
<Import Condition="'$(ArxAppType)'=='dbx' or '$(ArxAppType)'=='dbxnet'" Project="$(ArxSdkDir)\inc\dbx.props" />
<Import Condition="'$(ArxAppType)'=='crx' or '$(ArxAppType)'=='crxnet'" Project="$(ArxSdkDir)\inc\crx.props" />
<Import Condition="'$(ArxAppType)'=='arx' or '$(ArxAppType)'=='arxnet'" Project="$(ArxSdkDir)\inc\arx.props" />

后3个文件主要是不同项目类型的引用库的文件。

Bash
1
2
3
<Import Condition="'$(Configuration)'=='Release'" Project="$(ArxSdkDir)\inc\rxsdk_Releasecfg.props" />
<Import Condition="'$(Configuration)'=='Debug'" Project="$(ArxSdkDir)\inc\rxsdk_Debugcfg.props" />
<Import Condition="'$(ArxAppType)'=='dbxnet' or '$(ArxAppType)'=='crxnet' or '$(ArxAppType)'=='arxnet'" Project="Autodesk.arx-2020-net.props" />

这三个是调试和最终发布的一些设置,其中出bug那个地方是在true改为false。 原文链接:https://blog.csdn.net/ImummyI/article/details/103980572

clr操作cpp最简单的方式

创建cpp代码

导出需要使用的代码

在cpp中先封装一次

CFuncHelper.h

C++
#pragma once
#ifdef ZWWHFORJTSERIALNOLABEL_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP
#endif
class DLLIMPEXP CFuncHelper
{
public:
    CFuncHelper();
    ~CFuncHelper();

    bool generateNo();
};

CFuncHelper.cpp

C++
#include "stdafx.h"
#include "CFuncHelper.h"
#include "CGenerateSerialNo.h"
#include "CGlobalHelper.h"
#include "CSelCSerialNo.h"
#include "CCoordinateInfoJig.h"


CFuncHelper::CFuncHelper()
{
}


CFuncHelper::~CFuncHelper()
{
}

bool CFuncHelper::generateNo()
{
    CGenerateSerialNo gen;
    if (!gen.doIt())
    {
        return false;
    }
    gen.drawSerialNoByInfo();
    AcGePoint3d basePt = gen.basePt();

    AcDbExtents exts = CGlobalHelper::getFrameExtsbyPoint(basePt);
    CGlobalHelper::ZOOMWINDOW(exts.minPoint(), exts.maxPoint());
    AcGePoint3d minPt, maxPt;
    minPt = CGlobalHelper::TransformPoint(exts.minPoint(), 0, 1);
    maxPt = CGlobalHelper::TransformPoint(exts.maxPoint(), 0, 1);
    exts.set(minPt, maxPt);
    CSelCSerialNo sel;
    sel.setExtents(exts);
    resbuf *rb = acutBuildList(
        -4, _T("<and"), RTDXF0, CBaseConstant::SERIAL_NO, -4, _T("and>"),
        RTNONE); // Simplification for apparent list
    if (!sel.selEnt(rb))
    {
        return false;
    }

    vector<CDimInfo> vecInfo = sel.data();
    if (vecInfo.size() < 1)
    {
        return false;
    }

    CCoordinateInfoJig *jig = new CCoordinateInfoJig();
    jig->startJig(vecInfo);
    return true;
}

创建Clr代码

image-20241202083011439

clrDemo.h

C++
#pragma once
#include "CFuncHelper.h"
using namespace System;

namespace clrDemo {
    public ref class ManageExport
    {
    public:
        ManageExport() : nativeCSelCircleInfo(new CFuncHelper()) {}
        ~ManageExport() { this->!ManageExport(); }
        !ManageExport() { delete nativeCSelCircleInfo; }
        bool Test()
        {
            return nativeCSelCircleInfo->generateNo();
        }

    private:
        CFuncHelper* nativeCSelCircleInfo; // 原生CSelCircleInfo类的实例指针

    };
}

Csharp中引入clr生成的dll

使用 XAML 格式化工具:XAML Styler

1. XAML 的问题#

刚入门 WPF/UWP 之类的 XAML 平台,首先会接触到 XAML 这一新事物。初学 XAML 时对它的印象可以归纳为一个词:一坨

随着我在 XAML 平台上工作的时间越来越长,我对 XAML 的了解就越来越深入,从语法、约束、扩展性等方方面面,我明白到 XAML 是桌面开发平台的一个最佳解决方案。这时候我已经对 XAML 有了改观,我重新用一个词归纳了我对它的印象:一大坨

没错,这时候我已经是一个成熟的 XAML 工人了,经过我熟练的双手产生了一坨又一坨 XAML,它们成长相遇结合繁衍,变成了一大坨又一大坨 XAML。

明明 XAML 这么一大坨已经够艰难了,偏偏对于它的格式化微软爸爸也没给个好的方案。对我来说,XAML 格式化主要的难题是下面几个:

  • 如果所有属性都写在同一行,它太宽了很难看到后面的属性
  • 如果每个属性单独一行,它又太长了很难看清楚它的结构
  • 属性之间没有排序,重要属性的属性找起来很困难
  • 团队没有统一的标准,不小心格式化一下代码的话全部都会变,CodeReview 烦死个人

如果不想得过且过忍受上述这些问题的话,可以试试用 XAML Styler 这个工具,它正好解决了我最想解决的问题。

2. 安装 XAML Styler#

XAML Styler 是一个 VisualStudio插件(也可用于其它 IDE),这是它在 Github 上的地址:

https://github.com/Xavalon/XamlStyler

在这里你可以找到具体的文档,而这篇文章我只介绍我关心的其中几个属性,不一定满足到你。

在 VisualStudio 的管理扩展窗口中,输入 XamlStyle 搜索,点击“下载”然后关闭 VisualStudio 即可完成安装。

img

安装完成后重启 Visual Studio,可以在“选项”窗口中看到它的配置:

img

之后,每次在 XAML 编辑器中执行保存都会自动进行格式化操作。你也可以在 XAML 编辑器的右键菜单选择 Format XAML 或使用快捷键进行格式化。

img

3. 格式化#

XAML 的格式主要有两种方式:所有属性放一行和每个属性单独一行。

如果选择所有属性放一行的时候,XAML 结构清晰,结构严谨,段落分明,而且文件也很短。

可是万一很多属性问题就出来了,一行 XAML 会变得很长。而且看看下面两个 ContentPresenter,同样都有 Margin 属性、HorizontalAlignment 属性,VerticalAlignment 属性,RecognizesAccessKey 属性,SnapsToDevicePixels 顺序ing,但你能看到第二个 ContentPresenter 后面偷偷塞了个 Margin 吗:

XML
Copy<ContentPresenter Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<ContentPresenter Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="40"/>

如果在 VisualStudio 中“文本编辑器->XAML->格式化->间距->特性间距”这个选项中选择了“将各个属性分别放置”:

img

格式化文档后上面的 XAML 就会变成这样:

XML
Copy<ContentPresenter Margin="{TemplateBinding Padding}"
                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                  RecognizesAccessKey="True"
                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<ContentPresenter Margin="{TemplateBinding Padding}"
                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                  RecognizesAccessKey="True"
                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                  Margin="40" />

每个属性单独一行不仅不会看漏属性,而且编辑器本身也不会有横向和纵向两种方向的移动,只有从上到下的移动,这就舒服多了。

可是大部分情况下每个属性分行放置会破坏原本清晰的 XAML 层次结构,例如下面这种本来好好的 XAML:

XML
1
2
3
4
5
6
Copy<Setter Property="FontWeight" Value="Normal" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="Height" Value="50" />
<Setter Property="Width" Value="50" />
<Setter Property="Maximum" Value="1" />

变成这样:

XML
Copy<Setter Property="FontWeight"
        Value="Normal" />
<Setter Property="UseSystemFocusVisuals"
        Value="True" />
<Setter Property="FocusVisualMargin"
        Value="-3" />
<Setter Property="Height"
        Value="50" />
<Setter Property="Width"
        Value="50" />
<Setter Property="Maximum"
        Value="1" />

这种风格优雅得像诗歌 我偶尔称为豆瓣风 一行变两行 两行变四行 本来 一页看得完 的代码 变成 两页才看得完 也是够 麻烦的。

XAML Styler 很好地解决了这个问题,它通过 “Attribute tolerance” 属性控制每一行的容许的最多的属性数量,如果一个元素的属性数量少于设定值,那就放在一行,如果超过就所有属性单独一行。通常我将这个属性设置为 2,再配合 “Keep first attribute on same line = true” 的设置,可以做到下面这种格式化效果:

XML
Copy<SolidColorBrush x:Key="NormalTextColor" Color="#2E2F33" />
<SolidColorBrush x:Key="PrimaryColor" Color="#FFED5B8C" />
<SolidColorBrush x:Key="LineColor" Color="#E1E1E1" />
<SolidColorBrush x:Key="TransparentBackground" Color="Transparent" />

<ControlTemplate x:Key="CompletedTemplate" TargetType="ContentControl">
    <Grid x:Name="CompletedElement" Margin="-2">
        <control:DropShadowPanel HorizontalContentAlignment="Stretch"
                                 VerticalContentAlignment="Stretch"
                                 BlurRadius="8"
                                 OffsetX="0"
                                 OffsetY="0"
                                 Color="#FFED5B8C">
            <Ellipse x:Name="CompletedRectangle" Fill="{StaticResource PrimaryColor}" />
        </control:DropShadowPanel>
    </Grid>
</ControlTemplate>

这样就可以兼顾两种格式化的优点。

4. 排序#

如果元素有多个属性,要找到它的主要属性(通常是 Name 和 Grid.Row)需要颇费一番功夫。XAML Styler 根据一个可设定的规则自动将元素的各个属性排序,这个规则如下:

JSON
Copy"AttributeOrderingRuleGroups": [
    "x:Class",
    "xmlns, xmlns:x",
    "xmlns:*",
    "x:Key, Key, x:Name, Name, x:Uid, Uid, Title",
    "Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom",
    "Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight",
    "Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex",
    "*:*, *",
    "PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint",
    "mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText",
    "Storyboard.*, From, To, Duration"
],

排序结果大致如下:

XML
1
2
3
4
5
6
7
8
9
Copy<Button x:Name="Show"
        Grid.Row="1"
        Padding="40,20"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Background="#00aef1"
        Content="Show"
        Foreground="White"
        Style="{StaticResource BubbleButtonStyle}" />

另外,我不喜欢它自动将 VisualStateManager 排序到后面,虽然这个排序合理,但不符合我的习惯,所以要将 “Record visual state manager” 设置为 None。

5. 统一标准#

最后,就算自己做好了格式化,团队中的其它成员使用了不同的格式化标准也会引起很多问题。针对这个问题 Xaml Styler 也提供了解决方案。

在项目的根目录创建一个名为“Settings.XamlStyler”的文件,内容参考这个网址:https://github.com/Xavalon/XamlStyler/wiki/External-Configurations 中的 Default Configuration。有了这个配置文件,XAML Styler 就会根据它而不是全局配置进行格式化,作为项目的统一格式化标准。

Csharp使用Newtonsoft.Json生成JSON字符串

下载newtonsoftjson

在“解决方案资源管理器”中,右键单击项目,然后选择“管理NuGet程序包”。在NuGet包管理器中,搜索“Newtonsoft.Json”。找到Newtonsoft.Json包,点击安装按钮

C#
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

对于复杂json串

  • 对于简单的json的可以直接解析, 复杂的json, 建议用先创建json对应的类,然后再用JsonConvert.DeserializeObject转为类来解析, 当json比较复杂时, 创建类也比较浪费时间, VS2022为C#提供了json转C#类的工具,先复制需要转为类的json字符串,然后将光标定位到cs文件的空白处,最后点击编辑–选择性粘贴–将Json粘贴为类,如下图:

  • 除了VS自带的工具,也有一些网站提供了类似的功能,例如Json2CSharp

### demo

JSON
1
2
3
4
5
6
7
8
9
{
    "items": [{
        "id": "csrf",
        "attributes": {
            "nonce key": "CSRF NONCE",
            "nonce": "i8Ah1n1DHs71704s2oZnSxmiz4/R3T5mbFrkxErz4m8RUDf3kyX+ror25kZ09Env0tGeVBe+iES8/Y04XRfAKvghp1/+ZIx09oVE7GiE"
        }
    }]
}

class

C#
//如果好用,请收藏地址,帮忙分享。
public class Attributes
{
    /// <summary>
    /// 
    /// </summary>
    public string nonce_key { get; set; }
/// <summary>
/// 
/// </summary>
    public string nonce { get; set; }
}

public class ItemsItem
{
    /// <summary>
    /// 
    /// </summary>
    public string id { get; set; }
    /// <summary>
    /// 
    /// </summary>
    public Attributes attributes { get; set; }
}
//Root可以改成自己喜欢的类名
public class CScrfRoot
{
    /// <summary>
    /// 
    /// </summary>
    public List<ItemsItem> items { get; set; }
}

读取json文件

C#
public static string GetDllDirectory()
{
    string codeBase = Assembly.GetExecutingAssembly().CodeBase;
    UriBuilder uri = new UriBuilder(codeBase);
    string path = Uri.UnescapeDataString(uri.Path);
    return System.IO.Path.GetDirectoryName(path);
}
public MyDropMenu()
{
    System.Uri resourceLocater = new System.Uri("/MyDropMenu;component/ComUseicons.xaml", System.UriKind.Relative);
    ResourceDictionary rd = (ResourceDictionary)Application.LoadComponent(resourceLocater);
    Application.Current.Resources.MergedDictionaries.Add(rd);

    InitializeComponent();
    try
    {
        string jsonFile = GetDllDirectory() + "\\Menu.json";
        // 确保文件存在
        if (!File.Exists(jsonFile))
            throw new FileNotFoundException("The JSON file was not found." + jsonFile);

        // 读取文件内容并反序列化为指定的类型 T
        var reader = new StreamReader(jsonFile);
        var json = reader.ReadToEnd();
        var person = JsonConvert.DeserializeObject<Root>(json);
        Items = person.ItemMenu;
        //遍历items,将icon添加数据
        // 遍历并修改Icon
        foreach (var itemMenu in Items)
        {
            itemMenu.Icon = GetDllDirectory() + "\\config\\Images\\PNG\\" + itemMenu.Icon;
            foreach (var subItem in itemMenu.SubItems)
            {
                subItem.Icon = GetDllDirectory() + "\\config\\Images\\PNG\\" + subItem.Icon;
            }
        }
        LeftMenu.ItemsSource = Items;
    }
    catch (Exception)
    {
        throw;
    }
}

获取token(nonce)值

C#
 public static string getTokenFromJson(string strJson)
        {
            string strRet = "";
            //strJson = "{\"items\":[{\"id\":\"csrf\",\"attributes\":{\"nonce key\":\"CSRF NONCE\",\"nonce\":\"i8Ah1n1DHs71704s2oZnSxmiz4/R3T5mbFrkxErz4m8RUDf3kyX+ror25kZ09Env0tGeVBe+iES8/Y04XRfAKvghp1/+ZIx09oVE7GiE\"}}]}";
            var person = JsonConvert.DeserializeObject<CScrfRoot>(strJson);
            List<ItemsItem> listItems = person.items;
            if(listItems.Count >= 1)
            {
                ItemsItem itemsItem = listItems[0];
                Attributes attr = itemsItem.attributes;
                strRet = attr.nonce;
            }

            return strRet;
        }

LINQ to JSON主要使用到JObject, JArray, JProperty和JValue这四个对象

  • JObject用来生成一个JSON对象,简单来说就是生成”{}”,
  • JArray用来生成一个JSON数组,也就是”[]”,
  • JProperty用来生成一个JSON数据,格式为key/value的值,
  • 而JValue则直接生成一个JSON值
C#
//将json转换为JObject
JObject jObj = new JObject();
jObj.Add("process0id", AdditionClass.GetDeFaultProjectNo());


PdfRow pdfRow1 = new PdfRow();
pdfRow1.status = "success";
pdfRow1.pdfname = "D:\\ZWPDF\\PDF\\JG-72-BL-LB1.pdf";


PdfRow pdfRow2 = new PdfRow();
pdfRow2.status = "error";
pdfRow2.pdfname = "D:\\ZWPDF\\PDF\\JG-72-BL-LB2.pdf";


List<PdfRow> videogames = new List<PdfRow>();
videogames.Add(pdfRow1);
videogames.Add(pdfRow2);

JArray jArray = (JArray)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(videogames));
jObj.Add("message", "转换完成");
jObj.Add("rowPdf", jArray);
Console.WriteLine(jObj.ToString());
JSON
{
  "process0id": "05369",
  "message": "转换完成",
  "rowPdf": [
    {
      "status": "error",
      "pdfname": "D:\\TEST\\ZP-35-DYT-35N3--竖向图框.dwg"
    },
    {
      "status": "error",
      "pdfname": "D:\\TEST\\图框外有多余线条.dwg"
    },
    {
      "status": "error",
      "pdfname": "D:\\TEST\\弧线标注的圆心在图框外1.dwg"
    }
  ]
}

ObjectARX加载菜单

使用ObjectARX有几种加载菜单的方式

使用aced函数加载

LocadMenu.h

C++
#pragma once
#include "stdafx.h"

class Menu
{
public:
    Menu();
    ~Menu();
    static bool    IsFileExists(LPCTSTR szPath);
    static CString GetFolder(LPCTSTR szPath);
    static CString GetCurDir();
    static CString FindConPath(CString FileName);
    static void    CLoadMenu(CString sName, CString szPath);
    static void    CUnLoadMenu(CString sName, CString szPath);
    static void    RemoveMenuCache(CString sName, CString szPath);
};

LocadMenu.cpp

C++
#include "stdafx.h"
#include "LoadMenu.h"

Menu::Menu() {}

Menu::~Menu() {}

bool Menu::IsFileExists(LPCTSTR szPath)
{
    CFileStatus st;
    return CFile::GetStatus(szPath, st);
}

CString Menu::GetFolder(LPCTSTR szPath)
{
    CString strPath(szPath);
    strPath.TrimRight(_T("\\/"));

    int pos = strPath.ReverseFind(_T('\\'));
    int pos2 = strPath.ReverseFind(_T('/'));
    pos = max(pos, pos2);

    if (pos < 0)
    {
        return _T("");
    }

    return strPath.Left(pos + 1);
}

CString Menu::GetCurDir()
{
    TCHAR szPath[_MAX_PATH];
    ::GetModuleFileName(_hdllInstance, szPath, _MAX_PATH);

    TCHAR szDrive[_MAX_DRIVE];
    TCHAR szDir[_MAX_DIR];
    TCHAR szFname[_MAX_FNAME];
    TCHAR szExt[_MAX_EXT];
    _tsplitpath_s(szPath, szDrive, szDir, szFname, szExt);

    return CString(szDrive) + szDir;
}

CString Menu::FindConPath(CString FileName)
{
    CString sDir = GetCurDir();

    CString sPath = sDir + FileName;
    while (!IsFileExists(sPath))
    {
        sDir = GetFolder(sDir);
        if (sDir.IsEmpty())
        {
            return _T("");
        }

        sPath = sDir + FileName;
    }
    return sPath;
}

void Menu::CLoadMenu(CString sName, CString szPath)
{
    //加载菜单
    if (!acedIsMenuGroupLoaded(sName))
    {
        CString str;

        str = FindConPath(szPath + _T(".cuix"));
        if (str.IsEmpty())
        {
            str = FindConPath(szPath + _T(".mnu"));
        }

        acedLoadPartialMenu(str);
    }
}

void Menu::CUnLoadMenu(CString sName, CString szPath)
{
    //卸载菜单
    if (acedIsMenuGroupLoaded(sName))
    {
        CString str;

        str = FindConPath(szPath + _T(".cuix"));
        if (str.IsEmpty())
        {
            str = FindConPath(szPath + _T(".mnu"));
        }

        acedUnloadPartialMenu(str);
    }
}

void Menu::RemoveMenuCache(CString sName, CString szPath)
{
    if (acedIsMenuGroupLoaded(sName))
    {
        CString str;

        str = FindConPath(szPath + _T(".cuix"));
        if (str.IsEmpty())
        {
            return;
        }
        int   len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
        char* ptxtTemp = new char[len + 1];
        WideCharToMultiByte(CP_ACP, 0, str, -1, ptxtTemp, len, NULL, NULL);
        remove(ptxtTemp);
        delete[] ptxtTemp;
    }
}

使用方式

C++
virtual AcRx::AppRetCode On_kInitAppMsg(void *pkt)
{
    // You *must* call On_kInitAppMsg here
    AcRx::AppRetCode retCode = AcRxArxApp::On_kInitAppMsg(pkt);
    Menu::CUnLoadMenu(_T("SHRoad"), _T("Library\\Menu\\SHGLJTDZKFXM"));
    Menu::CLoadMenu(_T("SHRoad"), _T("Library\\Menu\\SHGLJTDZKFXM"));

    return (retCode);
}

virtual AcRx::AppRetCode On_kUnloadAppMsg(void *pkt)
{
    AcRx::AppRetCode retCode = AcRxArxApp::On_kUnloadAppMsg(pkt);
    Menu::CUnLoadMenu(_T("SHGLJTDZKFXM"), _T("Library\\Menu\\SHGLJTDZKFXM"));
    return (retCode);
}

第二种方式使用com加载

C++
#include "CAcadApplication.h"
#include "CAcadDocument.h"
#include "CAcadMenuBar.h"
#include "CAcadMenuGroup.h"
#include "CAcadMenuGroups.h"
#include "CAcadPopupMenu.h"
#include "CAcadPopupMenus.h"
//加载cui文件
bool LoadPartialMenu(
    const TCHAR* filePath //局部菜单文件名
    ,
    const TCHAR* menuGroupName)

{
    //如果有的话先卸载

    long menuGroupNum; //菜单组数目

    VARIANT index;

    VariantInit(&index);

    index.vt = VT_I4;

    CString strGroupName(menuGroupName);

    CAcadApplication acadApp(acedGetAcadWinApp()->GetIDispatch(TRUE));

    CAcadMenuGroups menuGroups(acadApp.get_MenuGroups());

    CAcadMenuGroup menuGroup;

    menuGroupNum = menuGroups.get_Count();

    for (long i = 0; i < menuGroupNum; i++)
    {
        index.lVal = i;

        menuGroup = menuGroups.Item(index);
        CString strName = menuGroup.get_Name();
        if (strName.CompareNoCase(strGroupName) == 0)
        {
            menuGroup.Unload();

            break;
            //return false;
        }
    }

    //加载菜单

    VARIANT BaseMenu; //是否加载为基础菜单

    VariantInit(&BaseMenu);

    BaseMenu.vt = VT_BOOL;

    BaseMenu.boolVal = FALSE;

    menuGroups.Load(CString(filePath), BaseMenu);
    // 把菜单在菜单条上显示出来

    //CAcadMenuBar menuBar(acadApp.get_MenuBar());  //当前菜单条

    //CAcadPopupMenus popupMenus(menuGroup.get_Menus()); //要加入的菜单条

    //CAcadPopupMenu popupMenu;

    //long curPopupMenuNum = menuBar.get_Count();   //当前菜单条上菜单的数目

    //long n = popupMenus.get_Count();
    ////bool beExit = false;
    //for (long i = 0; i < n; i++) {

    //  index.lVal = i;

    //  popupMenu = popupMenus.Item(index);

    //  index.lVal = i + curPopupMenuNum;

    //  popupMenu.InsertInMenuBar(index);
    //  CString strName = popupMenu.get_Name();
    //  if (strName.CompareNoCase(_T("TaiyuanBoilerElevation")) == 0)
    //  {
    //      //beExit = TRUE;
    //      break;
    //  }
    //}
    return true;
}
static void initLoadMenu()
{
    CString strPath = CUtility::GetAppPath();
    strPath = strPath.Left(strPath.ReverseFind('\\'));
    if (strPath != _T(""))
    {
        strPath += _T("\\xhhk.cuix");
    }
    LoadPartialMenu(strPath, _T("xhhk"));
}

On_kInitAppMsg

initLoadMenu();

推荐使用第一种方式,不需要加载那么多的头文件

image-20250604151542564

CAD.net调用内置对话框

C#
[CommandMethod("CmdTest_ShowDialog")]
public void CmdTest_ShowDialog()
{
    var dm = ZwSoft.ZwCAD.ApplicationServices.Application.DocumentManager;
    var doc = dm.MdiActiveDocument;
    var ed = doc.Editor;
    var db = doc.Database;
    ed.WriteMessage("\n测试cad颜色面板+线型面板");

    var cd = new ZwSoft.ZwCAD.Windows.ColorDialog();
    var dr = cd.ShowDialog();
    if (dr == System.Windows.Forms.DialogResult.OK)
        ed.WriteMessage("\ncad颜色选择了: " + cd.Color.ToString());

    var ld = new ZwSoft.ZwCAD.Windows.LinetypeDialog();
    dr = ld.ShowDialog();
    if (dr == System.Windows.Forms.DialogResult.OK)
        ed.WriteMessage("\ncad线型选择了: " + ld.Linetype.ToString());

    var dlg = new System.Windows.Forms.ColorDialog();
    dr = dlg.ShowDialog();
    if (dr == System.Windows.Forms.DialogResult.OK)
        ed.WriteMessage("\n系统颜色选择了: " + dlg.Color.ToString());
}

ObjectARX ucs和wcs

ucs和wcs转换

No Description
0 World (WCS)
1 User (current UCS)
2 Display:DCS of current viewport when used with code 0 or 1DCS of current model space viewport when used with code 3
3 Paper space DCS (PSDCS; used only with code 2)
C++
//0表示wcs 1表示ucs 2 DCS, 3 PSDCS
AcGePoint3d CGlobalHelper::TransformPoint(const AcGePoint3d &point, int nFromType, int nToType)
{
    AcGePoint3d pt;
    //从ucs转到wcs
    struct resbuf rbFrom, rbTo;
    rbFrom.restype = RTSHORT;
    rbFrom.resval.rint = nFromType; // from wcs
    rbTo.restype = RTSHORT;
    rbTo.resval.rint = nToType;     // from ucs
    acedTrans(asDblArray(point), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(pt));
    return pt;
}

常见的wcs

AcDbEntity中存储和返回的都是wcs,比如

C++
//直线 点都必须在WCS坐标中
AcDbLine(const AcGePoint3d& start, const AcGePoint3d& end);

//圆 中心和法向量必须是WCS坐标
AcDbCircle(const AcGePoint3d& cntr, const AcGeVector3d& nrm, double radius);

//圆弧 中心必须位于WCS坐标中
AcDbArc(const AcGePoint3d& center, const AcGeVector3d& normal, double radius, double startAngle, double endAngle);

//多段线 注意!!!点必须位于ECS坐标中
AcDbPolyline::addVertexAt(unsigned int index,  const AcGePoint2d& pt, double bulge = 0., double startWidth = -1.,  double endWidth = -1., Adesk::Int32 vertexIdentifier = 0);

//椭圆 中心必须位于WCS坐标中
AcDbEllipse( const AcGePoint3d& center, const AcGeVector3d& unitNormal, const AcGeVector3d& majorAxis, double radiusRatio, double startAngle = 0.0, double endAngle = 2*PI);

//引线 点必须位于WCS坐标中
AcDbLeader::appendVertex(const AcGePoint3d&);
view相关的点也是wcs
C++
void CGlobalHelper::ZOOMWINDOW(AcGePoint3d minPt, AcGePoint3d maxPt)
{
    // get the extents of the drawing
    AcDbViewTableRecord view;

    AcGePoint2d max_2d(maxPt[X], maxPt[Y]);
    AcGePoint2d min_2d(minPt[X], minPt[Y]);
    // now set the view centre point
    view.setCenterPoint(min_2d + (max_2d - min_2d) / 2.0);
    // now height and width of view
    view.setHeight(max_2d[Y] - min_2d[Y]);
    view.setWidth(max_2d[X] - min_2d[X]);
    // set the view
    acedSetCurrentView(&view, NULL);
    // updates the extents
    acdbHostApplicationServices()->workingDatabase()->updateExt(TRUE);
}

jig中交互的点都是wcs

C++
1
2
3
4
5
6
7
8
9
//WCS 
//JIG获取点,即传入的basePnt、返回的resPnt 均为WCS中的点
AcEdJig::acquirePoint(AcGePoint3d& resPnt , const AcGePoint3d& basePnt);

//JIG获取角度,即传入的basePnt为WCS中的点
AcEdJig::acquireAngle( double & ang, const AcGePoint3d& basePnt);

//JIG获取距离,即传入的basePnt为WCS中的点
AcEdJig::acquireDist( double & dist, const AcGePoint3d& basePnt);

常见的UCS

常用API接口返回参数

C++
//UCS
//获取点,即传入的pt、返回的result均为UCS中的点
int acedGetPoint(const ads_point pt, const ACHAR * prompt, ads_point result);

//获取实体,返回的ptres均为UCS中的点
int acedEntSel(const ACHAR * str, ads_name entres, ads_point ptres);

//获取角度,传入的pt为UCS中的点
int acedGetAngle(const ads_point pt, const ACHAR * prompt, ads_real * result);

//获取距离,传入的pt为UCS中的点
int acedGetDist(const ads_point pt, const ACHAR * prompt, ads_real * result);

//动态拖拽移动 ,即传入的pmt、回调函数中的pt、返回的p均为UCS中的点
int acedDragGen(const ads_name ss, const ACHAR * pmt, int cursor, int (*scnf) (ads_point pt, ads_matrix mt), ads_point p);

AcDbMText::rotation 

//传入的点是ucs,这个比较重要

C++
int nRet = acedSSGet(_T("C"), asDblArray(minPt), asDblArray(maxPt), rb, ssname);

AcDbMText::rotation setRotation 这里的角度是相对于UCS的x轴,所以需要处理,特别注意

DCS

目前已知的dcs就是plot中的点坐标

C++
struct resbuf rbFrom, rbTo;
rbFrom.restype = RTSHORT;
rbFrom.resval.rint = 0; // from WCS
rbTo.restype = RTSHORT;
if (m_bISModelType)
{
    rbTo.resval.rint = 2; // to dcs
}
else
{
    rbTo.resval.rint = 2; // to pdcs
    acedTrans(asDblArray(maxPt), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(maxPt));
    acedTrans(asDblArray(minPt), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(minPt));
    rbFrom.resval.rint = 2; // from UCS
    rbTo.resval.rint = 3;
}
int nCvport = 2;
utils.GetVar(_T("CVPORT"), &nCvport);
if (nCvport >= 2)
{
    acedTrans(asDblArray(maxPt), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(maxPt));
    acedTrans(asDblArray(minPt), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(minPt));
}

es = pPSV->setPlotWindowArea(pPlotSettings, minPt.x, minPt.y, maxPt.x, maxPt.y);

未完待补充

其它的wcs和ucs的待补充

GetClosestPointTo() on a BlockReference returns which coordinate system in AutoCAD using ObjectARX

Issue

Why are the coordinates of a closest point (using GetClosestPointTo()) obtained from a block reference relative to neither WCS nor UCS?

Solution
HTML
1
2
3
4
5
6
7
8
The points that you receive are relative to the coordinate system of the owning
AcDbBlockTableRecord. If you want to convert them to coordinate system of the
AcDbBlockTableRecord in which you have the AcDbBlockReference, you must
transform them by the AcDbBlockReference::blockTransform().

You probably first want to transform the "specified point" by the inverse of
AcDbBlockReference::blockTransform(), do the closest point calculation, then
convert the resulting point back to the original space.

CXTPPropertyGrid控件使用

xtp配置

C++
1
2
3
4
5
6
7
8
9
inc 默认安装目录
C:\Program Files (x86)\Codejock Software\MFC\Xtreme ToolkitPro v20.3.0\Source
lib 默认安装目录
C:\Program Files (x86)\Codejock Software\MFC\Xtreme ToolkitPro v20.3.0\Lib\vc150x64
在stdafx.h中添加

#include <XTToolkitPro.h>

//#include "arxHeaders.h"

image-20240401160031481

在对话框中拉一个picture control控件

client Edge设置为true visible 设置为false

DDX_Control(pDX, IDC_PLACEHOLDER, m_wndPlaceHolder);

加载控件

C++
CString strVal;
//map<pair<int, int>, CString> mp = GetXrecord();
vector<CString> vec{ _T("类型"), _T("长"), _T("宽"), _T("高"), _T("其它"), _T("其它1"), _T("其它2"), _T("其它3"), _T("其它4"), _T("其它5"), _T("其它6"), _T("其它7"), _T("其它8") };
CRect rc;
m_wndPlaceHolder.GetWindowRect(&rc);
ScreenToClient(&rc);

if (m_wndPropertyGrid.Create(rc, this, IDC_PROPERTY_GRID))
{
    m_wndPropertyGrid.SetVariableItemsHeight(TRUE);

    LOGFONT lf;
    GetFont()->GetLogFont(&lf);

    CXTPPropertyGridItem* pStandard = m_wndPropertyGrid.AddCategory(_T("属性数据"));
    pStandard->AddChildItem(new CXTPPropertyGridItem(_T("String item"), _T("测试")));
    pStandard->AddChildItem(new CXTPPropertyGridItemNumber(_T("Integer item")));
    pStandard->AddChildItem(new CXTPPropertyGridItemDouble(_T("Double item")));

    //下拉框
    CXTPPropertyGridItem* pItem = pStandard->AddChildItem(
        new CXTPPropertyGridItemEnum(_T("Enum item"), 2));
    pItem->GetConstraints()->AddConstraint(_T("一型"), 1);
    pItem->GetConstraints()->AddConstraint(_T("L型"), 2);
    pItem->GetConstraints()->AddConstraint(_T("R型"), 3);
    //////////////////////////////////////////////////////////////////////////


    //下拉形式
    //CXTPPropertyGridItem* pItemLanguage = pStandard->AddChildItem(new CXTPPropertyGridItem(strLabel, strDeFault));
    //CXTPPropertyGridItemConstraints* pList = pItemLanguage->GetConstraints();
    //for (auto it : tmpVec)
    //{
    //    pList->AddConstraint(it);
    //}
    //pItemLanguage->SetFlags(xtpGridItemHasComboButton | xtpGridItemHasEdit);

    //pStandard->Expand();
}
  • 隐藏Category
C++
m_wndPropertyGrid.SetPropertySort(xtpGridSortNoSort);//将Category隐藏
  • 清空所有数据
C++
m_wndPropertyGrid.ResetContent();
  • 读取数据
C++
//读取数据
CString strItem;        
CXTPPropertyGridItem* pItems = m_wndPropertyGrid.GetItem(0);
if (pItems != NULL)
{
    int nCount = pItems->GetChilds()->GetCount();
    for (int i = 0; i < nCount; i++)
    {
        CXTPPropertyGridItem* pItem = pItems->GetChilds()->GetAt(i);
        strItem = pItem->GetCaption();
        strVal = pItem->GetValue();
        acutPrintf(_T("\nitem:%s,val:%s"), strItem, strVal);
    }
}
  • 消息响应
C++
ON_MESSAGE(XTPWM_PROPERTYGRID_NOTIFY, OnGridNotify)
C++
LRESULT CDlgShowTk::OnGridNotify(WPARAM wParam, LPARAM lParam)
{
    if (wParam == XTP_PGN_ITEMVALUE_CHANGED)
    {
        CXTPPropertyGridItem* pItem = (CXTPPropertyGridItem*)lParam;
        TRACE(_T("Value Changed. Caption = %s, ID = %i, Value = %s\n"), pItem->GetCaption(),
            pItem->GetID(), pItem->GetValue());

        if (DYNAMIC_DOWNCAST(CXTPPropertyGridItemEnum, pItem))
        {
            if (pItem->GetMetrics(TRUE, FALSE))
            {
                pItem->GetMetrics(TRUE, FALSE)->m_nImage =
                    ((CXTPPropertyGridItemEnum*)pItem)->GetEnum();
            }
        }

        if (pItem->GetID() == 501) // Dynamic Options
        {
            CXTPPropertyGridItems* pSiblingItems = pItem->GetParentItem()->GetChilds();

            for (int i = 0; i < pSiblingItems->GetCount(); i++)
            {
                if (pSiblingItems->GetAt(i) != pItem)
                {
                    pSiblingItems->GetAt(i)->SetHidden(
                        !((CXTPPropertyGridItemBool*)pItem)->GetBool());
                }
            }
        }
    }
    if (wParam == XTP_PGN_EDIT_CHANGED)
    {
        CXTPPropertyGridInplaceEdit* pEdit = DYNAMIC_DOWNCAST(CXTPPropertyGridInplaceEdit,
            (CWnd*)lParam);
        if (pEdit && pEdit->GetItem())
        {
            // Custom Validation
            if (pEdit->GetItem()->GetID() == ID_ITEM_VERSION_LANGUAGE)
            {
                CString str;
                pEdit->CEdit::GetWindowText(str);

                if (str.GetLength() > 30)
                {
                    MessageBeep((UINT)-1);
                    pEdit->SetSel(0, -1);
                    pEdit->ReplaceSel(str.Left(30));
                }
            }
            // Custom Validation
            if (pEdit->GetItem()->GetCaption() == _T("ItemsInMRUList"))
            {
                CString str;
                pEdit->CEdit::GetWindowText(str);

                int i = _ttoi(str);
                if (i > 20)
                {
                    MessageBeep((UINT)-1);
                    pEdit->SetSel(0, -1);
                    pEdit->ReplaceSel(_T("20"));
                }
            }
        }
    }
    return 0;
}

联动修改

ID_ITEM_CORNER ID_ITEM_CONFIG 在resource.h中定义

C++
LRESULT CDlgCategory::OnGridNotify(WPARAM wParam, LPARAM lParam)
{
    if (wParam == XTP_PGN_ITEMVALUE_CHANGED)
    {
        CXTPPropertyGridItem* pItem = (CXTPPropertyGridItem*)lParam;
        if (pItem->GetID() == ID_ITEM_CONFIG) // Dynamic Options
        {
            CString strConfig = pItem->GetValue();
            CXTPPropertyGridItems* pSiblingItems = pItem->GetParentItem()->GetChilds();

            for (int i = 0; i < pSiblingItems->GetCount(); i++)
            {
                if (pSiblingItems->GetAt(i)->GetID() == ID_ITEM_CORNER)
                {
                    //我们在选择了“一型”就自动关联选择下面转角的”否“;选择其余配置就是自动关联选择“是”。
                    //The Cognitive Complexity of this function is 16 which is greater than 15 authorized.
                    pSiblingItems->GetAt(i)->SetValue(
                        (strConfig.CompareNoCase(_T("一型")) == 0) ? _T("否") : _T("是"));
                }
            }
        }
    }
    return 0;
}
  • 参考C:\Program Files (x86)\Codejock Software\MFC\Xtreme ToolkitPro v20.3.0\Samples\PropertyGrid\GridSample\PropertyGrid_vc150.sln里的PropertyGrid

CAD.net 处理菜单相关功能

Text Only
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
        ToolbarButton buttonCopy = barModify.AddToolbarButton(-1, "复制", "ID_MyCopy");
        ToolbarButton buttonErase = barModify.AddToolbarButton(-1, "删除", "ID_MyErase");
        ToolbarButton buttonMove = barModify.AddToolbarButton(-1, "移动", "ID_MyMove");
        ToolbarButton buttonRotate = barModify.AddToolbarButton(-1, "旋转", "ID_MyRotate");
        //将“修改工具栏”附着到“我的工具栏”的最后
        barDraw.AttachToolbarToFlyout(-1, barModify);
        //barDraw.ToolbarVisible = ToolbarVisible.show;
        cs.LoadCui();//必须装载CUI文件,才能看到添加的菜单
    }
    [CommandMethod("AddDoubleClick")]
    public void AddDoubleClick()
    {
        //装载局部CUI文件,若不存在,则创建
        CustomizationSection cs = activeDoc.AddCui(cuiFile, menuGroupName);
        //添加表示双击多段线动作的命令宏  
        MenuMacro macro = cs.AddMacro("多段线 - 双击", "^C^C_DoubleClickPline ", "ID_PlineDoubleClick", "调用自定义命令", null);
        //创建双击动作
        DoubleClickAction action = new DoubleClickAction(cs.MenuGroup, "优化多段线", -1);
        action.ElementID = "EID_mydblclick";//双击动作的标识号
        //设置双击动作的对象为多段线
        action.DxfName = RXClass.GetClass(typeof(Polyline)).DxfName;
        //创建一个双击命令对象,指定双击对象时执行的命令宏
        DoubleClickCmd cmd = new DoubleClickCmd(action, macro);
        action.DoubleClickCmd = cmd;//指定双击动作的命令对象
        cs.LoadCui();//必须装载CUI文件,才能看到添加的菜单
    }
    [CommandMethod("DoubleClickPline")]
    public void DoubleClickPline()
    {
        Application.ShowAlertDialog("你双击了多段线!");
    }
    [CommandMethod("AddDefaultContextMenu")]
    public void AddDefaultContextMenu()
    {
        //定义一个ContextMenuExtension对象,用于表示快捷菜单
        ContextMenuExtension contextMenu = new ContextMenuExtension();
        contextMenu.Title = "我的快捷菜单";//设置快捷菜单的标题
        MenuItem mi = new MenuItem("复制");//添加名为"复制"的菜单项
        //为"复制"菜单项添加单击事件
        mi.Click += new EventHandler(mi_Click);
        contextMenu.MenuItems.Add(mi);//将"复制"菜单项添加到快捷菜单中
        mi = new MenuItem("删除");//添加名为"删除"的菜单项
        //mi.Icon = new Icon(GetProgramDir() + "\\Image\\copy.ico");
        //为"删除"菜单项添加单击事件
        mi.Click += new EventHandler(mi_Click);
        contextMenu.MenuItems.Add(mi);//将"删除"菜单项添加到快捷菜单中
        //为应用程序添加定义的快捷菜单
        Application.AddDefaultContextMenuExtension(contextMenu);
    }
    void mi_Click(object sender, EventArgs e)
    {
        MenuItem mi = sender as MenuItem;//获取发出命令的快捷菜单项
        //根据快捷菜单项的名字,分别调用对应的命令
        if (mi.Text == "复制")
            activeDoc.SendStringToExecute("_Copy ", true, false, true);
        else if (mi.Text == "删除")
            activeDoc.SendStringToExecute("_Erase ", true, false, true);
    }
    [CommandMethod("AddObjectContextMenu")]
    public void AddObjectContextMenu()
    {
        //定义一个ContextMenuExtension对象,用于表示快捷菜单
        ContextMenuExtension contextMenu = new ContextMenuExtension();
        //添加一个名为"统计个数"的菜单项,用于在AutoCAD命令行上显示所选择实体的个数
        MenuItem miCircle = new MenuItem("统计个数");
        //为"统计个数"菜单项添加单击事件,事件处理函数为调用自定义的Count命令
        miCircle.Click += delegate (object sender, EventArgs e)
        {
            activeDoc.SendStringToExecute("_Count ", true, false, false);
        };
        contextMenu.MenuItems.Add(miCircle);//将"统计个数"菜单项添加到快捷菜单中
        //获得实体所属的RXClass类型
        RXClass rx = RXClass.GetClass(typeof(Entity));
        //为实体对象添加定义的快捷菜单
        Application.AddObjectContextMenuExtension(rx, contextMenu);
    }
    [CommandMethod("Count", CommandFlags.UsePickSet)]
    public void CountEnts()
    {
        Editor ed = activeDoc.Editor;
        PromptSelectionResult result = ed.SelectImplied();
        if (result.Status == PromptStatus.OK)
            ed.WriteMessage("共选择了" + result.Value.Count + "个实体\n");
    }
}

} `CUITools.cs` ​csharp using System.Collections.Specialized; using System.IO; using ZwSoft.ZwCAD.ApplicationServices; using ZwSoft.ZwCAD.Customization; namespace CuiDemo { ///

/// 操作CUI的类 /// public static class CUITools { [DllImport("zwcad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedPostCommand")] extern static private int zcedPostCommand(string strExpr); /// /// 调用C++的acedPostCommand函数 /// /// 无意义,只是为了定义扩展函数 /// 要执行的命令字符串 public static void PostCommand(this Editor ed, string expression) { zcedPostCommand(expression); } /// /// 获取并打开主CUI文件 /// /// AutoCAD文档对象 /// 返回主CUI文件 public static CustomizationSection GetMainCustomizationSection(this Document doc) { //获得主CUI文件所在的位置 string mainCuiFile=Application.GetSystemVariable("MENUNAME") + ".cuix"; //打开主CUI文件 return new CustomizationSection(mainCuiFile); } /// /// 创建局部CUI文件 /// /// AutoCAD文档对象 /// CUI文件名 /// 菜单组的名称 /// 返回创建的CUI文件 public static CustomizationSection AddCui(this Document doc, string cuiFile, string menuGroupName) { CustomizationSection cs;//声明CUI文件对象 if (!File.Exists(cuiFile))//如果要创建的文件不存在 { cs = new CustomizationSection();//创建CUI文件对象 cs.MenuGroupName = menuGroupName;//指定菜单组名称 cs.SaveAs(cuiFile);//保存CUI文件 } //如果已经存在指定的CUI文件,则打开该文件 else cs = new CustomizationSection(cuiFile); return cs;//返回CUI文件对象 } /// /// 装载指定的局部CUI文件 /// /// CUI文件 public static void LoadCui(this CustomizationSection cs) { if (cs.IsModified) cs.Save();//如果CUI文件被修改,则保存 //保存CMDECHO及FILEDIA系统变量 object oldCmdEcho = Application.GetSystemVariable("CMDECHO"); object oldFileDia = Application.GetSystemVariable("FILEDIA"); //设置CMDECHO=0,控制不在命令行上回显提示和输入信息 Application.SetSystemVariable("CMDECHO", 0); //设置FILEDIA=0,禁止显示文件对话框,这样可以通过程序输入文件名 Application.SetSystemVariable("FILEDIA", 0); //获取当前活动文档 Document doc=Application.DocumentManager.MdiActiveDocument; //获取主CUI文件 CustomizationSection mainCs=doc.GetMainCustomizationSection(); //如果已存在局部CUI文件,则先卸载 if (mainCs.PartialCuiFiles.Contains(cs.CUIFileName)) doc.SendStringToExecute(".cuiunload " + cs.CUIFileBaseName + " ", false, false, false); //装载CUI文件,注意文件名必须是带路径的 doc.SendStringToExecute(".cuiload " + cs.CUIFileName + " ", false, false, false); //恢复CMDECHO及FILEDIA系统变量的初始值 doc.SendStringToExecute("(setvar \"FILEDIA\" " + oldFileDia.ToString() + ")(princ) ", false, false, false); doc.SendStringToExecute("(setvar \"CMDECHO\" " + oldCmdEcho.ToString() + ")(princ) ", false, false, false); } /// /// 添加菜单项所要执行的宏 /// /// CUI文件 /// 宏的显示名称 /// 宏的具体命令 /// 宏的标识符 /// 宏的状态栏提示信息 /// 宏的图标 /// 返回创建的宏 public static MenuMacro AddMacro(this CustomizationSection source, string name, string command, string tag, string helpString, string imagePath) { MenuGroup menuGroup=source.MenuGroup;//获取CUI文件中的菜单组 //判断菜单组中是否已经定义与菜单组名相同的宏集合 MacroGroup mg=menuGroup.FindMacroGroup(menuGroup.Name); if (mg == null)//如果宏集合没有定义,则创建一个与菜单组名相同的宏集合 mg = new MacroGroup(menuGroup.Name, menuGroup); //如果已经宏已经被定义,则返回 foreach (MenuMacro macro in mg.MenuMacros) { if (macro.ElementID == tag) return null; } //在宏集合中创建一个命令宏 MenuMacro MenuMacro=new MenuMacro(mg, name, command, tag); //指定命令宏的说明信息,在状态栏中显示 MenuMacro.macro.HelpString = helpString; //指定命令宏的大小图像的路径 MenuMacro.macro.LargeImage = MenuMacro.macro.SmallImage = imagePath; return MenuMacro;//返回命令宏 } /// /// 添加下拉菜单 /// /// 包含菜单的菜单组 /// 菜单名 /// 菜单的别名 /// 菜单的标识字符串 /// 返回下拉菜单对象 public static PopMenu AddPopMenu(this MenuGroup menuGroup, string name, StringCollection aliasList, string tag) { PopMenu pm=null;//声明下拉菜单对象 //如果菜单组中没有名称为name的下拉菜单 if (menuGroup.PopMenus.IsNameFree(name)) { //为下拉菜单指定显示名称、别名、标识符和所属的菜单组 pm = new PopMenu(name, aliasList, tag, menuGroup); } return pm;//返回下拉菜单对象 } /// /// 为菜单添加菜单项 /// /// 菜单项所属的菜单 /// 菜单项的位置 /// 菜单项的显示名称 /// 菜单项的命令宏的Id /// 返回添加的菜单项 public static PopMenuItem AddMenuItem(this PopMenu parentMenu, int index, string name, string macroId) { PopMenuItem newPmi=null; //如果存在名为name的菜单项,则返回 foreach (PopMenuItem pmi in parentMenu.PopMenuItems) if (pmi.Name == name) return newPmi; //定义一个菜单项对象,指定所属的菜单及位置 newPmi = new PopMenuItem(parentMenu, index); ////如果name不为空,则指定菜单项的显示名为name,否则会使用命令宏的名称 if (name != null) newPmi.Name = name; newPmi.MacroID = macroId;//菜单项的命令宏的ID

CAD.net 处理菜单相关功能

PixPin_2024-10-24_15-37-38 PixPin_2024-10-24_15-37-56

csharp [CommandMethod("AddMenu")] public void AddMenu() { string currentPath = GetProgramDir();//当前运行目录 //装载局部CUI文件,若不存在,则创建 CustomizationSection cs = activeDoc.AddCui(cuiFile, menuGroupName); //添加表示绘制直线、多段线、矩形和圆的命令宏 cs.AddMacro("直线", "^C^C_Line ", "ID_MyLine", "创建直线段: LINE", currentPath + "\\Image\\Line.BMP"); cs.AddMacro("多段线", "^C^C_Pline ", "ID_MyPLine", "创建二维多段线: PLINE", currentPath + "\\Image\\Polyline.BMP"); cs.AddMacro("矩形", "^C^C_Rectang ", "ID_MyRectang", "创建矩形多段线: RECTANG", currentPath + "\\Image\\Rectangle.BMP"); cs.AddMacro("圆", "^C^C_circle ", "ID_MyCircle", "用指定半径创建圆: CIRCLE", currentPath + "\\Image\\Circle.BMP"); //添加表示复制、删除、移动及旋转操作的命令宏 cs.AddMacro("复制", "^C^CCopy ", "ID_MyCopy", "复制对象: COPY", currentPath + "\\Image\\Copy.BMP"); cs.AddMacro("删除", "^C^CErase ", "ID_MyErase", "从图形删除对象: ERASE", currentPath + "\\Image\\Erase.BMP"); cs.AddMacro("移动", "^C^CMove ", "ID_MyMove", "将对象在指定方向上平移指定的距离: MOVE", currentPath + "\\Image\\Move.BMP"); cs.AddMacro("旋转", "^C^CRotate ", "ID_MyRotate", "绕基点旋转对象: ROTATE", currentPath + "\\Image\\Rotate.BMP"); //设置用于下拉菜单别名的字符串集合 StringCollection sc = new StringCollection(); sc.Add("MyPop1"); //添加名为“我的菜单”的下拉菜单,如果已经存在,则返回null PopMenu myMenu = cs.MenuGroup.AddPopMenu("我的菜单", sc, "ID_MyMenu"); if (myMenu != null)//如果“我的菜单”还没有被添加,则添加菜单项 { //从上到下为“我的菜单”添加绘制直线、多段线、矩形和圆的菜单项 myMenu.AddMenuItem(-1, "直线", "ID_MyLine"); myMenu.AddMenuItem(-1, "多段线", "ID_MyPLine"); myMenu.AddMenuItem(-1, "矩形", "ID_MyRectang"); myMenu.AddMenuItem(-1, "圆", "ID_MyCircle"); myMenu.AddSeparator(-1);//为菜单添加一分隔条 //添加一个名为“修改”的子菜单 PopMenu menuModify = myMenu.AddSubMenu(-1, "修改", "ID_MyModify"); //从上到下为“修改”子菜单添加复制、删除、移动及旋转操作的菜单项 menuModify.AddMenuItem(-1, "复制", "ID_MyCopy"); menuModify.AddMenuItem(-1, "删除", "ID_MyErase"); menuModify.AddMenuItem(-1, "移动", "ID_MyMove"); menuModify.AddMenuItem(-1, "旋转", "ID_MyRotate"); } cs.LoadCui();//必须装载CUI文件,才能看到添加的菜单 }

增加toolbar

C#
[CommandMethod("AddToolbar")]
public void AddToolbar()
{
    //装载局部CUI文件,若不存在,则创建
    CustomizationSection cs = activeDoc.AddCui(cuiFile, menuGroupName);
    //添加名为“我的工具栏”的工具栏
    Toolbar barDraw = cs.MenuGroup.AddToolbar("我的工具栏");
    if (barDraw == null)
        return;
    //为“我的工具栏”添加绘制直线、多段线、矩形和圆的按钮
    barDraw.AddToolbarButton(-1, "直线", "ID_MyLine");
    barDraw.AddToolbarButton(-1, "多段线", "ID_MyPLine");
    barDraw.AddToolbarButton(-1, "矩形", "ID_MyRectang");
    barDraw.AddToolbarButton(-1, "圆", "ID_MyCircle");
    //添加名为“修改工具栏”的工具栏,用于弹出式工具栏
    Toolbar barModify = cs.MenuGroup.AddToolbar("修改工具栏");
    if (barModify == null)
    {
        cs.LoadCui();//必须装载CUI文件,才能看到添加的菜单
        return;
    }
    //为“修改工具栏”添加复制、删除、移动及旋转操作的按钮
    ToolbarButton buttonCopy = barModify.AddToolbarButton(-1, "复制", "ID_MyCopy");
    ToolbarButton buttonErase = barModify.AddToolbarButton(-1, "删除", "ID_MyErase");
    ToolbarButton buttonMove = barModify.AddToolbarButton(-1, "移动", "ID_MyMove");
    ToolbarButton buttonRotate = barModify.AddToolbarButton(-1, "旋转", "ID_MyRotate");
    //将“修改工具栏”附着到“我的工具栏”的最后
    barDraw.AttachToolbarToFlyout(-1, barModify);
    //barDraw.ToolbarVisible = ToolbarVisible.show;
    cs.LoadCui();//必须装载CUI文件,才能看到添加的菜单
}

增加右键菜单

C#
[CommandMethod("AddDefaultContextMenu")]
public void AddDefaultContextMenu()
{
    //定义一个ContextMenuExtension对象,用于表示快捷菜单
    ContextMenuExtension contextMenu = new ContextMenuExtension();
    contextMenu.Title = "我的快捷菜单";//设置快捷菜单的标题
    MenuItem mi = new MenuItem("复制");//添加名为"复制"的菜单项
    //为"复制"菜单项添加单击事件
    mi.Click += new EventHandler(mi_Click);
    contextMenu.MenuItems.Add(mi);//将"复制"菜单项添加到快捷菜单中
    mi = new MenuItem("删除");//添加名为"删除"的菜单项
    //mi.Icon = new Icon(GetProgramDir() + "\\Image\\copy.ico");
    //为"删除"菜单项添加单击事件
    mi.Click += new EventHandler(mi_Click);
    contextMenu.MenuItems.Add(mi);//将"删除"菜单项添加到快捷菜单中
    //为应用程序添加定义的快捷菜单
    Application.AddDefaultContextMenuExtension(contextMenu);
}
void mi_Click(object sender, EventArgs e)
{
    MenuItem mi = sender as MenuItem;//获取发出命令的快捷菜单项
    //根据快捷菜单项的名字,分别调用对应的命令
    if (mi.Text == "复制")
        activeDoc.SendStringToExecute("_Copy ", true, false, true);
    else if (mi.Text == "删除")
        activeDoc.SendStringToExecute("_Erase ", true, false, true);
}

完整代码如下所示:

image-20241024163522376

Class1.cs

C#
using System;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using ZwSoft.ZwCAD.ApplicationServices;
using ZwSoft.ZwCAD.Customization;
using ZwSoft.ZwCAD.DatabaseServices;
using ZwSoft.ZwCAD.EditorInput;
using ZwSoft.ZwCAD.Runtime;
using ZwSoft.ZwCAD.Windows;

namespace CuiDemo
{
    public class Class1
    {
        //设置CUI文件的名字,将其路径设置为当前运行目录
        string cuiFile = GetProgramDir() + "\\MyCustom.cuix";
        string menuGroupName = "MyCustom";//菜单组名
        //获得活动文档
        Document activeDoc = ZwSoft.ZwCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument;
        public static string GetProgramDir()
        {
            string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            return directory;
        }
        public Class1()
        {
            //添加程序退出时事件处理
            Application.QuitWillStart += new EventHandler(Application_QuitWillStart);
        }
        void Application_QuitWillStart(object sender, EventArgs e)
        {
            //由于触发此事件前文档已关闭,所以需通过模板重建,以便命令能够执行
            Document doc = Application.DocumentManager.Add("acadiso.dwt");
            //获取FILEDIA系统变量的值
            object oldFileDia = Application.GetSystemVariable("FILEDIA");
            //设置FILEDIA = 0,禁止显示文件对话框,这样可以通过程序输入文件名
            Application.SetSystemVariable("FILEDIA", 0);
            //获取主CUI
            CustomizationSection mainCs = doc.GetMainCustomizationSection();
            //如果存在指定的局部CUI文件,则进行卸载
            if (mainCs.PartialCuiFiles.Contains(cuiFile))
                doc.Editor.PostCommand("cuiunload " + menuGroupName + " ");
            //doc.SendStringToExecute("cuiunload " + menuGroupName + " ", false, false, false);
            //恢复FILEDIA系统变量的值
            Application.SetSystemVariable("FILEDIA", oldFileDia);
        }
        [CommandMethod("AddMenu")]
        public void AddMenu()
        {
            string currentPath = GetProgramDir();//当前运行目录
            //装载局部CUI文件,若不存在,则创建
            CustomizationSection cs = activeDoc.AddCui(cuiFile, menuGroupName);
            //添加表示绘制直线、多段线、矩形和圆的命令宏            
            cs.AddMacro("直线", "^C^C_Line ", "ID_MyLine", "创建直线段:   LINE", currentPath + "\\Image\\Line.BMP");
            cs.AddMacro("多段线", "^C^C_Pline ", "ID_MyPLine", "创建二维多段线:  PLINE", currentPath + "\\Image\\Polyline.BMP");
            cs.AddMacro("矩形", "^C^C_Rectang ", "ID_MyRectang", "创建矩形多段线:  RECTANG", currentPath + "\\Image\\Rectangle.BMP");
            cs.AddMacro("圆", "^C^C_circle ", "ID_MyCircle", "用指定半径创建圆:   CIRCLE", currentPath + "\\Image\\Circle.BMP");
            //添加表示复制、删除、移动及旋转操作的命令宏
            cs.AddMacro("复制", "^C^CCopy ", "ID_MyCopy", "复制对象:   COPY", currentPath + "\\Image\\Copy.BMP");
            cs.AddMacro("删除", "^C^CErase ", "ID_MyErase", "从图形删除对象:   ERASE", currentPath + "\\Image\\Erase.BMP");
            cs.AddMacro("移动", "^C^CMove ", "ID_MyMove", "将对象在指定方向上平移指定的距离:  MOVE", currentPath + "\\Image\\Move.BMP");
            cs.AddMacro("旋转", "^C^CRotate ", "ID_MyRotate", "绕基点旋转对象:  ROTATE", currentPath + "\\Image\\Rotate.BMP");
            //设置用于下拉菜单别名的字符串集合
            StringCollection sc = new StringCollection();
            sc.Add("MyPop1");
            //添加名为“我的菜单”的下拉菜单,如果已经存在,则返回null
            PopMenu myMenu = cs.MenuGroup.AddPopMenu("我的菜单", sc, "ID_MyMenu");
            if (myMenu != null)//如果“我的菜单”还没有被添加,则添加菜单项
            {
                //从上到下为“我的菜单”添加绘制直线、多段线、矩形和圆的菜单项
                myMenu.AddMenuItem(-1, "直线", "ID_MyLine");
                myMenu.AddMenuItem(-1, "多段线", "ID_MyPLine");
                myMenu.AddMenuItem(-1, "矩形", "ID_MyRectang");
                myMenu.AddMenuItem(-1, "圆", "ID_MyCircle");
                myMenu.AddSeparator(-1);//为菜单添加一分隔条
                //添加一个名为“修改”的子菜单
                PopMenu menuModify = myMenu.AddSubMenu(-1, "修改", "ID_MyModify");
                //从上到下为“修改”子菜单添加复制、删除、移动及旋转操作的菜单项
                menuModify.AddMenuItem(-1, "复制", "ID_MyCopy");
                menuModify.AddMenuItem(-1, "删除", "ID_MyErase");
                menuModify.AddMenuItem(-1, "移动", "ID_MyMove");
                menuModify.AddMenuItem(-1, "旋转", "ID_MyRotate");
            }
            cs.LoadCui();//必须装载CUI文件,才能看到添加的菜单
        }
        [CommandMethod("AddToolbar")]
        public void AddToolbar()
        {
            //装载局部CUI文件,若不存在,则创建
            CustomizationSection cs = activeDoc.AddCui(cuiFile, menuGroupName);
            //添加名为“我的工具栏”的工具栏
            Toolbar barDraw = cs.MenuGroup.AddToolbar("我的工具栏");
            if (barDraw == null)
                return;
            //为“我的工具栏”添加绘制直线、多段线、矩形和圆的按钮
            barDraw.AddToolbarButton(-1, "直线", "ID_MyLine");
            barDraw.AddToolbarButton(-1, "多段线", "ID_MyPLine");
            barDraw.AddToolbarButton(-1, "矩形", "ID_MyRectang");
            barDraw.AddToolbarButton(-1, "圆", "ID_MyCircle");
            //添加名为“修改工具栏”的工具栏,用于弹出式工具栏
            Toolbar barModify = cs.MenuGroup.AddToolbar("修改工具栏");
            if (barDraw == null)
{
    cs.LoadCui();//必须装载CUI文件,才能看到添加的菜单
    return;
}
            //为“修改工具栏”添加复制、删除、移动及旋转操作的按钮
            return newPmi;//返回菜单项对象
        }

        /// <summary>
        /// 为下拉菜单添加子菜单
        /// </summary>
        /// <param name="parentMenu">下拉菜单</param>
        /// <param name="index">子菜单的位置</param>
        /// <param name="name">子菜单的显示名称</param>
        /// <param name="tag">子菜单的标识字符串</param>
        /// <returns>返回添加的子菜单</returns>
        public static PopMenu AddSubMenu(this PopMenu parentMenu, int index, string name, string tag)
        {
            PopMenu pm=null;//声明子菜单对象(属于下拉菜单类)
            //如果菜单组中没有名称为name的下拉菜单
            if (parentMenu.CustomizationSection.MenuGroup.PopMenus.IsNameFree(name))
            {
                //为子菜单指定显示名称、标识符和所属的菜单组,别名设为null
                pm = new PopMenu(name, null, tag, parentMenu.CustomizationSection.MenuGroup);
                //为子菜单指定其所属的菜单
                PopMenuRef menuRef=new PopMenuRef(pm, parentMenu, index);
            }
            return pm;//返回子菜单对象
        }

        /// <summary>
        /// 为菜单添加分隔条
        /// </summary>
        /// <param name="parentMenu">下拉菜单</param>
        /// <param name="index">分隔条的位置</param>
        /// <returns>返回添加的分隔条</returns>
        public static PopMenuItem AddSeparator(this PopMenu parentMenu, int index)
        {
            //定义一个分隔条并返回
            return new PopMenuItem(parentMenu, index);
        }

        /// <summary>
        /// 添加工具栏
        /// </summary>
        /// <param name="menuGroup">工具栏所属的菜单组</param>
        /// <param name="name">工具栏的显示名称</param>
        /// <returns>返回添加的工具栏</returns>
        public static Toolbar AddToolbar(this MenuGroup menuGroup, string name)
        {
            Toolbar tb=null;//声明一个工具栏对象
            //如果菜单组中没有名称为name的工具栏
            if (menuGroup.Toolbars.IsNameFree(name))
            {
                //为工具栏指定显示名称和所属的菜单组
                tb = new Toolbar(name, menuGroup);
                //设置工具栏为浮动工具栏
                tb.ToolbarOrient = ToolbarOrient.floating;
                //设置工具栏可见
                tb.ToolbarVisible = ToolbarVisible.show;
            }
            return tb;//返回工具栏对象
        }

        /// <summary>
        /// 向工具栏添加按钮
        /// </summary>
        /// <param name="parent">按钮所属的工具栏</param>
        /// <param name="index">按钮在工具栏上的位置</param>
        /// <param name="name">按钮的显示名称</param>
        /// <param name="macroId">按钮的命令宏的Id</param>
        /// <returns>返回工具栏按钮对象</returns>
        public static ToolbarButton AddToolbarButton(this Toolbar parent, int index, string name, string macroId)
        {
            //创建一个工具栏按钮对象,指定其命令宏Id、显示名称、所属的工具栏和位置
            ToolbarButton button=new ToolbarButton(macroId, name, parent, index);
            return button;//返回工具栏按钮对象
        }

        /// <summary>
        /// 向工具栏添加弹出式工具栏
        /// </summary>
        /// <param name="parent">工具栏所属的父工具栏</param>
        /// <param name="index">弹出式工具栏在父工具栏上的位置</param>
        /// <param name="toolbarRef">弹出式工具栏所引用的工具栏</param>
        public static void AttachToolbarToFlyout(this Toolbar parent, int index, Toolbar toolbarRef)
        {
            //创建一个弹出式工具栏,指定其所属的工具栏和位置
            ToolbarFlyout flyout=new ToolbarFlyout(parent, index);
            //指定弹出式工具栏所引用的工具栏 
            flyout.ToolbarReference = toolbarRef.Name;
            //引用的工具栏初始状态不可见
            toolbarRef.ToolbarVisible = ToolbarVisible.show;
        }
    }
}

找到PostCommand

打开命令行

image-20241119092752464

将目录切换到CAD安装目录

运行命令

Bash
dumpbin.exe /exports zwcad.exe >D:\zwcad.txt

对照txt中zcedPostCommand的函数,写出如下代码:

C#
[DllImport("zwcad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedPostCommand")]
extern static private int zcedPostCommand(string strExpr);
/// <summary>
/// 调用C++的acedPostCommand函数
/// </summary>
/// <param name="ed">无意义,只是为了定义扩展函数</param>
/// <param name="expression">要执行的命令字符串</param>
public static void PostCommand(this Editor ed, string expression)
{
    zcedPostCommand(expression);
}

WPF里引用svg图标

第一种方式

引入xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" https://elinamllc.github.io/SharpVectors/articles/index.html

下载sharpvectors

C#
<Image Source="{svgc:SvgImage Source=/Images/SVG/email 33.svg}" />

第二种方式

将Path路径提出来,设置key值,放置在资源里

C#
<Window.Resources>
    <!--Close Image-->
    <PathFigureCollection x:Key="ClosePathData">
        M482.048 0h64v533.333h-64V0z
        M695.381 132.864v54.187c128 69.845 225.494 208.618 225.494 368.469 0 230.08-184.939 416.64-415.019 416.64-230.123 0-406.016-186.56-406.016-416.64 0-159.85 83.541-298.624 232.875-368.47v-54.186C162.048 206.528 52.075 368 52.075 555.52c0 256.96 203.37 465.259 460.33 465.259 256.939 0 459.52-208.299 459.52-465.259 0-187.52-105.877-348.992-276.544-422.656z
    </PathFigureCollection>
    <DrawingImage x:Key="CloseImage">
        <DrawingImage.Drawing>
            <GeometryDrawing>
                <GeometryDrawing.Brush>
                    <SolidColorBrush  Color="#666D73"/>
                </GeometryDrawing.Brush>
                <GeometryDrawing.Geometry>
                    <PathGeometry FillRule="Nonzero"  Figures="{StaticResource ClosePathData}" />
                </GeometryDrawing.Geometry>
            </GeometryDrawing>
        </DrawingImage.Drawing>
    </DrawingImage>
</Window.Resources>

多条Path就放多个PathFigureCollection里,使用时,直接调用

C#
<Image Source="{StaticResource CloseImage}" />

显示图标

使用SvgToXaml工具

资源文件

C#
 <!--  Close Image  -->
        <PathFigureCollection x:Key="ClosePathData">
            M482.048 0h64v533.333h-64V0z
            M695.381 132.864v54.187c128 69.845 225.494 208.618 225.494 368.469 0 230.08-184.939 416.64-415.019 416.64-230.123 0-406.016-186.56-406.016-416.64 0-159.85 83.541-298.624 232.875-368.47v-54.186C162.048 206.528 52.075 368 52.075 555.52c0 256.96 203.37 465.259 460.33 465.259 256.939 0 459.52-208.299 459.52-465.259 0-187.52-105.877-348.992-276.544-422.656z
        </PathFigureCollection>
        <DrawingImage x:Key="CloseImage">
            <DrawingImage.Drawing>
                <GeometryDrawing>
                    <GeometryDrawing.Brush>
                        <SolidColorBrush Color="Gray" />
                    </GeometryDrawing.Brush>
                    <GeometryDrawing.Geometry>
                        <PathGeometry Figures="{StaticResource ClosePathData}" FillRule="Nonzero" />
                    </GeometryDrawing.Geometry>
                </GeometryDrawing>
            </DrawingImage.Drawing>
        </DrawingImage>

        <!--BoxIcons_LogosBlenderGeometryKey-->
        <Geometry x:Key="BoxIcons_LogosBlenderGeometryKey">F1 M48,48z M0,0z M530.219,404.864C531.798,433.323 545.707,458.368 566.742,476.16 587.693,493.682 614.924,504.322 644.639,504.322 644.913,504.322 645.187,504.321 645.461,504.319L645.419,504.319C675.712,504.319 703.446,493.652 724.139,476.159 745.131,458.367 759.083,433.279 760.662,404.863 762.283,375.594 750.55,348.415 729.899,328.276 708.276,307.619 678.912,294.906 646.578,294.906 646.185,294.906 645.793,294.908 645.401,294.912L645.461,294.912C645.109,294.908 644.693,294.906 644.277,294.906 611.938,294.906 582.567,307.62 560.892,328.321L560.939,328.277C540.331,348.416,528.555,375.594,530.219,404.864z M346.624,347.264C346.837,336.171 350.421,314.539 355.712,297.6 367.982,259.513 387.391,226.735 412.548,199.316L412.373,199.509C439.559,169.668,472.554,145.638,509.633,129.121L511.487,128.383C549.32,111.262 593.517,101.286 640.045,101.286 640.599,101.286 641.153,101.287 641.706,101.29L641.62,101.29C641.736,101.29 641.874,101.29 642.012,101.29 688.999,101.29 733.619,111.424 773.807,129.625L771.796,128.81C810.672,146.268,843.623,170.392,870.613,200.04L870.826,200.277C895.749,227.577,915.125,260.406,926.878,296.668L927.402,298.538C932.668,314.564,936.452,333.214,938.012,352.493L938.069,353.365C938.518,359.018 938.773,365.605 938.773,372.251 938.773,422.081 924.397,468.553 899.566,507.747L900.18,506.709C882.233,535.122,860.325,559.175,834.892,578.899L834.26,579.37 834.303,579.413 566.996,784.768C549.46,798.165 520.02,798.123 500.82,784.683 481.321,771.072 479.145,748.587 496.383,734.336L496.34,734.293 607.529,643.797 268.116,643.413C240.127,643.413 213.204,624.981 207.871,601.77 202.41,578.09 221.439,558.463 250.58,558.378L250.537,558.25 422.356,558.591 114.516,322.346C85.6310000000001,300.202 76.2870000000001,263.381 94.4630000000001,240.042 112.98,216.319 152.234,216.319 181.503,239.957L348.842,376.874C348.885,376.874,346.453,358.399,346.623,347.263z M776.619,285.397C742.144,250.24 693.931,230.314 641.664,230.229 589.355,230.144 541.141,249.898 506.624,284.97 490.736,300.874 478.188,320.117 470.123,341.556L469.76,342.656C464.013,357.906 460.687,375.534 460.687,393.939 460.687,398.581 460.899,403.174 461.313,407.708L461.27,407.125C463.147,428.672 469.505,449.237 479.787,467.797 489.856,486.101 503.766,502.57 520.832,516.608 553.277,542.868 595.049,558.766 640.534,558.766 640.931,558.766 641.328,558.765 641.725,558.762L641.664,558.762C642.121,558.766 642.661,558.768 643.201,558.768 688.501,558.768 730.118,543.003 762.866,516.66L762.496,516.948C779.52,503.039 793.429,486.612 803.499,468.351 813.739,449.748 820.139,429.268 822.016,407.722 822.385,403.772 822.596,399.18 822.596,394.538 822.596,376.136 819.286,358.507 813.23,342.214L813.568,343.252C805.052,320.672,792.485,301.385,776.609,285.386L776.619,285.396z</Geometry>
        <DrawingGroup x:Key="BoxIcons_LogosBlenderDrawingGroupKey" ClipGeometry="M0,0 V48 H48 V0 H0 Z">
            <DrawingGroup Transform="0.0562308674397924,0,0,-0.0562308674397924,-4.78802069564223,49.1935494114674">
                <GeometryDrawing Brush="#FFD3D3D3" Geometry="{StaticResource BoxIcons_LogosBlenderGeometryKey}">
                    <GeometryDrawing.Pen>
                        <Pen
                            Brush="#FF000000"
                            EndLineCap="Flat"
                            LineJoin="Miter"
                            StartLineCap="Flat"
                            Thickness="1" />
                    </GeometryDrawing.Pen>
                </GeometryDrawing>
            </DrawingGroup>
        </DrawingGroup>
        <DrawingImage x:Key="BoxIcons_LogosBlenderDrawingImageKey" Drawing="{StaticResource BoxIcons_LogosBlenderDrawingGroupKey}" />

image-20240907113623699

C#
 <WrapPanel>
                <Image Margin="0,0,10,0" Source="{StaticResource BoxIcons_LogosBlenderDrawingImageKey}" />
                <Viewbox
                    Grid.Row="1"
                    Grid.Column="2"
                    Margin="0,0,10,0">
                    <Path
                        Data="M839.040 242.731c-13.009-30.022-27.582-55.778-44.492-79.769l0.93 1.39c-22.912-32.725-41.728-55.339-56.149-67.925-22.4-20.565-46.464-31.147-72.192-31.744-18.432 0-40.704 5.248-66.645 15.915-26.027 10.624-49.92 15.829-71.808 15.829-22.912 0-47.488-5.205-73.813-15.829-26.283-10.667-47.531-16.256-63.787-16.768-24.619-1.067-49.237 9.771-73.771 32.597-15.659 13.653-35.243 37.12-58.752 70.315-25.173 35.371-45.867 76.544-62.080 123.349-17.365 50.645-26.069 99.627-26.069 147.072 0 54.315 11.733 101.205 35.243 140.459 18.116 31.030 43.055 56.256 72.877 74.187l0.936 0.522c28.511 17.374 62.912 27.821 99.722 28.244l0.118 0.001c19.627 0 45.355-6.059 77.227-18.005s52.352-18.005 61.269-18.005c6.741 0 29.397 7.125 67.968 21.248 36.395 13.099 67.115 18.517 92.288 16.384 68.267-5.504 119.509-32.384 153.6-80.853-61.013-36.992-91.179-88.747-90.581-155.179 0.512-51.755 19.328-94.805 56.192-128.981 15.812-15.151 34.402-27.559 54.904-36.362l1.202-0.459c-4.523-13.099-9.301-25.6-14.336-37.632zM682.581 858.453c0-40.533-14.848-78.421-44.331-113.451-35.669-41.643-78.763-65.749-125.483-61.952-0.571 4.506-0.896 9.719-0.896 15.009 0 0.124 0 0.247 0.001 0.371v-0.019c0 38.955 16.896 80.597 47.061 114.688 15.019 17.237 34.133 31.616 57.301 43.051 23.125 11.264 44.971 17.493 65.536 18.56 0.555-5.461 0.811-10.88 0.811-16.256z"
                        Fill="Gray"
                        StrokeThickness="1" />
                </Viewbox>
                <Image Margin="0,0,10,0" Source="{StaticResource CloseImage}" />
                <Image Margin="0,0,10,0" Source="{svgc:SvgImage Source=/Images/SVG/email 33.svg}" />
            </WrapPanel>

在Button中使用

C#
 <local:ImageButton
            x:Name="btn_Image"
            Grid.Row="3"
            HorizontalContentAlignment="Center"
            VerticalContentAlignment="Center"
            Background="White"
            Content="播放"
            FontSize="16"
            Foreground="Gray"
            Icon="{StaticResource edit_13DrawingImage}"
            IconContentMargin="10,0,0,0"
            IconHeight="32"
            IconMouseOver="{StaticResource discount_21DrawingImage}"
            IconWidth="32"
            MouseDownForeground="Blue"
            MouseOverForeground="#FFFFFF"
            ToolTip="播放" />
        <local:ButtonEx
            Grid.Row="4"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            ButtonType="IconText"
            Content="11122"
            Icon="{StaticResource discount_21DrawingImage}" />