创建第一个VSTA项目

前言

该文档示例创建一个VSTA项目,介绍其中宏功能的实现步骤,分析代码的含义及其定义,并试图分析C#一些关键词的构造方法以帮助读者深入理解和借鉴。


我们来看一下如何创建自己的VSTA项目。我们在泊物窗中单击“Visual Studio Tools for Applications”,然后单击“新建”,单击“新建宏项目”。如下图。
在这里插入图片描述

会弹出保存目录和项目名对话框。默认是保存在C盘,C盘是系统盘,所以我们更改目录,找到电脑上Coreldraw的安装目录,找到“CorelVSTA”目录,改一下项目名,比如“LixianVsta”,点击“保存”。比如下图。
在这里插入图片描述

保存在CorelVSTA目录,项目就能随Coreldraw的启动而自动加载,而保存在其它地方就要手动加载了。可以看到,在保存目录下,生成了一个“CGSaddon”文件,这就是我们的VSTA项目。和VBA一样,也许我们要经常备份一个这个文件。如下图。
在这里插入图片描述

而在宏管理器项目列表中,也在“VSTAGlobal”下面生成了新建的项目。如下图。

在这里插入图片描述

全部展开“L ixianVsta”,鼠标右击“Macro1”,进入VSTA编程环境。先看一下“解决方案资源管理器”:
在这里插入图片描述

可以看到VSTA解决方案下多了一个项目L ixianVsta,有两个项目了,可见一个解决方案可以加入多个项目。利用这一点,我们在使用Visual Studio时就可以把我们的所有项目都加进来统一管理。
同时看到新建的项目的C#文档和VSTAGlobal中的C#文档基本一样。如下图。
在这里插入图片描述

把“Macro1”改成“SelectionFullScreen” (所选全屏)。经测试,宏名中出现中文无法运行,不知Corel公司会不会在后续版本中改进。如下图。
在这里插入图片描述

回到Coreldraw工作环境,也可以看到原来的“Macro1”已经变成了“SelectionFullScreen”。如下图。

将“SelectionFullScreen”函数代码块,改为以下代码:

    app.ActiveWindow.ActiveView.ToFitSelection();
    app.ActiveWindow.FullScreen = true;

如下图。
在这里插入图片描述

回到Coreldraw中,选择单个或多个图形,双击VSTA宏“SelectionFullScreen”,所选区域会放大显示在电脑整个屏幕,包括遮挡底部的任务栏。如下图。
在这里插入图片描述

现在的编程大都是面向对象的。在Coreldraw提供给用户进行二次开发的程序集“VGCore”中,提供了丰富的对象供我们使用,这些对象构成了按一定的层次构成了对象模型层次结构。Application处于程序集的最顶层,从这里出发,我们可以一层一层的深入,访问到我们所需要操作的对象。详细资料可以参阅百度贴吧coreldrawvba中Zebe的“【新手必知】CorelDRAW的对象模型层次结构”一文,网址是:https://tieba.baidu.com/p/6163659582?red_tag=1447808429。
每个对象下大都有自己的属性和方法。通过引用对象,获取或设置对象的属性值,使用对象的方法,我就能按自己的需要改变对象的呈现形式,达到编程操作Coreldraw的目标。
“app.ActiveWindow.ActiveView.ToFitSelection();”代码
app :从Coreldraw的顶层对象Application(app)去逐层访问下一层的对象、属性或方法。我们把鼠标停放在app上,可以看到相关信息。如下图
在这里插入图片描述

可以看到,app定义为Corel.Interop.VGCore.Application,对象类型为Corel.Interop.VGCore程序集中的Application类型。Main.app是指app是Main类中定义的。
我们右单击app单词,在弹出的界面中点击“转到定义”。如下图。
在这里插入图片描述

文档自动切换到Macro.Internal.cs:

在这里插入图片描述

可以看到,app的定义是在Macro.Internal.cs中的Main类中,即申明一个私有的只可以在Main类中访问的Corel. Interop. VGCore. Application对象。
我们在VSTA的“视图”菜单打开“对象浏览器”,在搜索框中输入“Application”,回车键。结果如下图。

在这里插入图片描述

可以看到,Application是接口,Corel.Interop. VGCore的成员。但在VSTA中已经实例化为Com对象。如果你试图加入以下代码:

    Corel.Interop.VGCore.Application bpp = new Corel.Interop.VGCore.Application();
    MessageBox.Show(bpp.ActiveDocument.Name);

如下图。
在这里插入图片描述

运行后在调试时就发生了错误。

ActiveWindow:我们把鼠标停放在ActiveWindow上,可以看到相关信息。如下图
在这里插入图片描述

可以看到,它其实也是Window。我们在试图菜单打开“对象浏览器”,在搜索框中输入ActiveWindow,回车键。结果如下。
在这里插入图片描述

印证了ActiveWindow其实也是Window的理解, IVGApplication的成员。前面已经讨论了Application,IVGApplication是什么呢。我们在对象浏览器中搜索“IVGApplication”。结果如下:
在这里插入图片描述

可以看到,IVGApplication也是VGCore的成员,也定义为接口,右上还有许多方法和属性。也许我们平时都使用Application来访问对象,很少使用IVGApplication,我们试图写下如下代码:
Corel.Interop.VGCore.IVGApplication bpp = new Corel.Interop.VGCore.IVGApplication();
立刻发生了错误。如下图。
在这里插入图片描述

说明IVGApplication是一个抽象接口。什么是接口,查阅和浏览器翻译微软的文档,有如下说明:接口包含一组非抽象的相关功能的定义。class或者是struct必须执行。接口可以定义static方法,这些方法必须有一个实现。从C#8.0开始,接口可以为成员定义默认实现。接口不能声明实例数据,如字段、自动实现的属性或类似属性的事件(https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces)。
接口用来被继承,比如父为儿子们准备了包含丰富的财富(内容,包括属性和方法等)的接口,让子来继承。下面试图分析一下接口的用法。
因为IVGApplication中有一个方法CreateDocument,我们在文档中输入以下代码:

IVGApp iVGApp = new IVGApp();
iVGApp.CreateDocument();
class IVGApp: IVGApplication//这里冒号的作用是继承
{
public void CreateDocument()//试图继承IVGApplication中的CreateDocument()方法并发扬光大
{
        MessageBox.Show("IVGApplicationCreateDocument");
}
}

如下图。
在这里插入图片描述

回到Coreldraw中,发生了许多的错误。如下图。
在这里插入图片描述

可见要继承IVGApplication的抽象接口,必须对IVGApplication的所有成员做出继承并重写内容。面对这么多的成员这一点我们是很难做成了,也许 Application已经帮我们做好了。就不用去费心了。
那我们写一个最简单的接口和继承的例子。
我们在类的层面写入以下代码:

    interface  IVGApp
    {
        public void CreateDocument()
        {
            
        }

    }
    class MyIVGApp : IVGApp
    {
        public void CreateDocument()
        {
            MessageBox.Show("IVGApplicationCreateDocument");
        }

}

在宏中写入以下代码:

    IVGApp iVGApp = new MyIVGApp();
iVGApp.CreateDocument();

如下图。
在这里插入图片描述

代码中没发现错误,回到Coreldraw中,就可以看到有错误:
在这里插入图片描述

“Macro.cs(30): @(COL:14)”中30就代表了错误的行号,我们改动第30行的代码,去掉关键词public:
在这里插入图片描述

再次回到Coreldraw中,还有错误:
在这里插入图片描述

在改一下第30行的代码,去掉大括号,加上英文冒号:
在这里插入图片描述

再次回到Coreldraw中,发现已经没有错误了,双击运行宏,结果如下:
在这里插入图片描述

已经成功运行。接口定义了成员的属性和方法,但没有赋予属性值和方法实现。类继承接口后必须全部再写这些成员的属性和方法。其意义可能就是程序中可能通过接口要实现数个同样的结构,使这些结构都有类似的结构而不至于是另类了。比如子必须继承父的所有属性但属性值就是你自己的了,父如果有一个特征定义“是否优秀”(属性或对象),那你一定也得继承这个属性,父就不能给这个属性赋值了,只能是由子的实际表现来赋值,你如果优秀那这个属性值就是“优秀”了;也要继承所有的方法但还是要自己去学会并有所创新,也可以加入自己的新方法。
在这里插入图片描述

ActiveWindow:我们同样右击ActiveWindow单词转到定义,可以看见其定义是再“[从元数据]”,意思就是其定义是VGCore程序集中,不是在自己文档代码中。

我们在对象浏览器中查看它的定义:

在这里插入图片描述

可见ActiveWindow也是一个Window。App对象下面有Windows对象,Windows对象下有Window,Windows是由一个或多个Window对象组成的集合。VGCore为我们提供了一个ActiveWindow的Window对象,是指当前处于活动(激活)状态的Window,让我不必再用遍历每个Window对象用某种方法去找到当前Coreldraw的活动Window。
IVGApplication和ApplicationClass下都有ActiveWindow对象,ApplicationClass一般我们比较少用。我们再看一下ApplicationClass对象下ActiveWindow的定义:

在这里插入图片描述

这里对ActiveWindow的定义中,ApplicationClass下ActiveWindow的定义,多了关键词public和virtual。关键词virtual(虚拟),查阅微软中C#的文档,其说明如下:virtual 关键字用于修改方法、属性、索引器或事件声明,并使它们可以在派生类中被重写。参见https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/virtual。
猜测我们也可以重写ActiveWindow?比如在ActiveWindow是只读的,我们在宏SelectionFullScreen的后面写入一句:
public Corel.Interop.VGCore.Window ActiveWindow { get; set; }//注意末尾没有分号,如下图:
在这里插入图片描述

运行宏,也没有出错。关于这个关键词的使用方法,用到了再去分析下。

ActiveView:其定义如下图。
在这里插入图片描述

这里Corel.Interop.VGCore.IVGWindow.ActiveView 的ActiveView是对象了,和Corel.Interop.VGCore.ActiveView中的ActiveView为接口不同。
ToFitSelection:ActiveView对象有一个方法,ToFitSelection。C#方法名中后面都必须跟一对小括号,然后大括号中写方法内容。小括号中是函数的参数,就是调用者可以输入不同的数据通过方法对操作对象作不同的控制;这里是留空,就是这个方法不需要另外传入参数。
我们再回到当新建一个宏“SelectionFulIScreen”的时候。我们再该宏的末尾加入一行代码:
[CgsAddInMacro]
这条代码的作用是新建一个宏。
再写入以下代码:

public void SelectionFullScreen()
{
    app.ActiveSelectionRange.Rotate(90);
}

如下图:
在这里插入图片描述

ActiveSelectionRange是ShapeRange(图形区域,里面可以包含一个或多个图形)一个实例对象。一般用来指代Coreldraw当前文档(Document)的当前页(Page)的当前层(Layer)的当前选中的图形区域。但我们不必这么做,也不能这样做,因为ActiveSelectionRange只能在Application的对象中才能访问到。ShapeRange好像可以跨页和跨图层,是不是可以还要具体测试,没用过。
ActiveSelectionRange对象有一个方法Rotate,使用参数是实型double,可以是小数或看起来像整数的double(因为C#中对于类似整数的double和实际上的整数(int)是有一些区别的。
运行效果如下:
在这里插入图片描述

实际上是正实数是逆时针旋转,负实数是顺时针旋转。当然您还可以指定旋转中心位置。

这里我们建立了两个Coreldraw文档编辑时常用的两个宏,完成界面如下:

在这里插入图片描述

public void SelectionFullScreen()

{
MyWindowClass myWindowClass = new MyWindowClass();
Window window = myWindowClass.ActiveWindow;
MessageBox.Show(window.Caption);
}
class MyWindowClass : ApplicationClass//继承ApplicationClass类接口,class前面不能加public,不然VSTA找不到入口函数
类的继承和virtual关键词:
Virtual一般用在修饰类的某些对象或方法,意思就是这些对象或方法都是虚的,都是可以改为实在的真正属于你自己的。
{

public override Window ActiveWindow//override重写ActiveWindow属性
{
get
{
//base指代ApplicationClass。改写让ApplicationClass类中的ActiveWindow返回该对象中的第一个窗口;

           return base.Windows[1]; 
       }
       //由于ApplicationClass中ActiveWindow是只读的,不能写set块赋值
   }

}
代码没有提示错误,返回Coreldraw也没有提示错误,但运行时就有问题了:
在这里插入图片描述

还是由于时COM编程的特性,我们再自己写一个带virtual关键词的类,继承这个类的例子。我们在“[CgsAddInMacro]”代码后面写入以下代码:

public void SelectionFullScreen()
{
NewEmployeeSalary newEmployeeSalary = new NewEmployeeSalary();//必须另外初始化一个类对象才能去访问它
newEmployeeSalary.Salary = 500000;//赋值
//由于newEmployeeSalary继承了EmployeeSalary类的结构,又没有重写GiveSalary方法,
//所以GiveSalary方法实际上是EmployeeSalary中的方法
int giveSalary = newEmployeeSalary.GiveSalary();
//giveSalary是整形,MessageBox.Show方法要求字串型参数,所以ToString方法转为字串型
MessageBox.Show(giveSalary.ToString());
MessageBox.Show(newEmployeeSalary.Salary.ToString());
}

class EmployeeSalary//员工工资类,用于管理员工工资
{
    public int changedSalary = 100000;//变化的工资,初始化值100000
    public virtual int Salary//virtual可以被重载;Salary,值是int类型的属性
    {
        //this指代类EmployeeSalary本身,不能EmployeeSalary中串用自身,创造了个this
        get { return this.changedSalary; }//这里其实可以不用this
        set { changedSalary = value; }//value指代从赋值方传来的值,只存在于set时
    }
    public virtual int GiveSalary()//GiveSalary,算出要给多少工资
    {
        return (int)(Salary * 1.3);//Salary可以看成get Salary;将工资额计提福利等,再发放工资
    }
}
class NewEmployeeSalary : EmployeeSalary//继承类EmployeeSalary的构造
{
    public override int Salary//override,重写,重载;Salary名称必须对应EmployeeSalary中的Salary
    {
        get { return base.Salary; }//base,基类,指类EmployeeSalary;base.Salary读取(get)基类属性Salary的值
        set
        {
            base.Salary = value;//用别入赋给的值,给EmployeeSalary类中的Salary属性赋值
        }
    }
}

这里的代码有点难理解,而且调试时容易造成Coreldraw崩溃,请注意保存文件。
基类编写了用应发工资乘以1.3来计算实发工资的类EmployeeSalary,本来面向基类EmployeeSalary,通过以下代码也可以实现同样的功能:

    EmployeeSalary employeeSalary = new EmployeeSalary();
    employeeSalary.Salary = 200000;
    int giveSalary = employeeSalary.GiveSalary();
    MessageBox.Show(giveSalary.ToString());

可以看到,virtual关键词的属性和方法都可以不重载就去使用。
给出重写属性Salary的例子主要还是为了研究一下重写属性对象的方法。
从这个继承例子可以看出,类的继承不同于接口的继承,不用重写所有属性和方法,因为被继承类中已经写好了属性和方法。我们只要重写需要改写的地方就可以了。Virtual关键词定义的模块给继承者提供了重写的机会,有必要我们就用“override”重写这个模块,既能使用现成的,又能通过改写实现具体的目标,我们还可以加入自己的属性和方法。在窗体的重绘中,就经常会用这种方法,改写封装的属性方法,改变窗体的呈现方式。提一下,未标识成virtual、 abstract或override的,是不能重载的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值