addin microstation c

54
Learning MicroStation Addins Step by Step[0] A series of posts in this blog will help you to learn the MicroStation Addins step by step, from elementary topics to advanced. According to the steps provided in these blogs, I believe you can quickly master this new approach of developing MicroStation application. I wrote these blogs in bilingual edition, Chinese and English, for the convenience of developers in China and other territories. For Chinese, you can read the text in blue; For English, you can read the text in black. Limited to my poor English level, I can't ensure the correctness of English edition. Just for your information. Chapter 0. Introduction and Prerequisite As of V8XM(08.09.xx.xx) a new development approach is introduced into MicroStation, we called this approach Addins. Addins is based on .NET Framework, thus you can use C#, C++/CLI or VB.NET language to develop Addins application. Compared to MVBA(MicroStation Visual Basic for Application), Addins can support command table and can be compiled into DLL. Compared to MDL(MicroStation Development Language/Library), you can use WinForm to design user interface in Addins and you can never to touch the .r resource which is tough for novice. To learn Addins programming, You'd better know and be able to use Bentley MicroStation(shorten as Mstn from now on) and Microsoft Visual Studio(shorten as VS from now on), plus have a basic knowledge of C# computer language. These three parts are not included in this series of posts, you have to find them by yourself. But don't be worry, there are a lot of Mstn related contents in Be Communities (you are accessing it) and there are plenty of VS and C# related contents in Internet. Say less, do more. Now let's start to act. Please install VS2010 and MicroStation V8i(SELECT series 2). After you successfully install these two softwares and try to start them, you will see their start pages shown as below. My VS2010 is installed at C:\VisualStudio\2010 and my MstnV8iSS2 is installed at F:\Bentley\0811\MicroStation. I would like to complement a note, you can develop Addins under Mstn V8iSS2 using VS2005, VS2008 or VS2010. Express, Professional or Enterprise versions are all ok. Learning MicroStation Addins Step by Step[1] Chapter 1. Creating a simplest Addin application This chapter will lead you to create a simplest 'Hello World' Addin application from scratch. This application can be loaded and executed in Mstn. 1. Start VS2010. 2. The below form will display by selecting the menu File > New > Project or directly pressing the shortcut keys Ctrl+Shift+N in VS2010. In this form, find and click Visual C# in the left pane and clickClass Library in the right pane. Then, select a project location and input a name of project. Before you click the OK button, remember to change the .NET Framework from default 4.0 to 3.5 ! This is very important because Mstn V8iSS2 can only support up to .NET Framework 3.5. (Actually .NET 3.5 is based on .NET 2.0. We can verify this if you are interested in it.)

Upload: nguyen-sen

Post on 17-Dec-2015

422 views

Category:

Documents


47 download

DESCRIPTION

Addin MicroStation C

TRANSCRIPT

  • Learning MicroStation Addins Step by Step[0]

    A series of posts in this blog will help you to learn the MicroStation Addins step by step, from

    elementary topics to advanced. According to the steps provided in these blogs, I believe you can

    quickly master this new approach of developing MicroStation application. I wrote these blogs in

    bilingual edition, Chinese and English, for the convenience of developers in China and other

    territories. For Chinese, you can read the text in blue; For English, you can read the text in black.

    Limited to my poor English level, I can't ensure the correctness of English edition. Just for your

    information.

    Chapter 0. Introduction and Prerequisite

    As of V8XM(08.09.xx.xx) a new development approach is introduced into MicroStation, we called

    this approach Addins. Addins is based on .NET Framework, thus you can use C#, C++/CLI or

    VB.NET language to develop Addins application. Compared to MVBA(MicroStation Visual Basic for

    Application), Addins can support command table and can be compiled into DLL. Compared to

    MDL(MicroStation Development Language/Library), you can use WinForm to design user interface

    in Addins and you can never to touch the .r resource which is tough for novice.

    To learn Addins programming, You'd better know and be able to use Bentley MicroStation(shorten as

    Mstn from now on) and Microsoft Visual Studio(shorten as VS from now on), plus have a basic

    knowledge of C# computer language. These three parts are not included in this series of posts, you

    have to find them by yourself. But don't be worry, there are a lot of Mstn related contents in Be

    Communities (you are accessing it) and there are plenty of VS and C# related contents in Internet.

    Say less, do more. Now let's start to act. Please install VS2010 and MicroStation V8i(SELECT series

    2). After you successfully install these two softwares and try to start them, you will see their start

    pages shown as below. My VS2010 is installed at C:\VisualStudio\2010 and my MstnV8iSS2 is

    installed at F:\Bentley\0811\MicroStation. I would like to complement a note, you can develop

    Addins under Mstn V8iSS2 using VS2005, VS2008 or VS2010. Express, Professional or Enterprise

    versions are all ok.

    Learning MicroStation Addins Step by Step[1]

    Chapter 1. Creating a simplest Addin application

    This chapter will lead you to create a simplest 'Hello World' Addin application from scratch. This

    application can be loaded and executed in Mstn.

    1. Start VS2010.

    2. The below form will display by selecting the menu File > New > Project or directly pressing the

    shortcut keys Ctrl+Shift+N in VS2010. In this form, find and click Visual C# in the left pane and

    clickClass Library in the right pane. Then, select a project location and input a name of project.

    Before you click the OK button, remember to change the .NET Framework from default 4.0 to 3.5 !

    This is very important because Mstn V8iSS2 can only support up to .NET Framework 3.5. (Actually

    .NET 3.5 is based on .NET 2.0. We can verify this if you are interested in it.)

  • Now, click the OK button will bring the below interface. The C# code is generated at left pane and

    theSolution Explorer is in right pane.

    3. Right-click the name of project csAddins in Solution Explorer and select the Properties in the pop-

    up menu to open the Project Properties form. Under the Build page, click the Browse... button after

    theOutput path textbox to select mdlapps folder under your Mstn. For my case, this path is finally

    set to F:\Bentley\0811\MicroStation\mdlapps\. (If your Addins DLL is generated in your specified

    folder not in this mdlapps folder, you should set configuration variable MS_ADDINPATH to your

    folder in order to load your Addin correctly from Mstn.)

  • 4. Modify the reference in this project. In this step we will add four references: one is .NET library,

    three are Mstn managed libraries. We can also delete four unnecessary references generated by

    wizard.

    4.1. Right-click the References item in Solution Explorer and then select the Add Reference... item

    in the pop-up menu. In the Add Reference form, select .NET page, find and select

    theSystem.Windows.Forms line and then click OK button to add this reference. The class

    MessageBox we will use in our code needs this library.

    4.2. Select Browse page in the above form, browse and select the following three dynamic libraries to

    add them.

    \MicroStation\ustation.dll

    \MicroStation\assemblies\bentley.microstation.interfaces.1.0.dll

    \MicroStation\assemblies\ECFramework\bentley.general.1.0.dll

  • 4.3. Delete four unnecessary references which we don't need them now. Final reference list is shown

    as below:

    5. This step is optional, but you 'd better do this. Right-click Class1.cs in Solution Explorer and then

    select Rename from the pop-up menu. Change the Class1.cs to MyAddin.cs. When you face the

    below prompt, please click the Yes button to continue.

    6. Modify MyAddin.cs source code to the below snippet:

    using System;

    using System.Windows.Forms;

  • namespace csAddins

    {

    internal sealed class MyAddin : Bentley.MicroStation.AddIn

    {

    private MyAddin(System.IntPtr mdlDesc) : base(mdlDesc)

    {

    }

    protected override int Run(string[] commandLine)

    {

    MessageBox.Show("Hello World");

    return 0;

    }

    }

    }

    If a .NET assembly can be executed as an Addin under Mstn, it must meet three conditions: a)

    Includes one class derived from Bentley.MicroStation.Addin; b) This derived class must supply a

    single argument constructor that take an MDL descriptor as an int; That constructor must chain to the

    base class constructor and pass the argument along; c) This derived class must override the virtual

    Run() method of the base Addin class. The above definition of MyAddin just meets these three

    conditions.

    7. Build this solution by selecting the Build > Build Solution menu in VS. When you see the below

    message in output window of VS, it denotes that you have created an Mstn Addin successfully. If you

    unluckily see 0 succeeded, x failed message, you have to check the above steps carefully. Checking

    the errors description can help you to locate the place of error.

    ========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

    8. Now lets verify our Addin program. Please start Mstn. Type MDL LOAD csAddins in the key-in

    field and then press Enter. You will see a "Hello World" message box displayed. By far, we have

    introduced to you all the steps about how to create a simplest Addin. Note: You can open the Key-in

    dialog box by selecting Mstns menu Utilities > Key-in.

    9. If you want, you can continue to do a more step. In this step, we will verify that .NET 3.5 is based

    on .NET 2.0 (.NET 4.0 is not based on .NET 2.0). First, exit Mstn to unload csAddins application

    because we cant unload an Addin using MDL UNLOAD command. We will discuss the approach

    about how to unload an Addin without exiting Mstn in late chapter; Second, change the code of Run()

    method of MyAddins.cs file as the below snippet. Then, build the solution in VS (refer to step 7);

    Finally, restart Mstn and load csAddins(refer to step 8). You will see the message box shown as the

    following, in which, the installed path of .NET Framework is shown. We can easily find .NET 3.5 is

    based on .NET 2.0 from the v2.0.50727 character string.

  • string sWinFrameworkPath =

    System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();

    MessageBox.Show("Framework Path =" + sWinFrameworkPath);

    return 0;

    Learning MicroStation Addins Step by Step[3]

    Chapter 3. Running and debugging Addins

    An Addin application is an assembly based on .NET Framework and it will be loaded into an

    application domain to run. After you keyin a default (here, default means without any extra

    parameter except application filename) MDL LOAD command to load an Addin, this addin will be

    loaded into the default AppDomain of Mstn , this domain name is DefaultDomain. Maybe as you

    know, we cant unload an assembly but we do can unload an AppDomain. So Mstn provides an

    undocumented CLR (CLR means Common Language Runtime) command, it includes an unload

    appdomain command. Because all assemblies of Mstn are loaded into DefaultDomain, when we load

    our own assembly into non-default domain, it sometimes arises unexpected result. This is why this

    CLR command undocumented. But for most of Addin applications, this unload command can work

    correctly. Being able to unload an Addin can help you debug your assembly dramatically especially

    for the poor performance machines because entering and exiting Mstn repeatedly will waste more

    time of developers. The following step-by-step operations will illuminate what we have said.

    1. Start Mstn, keyin MDL LOAD csAddins in keyin field to load our assembly. Then keyining CLR

    DIALOGwill lead the .NET AppDomain Manager dialogbox. Click the DefaultDomain, you will

    see the below form as shown. Please note, your csAddins assembly is just in this application domain.

  • 2. csAddins assembly is unable to unload once it is loaded because you cant unload DefaultDomain.

    At this time, when you keyin MDL LOAD csAddins again, you will see CSADDINS is already

    loaded in the status bar at the left-bottom corner of Mstn window.

    3. Exit Mstn and restart it again, keyin MDL LOAD csAddins,,MyDomain to load your assembly

    into MyDomain application domain. Then keyin CLR DIALOG, you will see the below form and

    you can find csAddins is in MyDomain application domain not in DefaultDomain.

  • 4. Keyin CLR UNLOAD DOMAIN MyDomain to unload our assembly, delete all graphics in

    active model. Then keyin MDL LOAD csAddins,,MyDomain (Actually, you can double click the

    keyin you entered before to execute the command. If your command keyin window is so small that

    you cant see the keyin you entered before, then you can use the up-arrow key in your keyboard to

    scroll out the previous keyins), you can see the familiar graphics in active model again. This denotes

    that we can run our assembly time after time without have to exit Mstn everytime.

    Now that you have learned how to run Addins, lets begin the introduction of debugging Addins. We

    intend to introduce two approaches of debug, they use default AppDomain and custom AppDomain

    separately. Certainly, the first approach is safe and reliable for all assemblies but it needs you restart

    Mstn repeatedly. The second approach is sometimes unavailable for some assemblies but it doesnt

    need you restart Mstn over and over. If you have a power machine, I recommend you to use the first

    approach, or you can try the second. The following steps will describe the first approach using

    default application domain to debug you assembly.

    1. Open your project in VS. Point Start external program to the ustation.exe of Mstn V8iSS2. You

    can find this item in Debug category of your project property window. You can also keyin an valid

    DGN filename in the Command line argument field, so you can enter Mstn without the display of

    MicroStation Manager window. Please note, this step is belong to the configuration of your VS

    project, so it is just need to do once. The below is the setting in my box. You may change the path of

    ustation.exe according to your case.

  • 2. Find the line CreateElement.LineAndLineString() in the MyAddins.cs file, click the leftmost

    column of this line to set it as a breakpoint. There will be an orange-red ball at the leftmost column of

    this line and the this line code will be highlighted. See the below picture:

    3. Clicking the Run button (the green arrow button in the below picture) in the debug toolbar of VS

    will start Mstn. Keyin MDL LOAD csAddins in the Mstns keyin field and press Enter key, you

    assembly will run to breakpoint and suspend.

    4. There are a lot of tools in the debug toolbar, such as the Step Into tool shown in the below red

    frame and Step Over tool shown in the below blue frame. If you cant find this toolbar, please select

    the menuView > Toolbars > Debug in VS. Now lets click the Step Into tool to enter the

    methodLineAndLineString of CreateElement.cs.

    5. When you move your mouse over a variable, the value of this variable will display in a pop-up

    window, shown as below.

  • 6. Click the Stop Debugging tool in debug toolbar will stop debug process, the Mstn session started

    by VS will be also closed. There are a plenty of commands and approaches to debug your application

    in VS, you can find materials related to this to learn deeply, no more tautology here.

    Next we will introduce another approach to debug your application. This approach doesnt need

    restarting Mstn repeatedly and doesnt need to configure the Debug page of project property at first.

    Please operate following the below steps:

    1. Start Mstn and this Mstn session will be used by you repeatedly.

    2. Set breakpoint at the line CreateElement.LineAndLineString() in

    the MyAddins.cs file. See the step 2 of the first approach for its detail.

    3. Select VS menu Debug > Attach to Process, find and select ustation.exe in the pop-up

    processes window (Tip: Click the title of Process column to order the processes

    descendingly. Thus, ustation.exe will arise to the top of list and you can find it easily).

    Then click the Attach button.

  • Note: If you are using VS2005, please note the Attach to option. If it isnt Managed code, you should

    click the Select button to select Managed code for Addins debugging. VS2010 seems always set the

    Automatic mode correctly.

    4. Switch to Mstn, keyin MDL LOAD csAddins,,MyDomain and press Enter, you will enter the

    debug state. The methods of debugging is same as those we have described at the first approach.

    Please refer to the step 4 and 5 of the first approach.

    5. When you press Stop button in debug toolbar, csAddins assembly stop running but Mstn session

    will remain. If you want to do the next debug, keyin CLR UNLOAD DOMAIN MyDomain first and

    repeat the step 3 to step 5 of this approach.

    Learning MicroStation Addins Step by Step[4]

    Chapter 4. Adding commands to Addins

    If you have learnt MDL(MicroStation Development Language/Library) and MVBA(MicroStation

    Visual Basic for Application) before you learn Addins, maybe you have noticed that we can use keyin

    command in MDL but cant in MVBA. Although Addins look like MVBA because both of them use

    the same COM library, Addins are actually loaded after they are converted to a MDL application. You

    can find mdlseed.ma and mdlseed.dll under the folder \MicroStation\mdlsys\asneeded and these

    files are designed for this aim. So Addins are similar to MDL and they support keyin command. With

    the prevalence of XML, Addins are using XML as the format of keyin command table, not as the

    Table format in MDL resource files. Introduction of the XML format of command table is the main

    topic in this chapter. Embedding command table into Addin assembly and writing keyin handlers are

    involved as well. Further, Using UstnXOM to verify your Addins is the last topic in this chapter.

    Lets start to look at a XML command table which comes from

    \MDLProjects\Dotnet\Examples\TextMod\TextMod.Commands.XML in Mstn SDK. The topmost

    node in a XML command table is KeyinTree and three parts RootKeyinTable, SubKeyinTables and

    KeyinHandlers are included in KeyinTree. These three parts denote the root keyin table, sub keyin

    tables and the process function (i.e., handler) of every keyins separately.

  • There is an attribute ID, which denotes the name of the table, in and

    . The contents of table consist of one or more elements and the attribute

    SubtableRef of point to the ID of sub table. Thus, the command tree can be constructed

    by using SubtableRef of in current table and the ID of in sub table. You

    can define up to four levels command table in section because every Mstn

    command can be constructed by one to five words. must have the attribute

    CommandWord which is the keyin word you entered in Mstn keyin field.The attribute

    CommandClass of denotes the command category of this keyin command and it can be

    Placement, Viewing, Fence, Parameters, Locks, MacroCommand, Manipulation etc. It can also be

    Inherit, means the command class of this is same as its parents. An item is

    also included in and it describes more. The attributes of can be

    Required, Default, TryParse, Hidden etc. Required="true" means this command word is not the leaf

    node, it must have child node. Default="true" denotes this command word is default. When you enter

    a command keyin without this word, the final keyin will append this word. In one level of table, there

    is only one can be default. For example, based on the above XML command table, when

    you enter TextMod Case and press Enter TextMode Case Upper is sent to Mstn and the keyin

    handler TextMod.KeyinCommands.ChangeToUpper will be called. TryParse="true" means you can

    enter some extra characters or strings after this command word and these extra words are passed into

    the argment "unparsed" in keyin handler. For example, you can

    enter Red, Green or Blue after Active Colorcommand and you can also keyin a number after this

    command, such as Active Color 245. It is impossible to list all keyins in this case, so we can use

    attribute TryParse. Hidden="true" means this command is hidden, i.e., user is unable to see this

    command in Mstn command browser but it is available. These hidden commands are often used by

    code not for users keyin or have some other reasons.

  • There is only one kind of element under the node. The attribute

    Keyin of denotes the full keyin command string. The attribute Function of

    denotes the handler name of this keyin command and the handler name is

    constructed by namespace name, class name and function name (i.e., method name). In other words,

    when user keyins the command string contained in attribute Keyin, the method in attribute Function

    will be called.

    Now, lets modify csAddins step by step and make it support keyin command.

    1. Open your csAddins project in VS, select menu Project > Add New Item (certainly, you can do it

    by right-clicking the project name and select Add New Item from the popup menu like before). Find

    and select the XML File item in opened form, enter command.xml in the Name: field, then

    click Add button to add an empty XML file to current project. Shown as below:

    2. Copy and paste the following lines of command table to your command.xml file.

  • 3. Right-click command.xml file in Solution Explorer and select Properties menu to open Properties

    form. Set the Build Action to Embeded Resource. Thus, command.xml file will be embedded into

    csAddins.dll file. Refer to the below picture:

  • 4. Modify MyAddins.cs in two places: 4a) Add AddInAttribute attribute definition before the line

    "class MyAddin". 4b) Comment out or delete all the other lines of function Run except the last line.

    Finished class should be looked like this:

    [Bentley.MicroStation.AddInAttribute

    (KeyinTree = "csAddins.commands.xml", MdlTaskID = "CSADDINS")]

    internal sealed class MyAddin : Bentley.MicroStation.AddIn

    {

    private MyAddin(System.IntPtr mdlDesc)

    : base(mdlDesc)

    {

    }

    protected override int Run(string[ ] commandLine)

    {

    return 0;

    }

    }

    5. Modify the declaration of every function in CreateElement.cs, make them have a string argument.

    For example:

    public static void LineAndLineString(string unparsed)

    {

    }

    public static void ShapeAndComplexShape(string unparsed)

    {

    }

    6. Build your project in VS, then switch to Mstn and load your csAddins assembly (Refer to the above

    chapters for detailed steps). After you load your assembly, you cant see any elements drawn because

    we have deleted all function calls in function Run of MyAddins.cs.

    7. Open keyin dialogbox by selecting Mstn main menu Utilities > Key-in. When you

    enter csaddins you will see a command keyin tree shown as below and this tree corresponds to the

    definition in commands.xml file. At this time, when you enter a complete command and press Enter,

    you will see a part of drawings displayed in the view. For example, entering csaddins createelement

    coneandbsplinesurface will bring the below two 3D elements drawn in the view.

  • Now that you have learnt how to add commands to an Addin application, I would like to introduce a

    new tool related to this topic. Due to compiler csc.exe is unable to verify the correctness of XML

    command file format, Bentley provide a specific tool called UstnXOM.exe which can check the

    closure of an Addin and the correctness of the definition of command table. If you have installed Mstn

    SDK, you will find this tool under the folder \MicroStation\mdl\bin. Or you can get it by the

    following step 1. (Note: You can download Mstn SDK from Bentley SELECTService website

    http://selectservice.bentley.com. Logining into this website need you have an authorized Bentley

    SELECT account). Lets start to learn how to use UstnXOM tool step by step.

    1.Download UstnXOM.zip file and uncompress it to folder \MicroStation\Assemblies.

    UstnXOM.zip

    2.Add a new environment variable MS to point your Mstn installation folder in

    Windows Environment Variables dialogbox. For me, MS point to F:\Bentley\0811\MicroStation\.

    Append F:\Bentley\0811\MicroStation\Assemblies\ to your PATH environment variable. All these

    settings are shown as below. Please note, dont use a path with any space characters. If your path has

    any space, you can replace it with its short 8.3 format (Remark: You can enter a DOS command

    DIR/X to find the short path corresponded to a long with-space path. For example, "C:\Program Files"

    can be replaced by "C:\Progra~1".).

  • 3.Open a command prompt window from Windows (this black background window is a little ugly but

    very useful), change directory to your \MicroStation\mdlapps folder. Keyining UstnXOM

    ValidateAddIn csAddins.dll can bring the below picture. The contents in red frame are my keyins.

    You can see the prompt "No errors identified" which means your csAddins structure and the its

    command table are all fine.

    4. UstnXOM has some other functions, such as examining the content of the KeyinTree.xsd file. In

    this case you can first enter UstnXOM DumpSchemaList to get the name of KeyinTree, it is

    KeyinTree.xsd.Deflate. Then enter UstnXOM DumpSchema KeyinTree.xsd.Deflate to display the

    complete KeyinTree definition file shown as below.

  • 5. You can also add this verification program to your VS project configuration then this

    verification program will be executed whenever you build your application. Do this: open the

    attribute form of your csAddins project, enter $(MS)\assemblies\UstnXOM ValidateAddIn

    $(TargetPath) in Post-build event command line of Build Events tab.

    After this, whenever you build your project you will see the following information from the

    output window of VS.

  • Learning MicroStation Addins Step by Step[5]

    Chapter 5. Adding WinForms to Addins

    We will introduce how to use modal, modeless, toolsettings and dockable dialog boxes in

    Mstn. Modal, modeless and dockable dialog boxes are common in any other applications and

    you can find a lot of information about them from Internet. Toolsettings dialog box is specific

    to Mstn. It is a modeless dialog box and its content changes along with different tools. We can

    visually design the UI of dialog box because all the dialog boxes in Addins are derived

    from WinForm. This can improve your coding efficiency dramatically. The programmers who

    have coded a mass of dull .r resources will enjoy the UI programming using Addins. Now

    lets extend our csAddins application step by step.

    1.Open the csAddins solution in VS, click the menu Project > Add Windows Form.

    InputModalForm.cs in the Name field of the Add New Items form and click the Add button to

    create a new WinForm. Add New Item form is shown as below picture.

  • 2. Next we will open out the power of visualization. Clicking Toolbox icon button (or pressing

    Ctrl+Alt+X when Form1 is focused) will open Toolbox window which includes a lot of controls we

    can use. You can simply drag a control and drop it to Form1, then adjust its position.

    Clicking Properties Window icon button (or pressing Alt+Enter when Form1 is focused) will open

    properties window which includes a lot properties need us to set. Please see the below picture for

    these two important windows.

    3. The finished ModalForm is shown as below. We list all the properties need to set. We set the

    Modifiers property of textBox1 to Public in order to access it from its parent class DemoForm. Please

    note, you neednt change the Location and Size property by typing, they can be changed by visually

    dragging in VS.

    Form (Name) = ModalForm / AcceptButton = btnOk / ControlBox = False /

    FormBorderStyle = FixedDialog

    / Size = 288,145 / Text = ModalForm

    Label (Name) = label1 / Location = 22,24 / Size = 82,17 / TabIndex = 0 / Text = Your Value:

    TextBox (Name) = textBox1 / Location = 111,24 / Modifiers = Public / Size = 149,22 /

    TabIndex = 1

    Button (Name) = btnOk / DialogResult = OK / Location = 41,71 / Size = 75,26 / TabIndex =

    2 / Text = OK

  • Button (Name) = btnCancel / DialogResult = Cancel / Location = 157,71 / Size = 75,26 /

    TabIndex = 3 / Text = Cancel

    4. Now we will create a modeless dialog box. Insert a WinForm named MultiScaleCopyForm into

    your csAddins project. Why we name this form to MultiScaleCopyForm? The reason is we will use it

    as a toolsettings dialog box of a command in the next chapter. Drag needed controls to this form from

    VSs toolbox window then set the properties of them as below.

    Form (Name) = MultiScaleCopyForm / FormBorderStyle = FixedDialog / MaximizeBox =

    False / MinimumBox = False

    / Size = 199,225 / Text = MultiScaleCopyForm

    Label (Name) = label1 / Location = 13,17 / Size = 47,17 / TabIndex = 0 / Text = Scale:

    TextBox (Name) = txtScale / Location = 81,12 / Size = 100,22 / TabIndex = 1 / Tag = 0.95 /

    Text = 0.95

    Label (Name) = label2 / Location = 13,45 / Size = 63,17 / TabIndex = 2 / Text = X Offset:

    TextBox (Name) = txtXOffset / Location = 81,40 / Size = 100,22 / TabIndex = 3 / Tag = 4 /

    Text = 4

    Label (Name) = label3 / Location = 13,73 / Size = 63,17 / TabIndex = 4 / Text = Y Offset:

    TextBox (Name) = txtYOffset / Location = 81,68 / Size = 100,22 / TabIndex = 5 / Tag = 0 /

    Text = 0

    Label (Name) = label4 / Location = 13,101 / Size = 63,17 / TabIndex = 6 / Text = Z Offset:

    TextBox (Name) = txtZOffset / Location = 81,96 / Size = 100,22 / TabIndex = 7 / Tag = 0 /

    Text = 0

    Label (Name) = label5 / Location = 13,129 / Size = 55,17 / TabIndex = 8 / Text = Copies:

    TextBox (Name) = txtCopies / Location = 81,124 / Size = 100,22 / TabIndex = 9 / Tag = 10 /

    Text = 10

    Button (Name) = btnDefault / Location = 40,157 / Size = 113,25 / TabIndex = 10 / Text =

    Load Default

    5. Add event handlers for form and controls.

    a) Select MultiScaleCopyForm itself in its design window of VS (You can open form design window

    by right-clicking MultiScaleCopyForm.cs in VS solution explorer and select View Designer menu). If

    the properties window of VS isnt opened please open it according the step 2 of this chapter. In the

    properties window, switch to the tab Events (shown as the red frame of below picture). Find

    FormClosed and Load events in events list and double click them to create two event handler methods

    MultiScaleCopyForm_FormClosed and MultiScaleCopyForm_Load.

    b) Switch back to the design window again and select txtScale control. In the tab Events of properties

    window, find and double click event KeyPress to add txtScale_KeyPress event handler method.

    c) Similarly, add txtXOffset_KeyPress and txtCopies_KeyPress event handler methods.

  • d) Because txtYOffset and txtZOffset is similar to txtXOffset, we can share one event handler

    txtXOffset_KeyPress for these three controls. Select txtYOffset control in design window and find

    KeyPress event in tab Events of properties window. Dont double click the KeyPress event, or it will

    create a new event handler method txtYOffset_KeyPress. Instead of double clicking, you should

    select the txtXOffset_KeyPress from the drop-down list. Process the txtZOffset same as txtYOffset.

    e) Double click Load Default button to add btnDefault_Click event handler method.

    6. Open the source code of MultiScaleCopyForm by right-clicking MultiScaleCopyForm.cs file in VS

    solution explorer and selecting View Code menu from the popup menu, then modify this code as the

    below. Please note the several keypoint:

    a) To access Windows register, we need to use using Microsoft.Win32;

    b) In order to make a form on top of all other windows in Mstn, you should derive this form from a

    Bentley defined base class ----Bentley.MicroStation.WinForms.Adapter.

    c) The three KeyPress methods are used to validate the input data of Textbox controls.

    d) When the user clicks the Load Default button, btnDefault_Click method copy the Tag property

    value to the Text property for every TextBox control.

    e) Whenever the form is closed, MultiScaleCopyForm_FormClosed method is called and it writes

    current 5 values of TextBox controls into Windows register. When this form is opened next time,

    MultiScaleCopyForm_Load method is called and it reads these values from Windows register and

    populates them to TextBox controls.

  • using System;

    using System.Windows.Forms;

    using Microsoft.Win32;

    using Bentley.MicroStation.WinForms;

    namespace csAddins

    {

    public partial class MultiScaleCopyForm : //Form

    Adapter

    {

    public MultiScaleCopyForm()

    {

    InitializeComponent();

    }

    private void txtScale_KeyPress(object sender, KeyPressEventArgs e)

    {

    if (!Char.IsDigit(e.KeyChar) && e.KeyChar != '\b' && e.KeyChar != '.')

    e.Handled = true;

    }

    private void txtXOffset_KeyPress(object sender, KeyPressEventArgs e)

    {

    if (!Char.IsDigit(e.KeyChar) && e.KeyChar != '\b' && e.KeyChar != '.' && e.KeyChar != '-')

    e.Handled = true;

    }

    private void txtCopies_KeyPress(object sender, KeyPressEventArgs e)

    {

    if (!Char.IsDigit(e.KeyChar) && e.KeyChar != '\b')

    e.Handled = true;

    }

    private void btnDefault_Click(object sender, EventArgs e)

    {

    txtScale.Text = txtScale.Tag.ToString();

    txtXOffset.Text = txtXOffset.Tag.ToString();

    txtYOffset.Text = txtYOffset.Tag.ToString();

  • txtZOffset.Text = txtZOffset.Tag.ToString();

    txtCopies.Text = txtCopies.Tag.ToString();

    }

    private void MultiScaleCopyForm_Load(object sender, EventArgs e)

    {

    RegistryKey myKey =

    Registry.CurrentUser.OpenSubKey("Software\\csAddins\\MultiScaleCopy");

    if (null != myKey)

    {

    txtScale.Text = myKey.GetValue("txtScale").ToString();

    txtXOffset.Text = myKey.GetValue("txtXOffset").ToString();

    txtYOffset.Text = myKey.GetValue("txtYOffset").ToString();

    txtZOffset.Text = myKey.GetValue("txtZOffset").ToString();

    txtCopies.Text = myKey.GetValue("txtCopies").ToString();

    }

    }

    private void MultiScaleCopyForm_FormClosed(object sender, FormClosedEventArgs e)

    {

    RegistryKey rootKey = Registry.CurrentUser.OpenSubKey("Software", true);

    RegistryKey appKey = rootKey.CreateSubKey("csAddins");

    RegistryKey myKey = appKey.CreateSubKey ("MultiScaleCopy");

    myKey.SetValue("txtScale", txtScale.Text.ToString());

    myKey.SetValue("txtXOffset", txtXOffset.Text.ToString());

    myKey.SetValue("txtYOffset", txtYOffset.Text.ToString());

    myKey.SetValue("txtZOffset", txtZOffset.Text.ToString());

    myKey.SetValue("txtCopies", txtCopies.Text.ToString());

    }

    }

    }

    Warn: After you changed your forms base class from Form to Adapter, you cant modify

    your WinFrom visually. In this case, when you switch to the design window and try to edit

    your WinForm you will encounter the below error message. To overcome this issue, you have

    to firstly change the base class from Adapter to Form, then modify your form visually, finally

    change base class back to Adapter when you finished your modification.

    7. Similarly, we create NoteCoordForm as below. All the controls in this form take the standard

    actions, in other words, you dont need to add any event handler to them. The only thing you should

    do is to change your WinForm base class from Form to Adapter. We will use this form as a

  • toolsettings dialog later. There are four radio buttons in this form; they are rdoHoriz, rdoVert, rdoEN

    and rdoXY. The Checked property of rdoHoriz and rdoEN are True. rdoHoriz and rdoVert are in one

    group, rdoEN and rdoXY are in the other group.

    Form (Name) = NoteCoordForm / FormBorderStyle = FixedDialog / MaximizeBox = False /

    MinimumBox = False

    / ShowIcon = False / Size = 288,186 / Text =

    NoteCoordForm

    GroupBox (Name) = grpTxtDir / Location = 13,13 / Size = 257,58 / TabIndex = 0 / Text =

    Text Direction

    RadioButton (Name) = rdoHoriz / Checked = True / Location = 52,27 / Size = 93,21 /

    TabIndex = 0

    / TabStop = True / Text = Horizontal

    RadioButton (Name) = rdoVert / Location = 168,27 / Size = 76,21 / TabIndex = 1 / Text =

    Vertical

    GroupBox (Name) = grpLabel / Location = 15,85 / Size = 254,57 / TabIndex = 1 / Text =

    Label

    RadioButton (Name) = rdoEN / Checked = True / Location = 52,25 / Size = 56,21 / TabIndex

    = 0

    / TabStop = True / Text = EN=

    RadioButton (Name) = rdoXY / Location = 168,24 / Size = 55,21 / TabIndex = 1 / Text =

    XY=

    8. Prepare three picture files to denote modal, modeless and toolsettings dialog box respectively. Then

    open property window of your project by right-clicking your project name csAddins and selecting

    theProperties item from the popup menu. Switch to the Resources page. When you ensure you have

    selected the Images resource type, please click the Add Resource button to add picture resource. We

    show this as below.

  • 9. Create a new form named ToolbarForm. Add three button controls and a ToolTip control. Assign

    the above three pictures to these buttons. The finished form shows as below.

    Form (Name) = ToolbaForm / FormBorderStyle = FixedToolWindow / MaximizeBox = False

    / MinimumBox = False

    / ShowIcon = False / ShowInTaskbar = False / Size = 124,62 /

    Text = Demo Toolbar

    Button (Name) = btnModal / Image = csAddins.Properties.Resources.modal / Location =

    5,3 / Size = 32,32 / TabIndex = 0

    / ToolTip on toolTip1 = Demo Modal DialogBox

    Button (Name) = btnTopLevel / Image = csAddins.Properties.Resources.toplevel / Location =

    43,3 / Size = 32,32

    / TabIndex = 1 / ToolTip on toolTip1 = Demo TopLevel

    DialogBox

    Button (Name) = btnToolSettings / Image = csAddins.Properties.Resources.tool / Location =

    81,3 / Size = 32,32

    / TabIndex = 2 / ToolTip on toolTip1 = Demo ToolSettings

    DialogBox

    10. Double click these three buttons to construct their event handler methods. Then modify

    ToolbarForm.cs source code as below. Please note two keypoints:

    a) ToolbarForm derives from the base class Adapter and the interface IGuiDockable because we will

    make this form dockable. The further actions to take are to implement two methods ----

    GetDockedExtent and WindowMoving.

    b) When the user clicks the buttons, they will send command to Mstn. The implementation of

    command handlers are shown at the next step.

    using System;

    using System.Windows.Forms;

  • using Bentley.MicroStation.InteropServices;

    using Bentley.MicroStation.WinForms;

    namespace csAddins

    {

    public partial class ToolbarForm : //Form

    Adapter, IGuiDockable

    {

    private Bentley.Interop.MicroStationDGN.Application app = null;

    public ToolbarForm()

    {

    InitializeComponent();

    app = Utilities.ComApp;

    }

    // The below two methods come from IGuiDockable

    public bool GetDockedExtent(GuiDockPosition dockPosition, ref GuiDockExtent extentFlag, ref

    System.Drawing.Size dockSize)

    {

    return false;

    }

    public bool WindowMoving(WindowMovingCorner corner, ref System.Drawing.Size newSize)

    {

    newSize = new System.Drawing.Size(118, 34);

    return true;

    }

    private void btnModal_Click(object sender, EventArgs e)

    {

    app.CadInputQueue.SendKeyin("csAddins DemoForm Modal");

    }

    private void btnTopLevel_Click(object sender, EventArgs e)

    {

    app.CadInputQueue.SendKeyin("csAddins DemoForm TopLevel");

    }

    private void btnToolSettings_Click(object sender, EventArgs e)

    {

    app.CadInputQueue.SendKeyin("csAddins DemoForm ToolSettings");

  • }

    }

    }

    11. Create a new class DemoForm which file is DemoForm.cs. In this class we will process several

    keyin commands and open different types of dialog boxes. For this, we call the methods

    AttachAsGuiDockable, AttachAsTopLevelForm and AttachToToolSettings of Adapter.

    using System;

    using System.Windows.Forms;

    namespace csAddins

    {

    class DemoForm

    {

    public static void Toolbar(string unparsed)

    {

    ToolbarForm myForm = new ToolbarForm();

    myForm.AttachAsGuiDockable(MyAddin.s_addin, "toolbar");

    myForm.Show();

    }

    public static void Modal(string unparsed)

    {

    ModalForm myForm = new ModalForm();

    if (DialogResult.OK == myForm.ShowDialog())

    MessageBox.Show(myForm.textBox1.Text.ToString());

    }

    public static void TopLevel(string unparsed)

    {

    MultiScaleCopyForm myForm = new MultiScaleCopyForm();

    myForm.AttachAsTopLevelForm(MyAddin.s_addin, false);

    myForm.Show();

    }

    public static void ToolSettings(string unparsed)

    {

    NoteCoordForm myForm = new NoteCoordForm();

    myForm.AttachToToolSettings(MyAddin.s_addin);

    myForm.Show();

    }

  • }

    }

    12. Modify existing commands.xml file, Add four new commands csAddins DemoForm

    Toolbar|Modal|TopLevel|ToolSettings. The new added lines are shown in the below code in brown

    text.

  • 13. Rebuild your project in VS. If you can build successfully at the first time, you are so luck. After

    you finally generated csAddins.dll, you can load it in Mstn and keyin csAddins DemoForm

    Toolbar in the Keyin field. This time you will see a toolbar window opened as the step 9 shown.

    Clicking each button will open modal, modeless and toolsettings dialog box and this toolbar window

    can be dockable.

    Till now, the source code of our project is as below. You can download it and modify the project

    properties according to your software installation location and build the project to create an

    executable csAddins.dll.

    http://communities.bentley.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-

    WeblogFiles/00-00-05-22-97/7536.csAddins_5F00_version01.zip

    Learning MicroStation Addins Step by Step[6]

    Chapter 6. Using IPrimitiveCommandEvents and ILocateCommandEvents to implement

    interactive commands

    You must have used the PLACE LINE and MODIFY ELEMENT command. Actually, we can

    classify Mstn commands as view command and primitive command. Furthermore, primitive

    command can be classified as placement command and modification command. Commands we will

    implement in this chapter are placement command and modification command which need class

    derived from interface IPrimitiveCommandEvents and ILocateCommandEvents separately. The first

    implemented command is placing the coordinate value of user specified point dynamically and the

    second is scaled-copying identified element multi-times. These two commands come from practice

    and have some reference values.

    To simplify our tasks, we will modify existing code based on the code of last chapter and doesnt add

    new commands. Although the original command names are not very proper for our new tasks, the

    irrelevancy of command name is not serious because user can start our functions by clicking icons. If

    you are interesting about this, you can add two new proper commands and write two new command

    handlers. Now lets start to operate step by step and show off the result of our effort.

    1. Make sure you have the below two lines at the beginning of MyAddins.cs, then modify the Run

    method as following. This can bring out the toolbox shown in the step 9 of last chapter as soon as you

    load our assembly csAddins.dll.

    using Bentley.MicroStation.InteropServices;

    using BCOM = Bentley.Interop.MicroStationDGN;

    protected override int Run(string[] commandLine)

    {

    BCOM.Application app = Utilities.ComApp;

  • app.CadInputQueue.SendKeyin("csAddins DemoForm Toolbar");

    return 0

    }

    2. Open file NoteCoordForm.Desinger.cs with code mode shown as below picture. Scroll to the end of

    the code; change some controls modifier from private to public. The finished code should display as

    following. The reason why we make this change is we need access these public member variables out

    of their classes. The value of these member variables denotes the operation result of GUI.

    private System.Windows.Forms.GroupBox grpTxtDir;

    public System.Windows.Forms.RadioButton rdoHoriz;

    public System.Windows.Forms.RadioButton rdoVert;

    private System.Windows.Forms.GroupBox grpLabel;

    public System.Windows.Forms.RadioButton rdoXY;

    public System.Windows.Forms.RadioButton rdoEN;

    3. Similarly, modify file MultiScaleCopyForm.Designer.cs as below.

    public System.Windows.Forms.TextBox txtScale;

    private System.Windows.Forms.Label label1;

    private System.Windows.Forms.Label label2;

    public System.Windows.Forms.TextBox txtXOffset;

    private System.Windows.Forms.Label label3;

    public System.Windows.Forms.TextBox txtYOffset;

    private System.Windows.Forms.Label label4;

    public System.Windows.Forms.TextBox txtZOffset;

    private System.Windows.Forms.Label label5;

    public System.Windows.Forms.TextBox txtCopies;

    private System.Windows.Forms.Button btnDefault;

    4. Now we open DemoForm.cs and do a lot of changes in it. Firstly, ensure the using statements as

    below. Please note, we use using Bentley.Interop.MicroStationDGN; and using BCOM =

    Bentley.Interop.MicroStationDGN; (please refer to the manual of C# language for the detailed usage

    of using keyword). Why we use the second using BCOM=? The reason is the class Application and

    View exist in namespace System.Windows.Forms and Bentley.Interop.MicroStationDGN

    simulaneously. In this scenario, we can abbreviate Bentley.Interop.MicroStationDGN.Application and

    Bentley.Interop.MicroStationDGN.View into BCOM.Applicationand BCOM.View.

    using System;

    using System.Windows.Forms;

    using System.Runtime.InteropServices;

    using Bentley.MicroStation.InteropServices;

  • using Bentley.Interop.MicroStationDGN;

    using BCOM = Bentley.Interop.MicroStationDGN;

    5. To implement the dynamic placement of identified point coordinate, we need to add a new class

    into DemoForm.cs. We name this new class NoteCoordClass which derived from interface

    IPrimitiveCommandEvents. The code of this class is as below. You can copy and paste it into your

    DemoForm.cs or type it by yourself. This class must overwrite 6 methods: Cleanup, DataPoint,

    Dynamics, Keyin, Reset and Start. Please refer to the Mstn VBA help documentation for the detailed

    information of these methods. You can also get the prototype of these methods in Addins by VS

    object brower shown as below picture.

    class NoteCoordClass : IPrimitiveCommandEvents

    {

    private BCOM.Application app = Utilities.ComApp;

    private NoteCoordForm myForm = new NoteCoordForm();

    private Point3d[] m_atPoints = new Point3d[3];

    private int m_nPoints = 0;

    public void Cleanup()

    {

    myForm.DetachFromMicroStation();

    }

    public void DataPoint(ref Point3d Point, BCOM.View View)

    {

  • if (0 == m_nPoints)

    {

    app.CommandState.StartDynamics();

    m_atPoints[0] = Point;

    m_nPoints = 1;

    app.ShowPrompt("Identify note position");

    }

    else

    {

    Dynamics(ref Point, View, MsdDrawingMode.Normal);

    Reset();

    }

    }

    public void Dynamics(ref Point3d Point, BCOM.View View, MsdDrawingMode DrawMode)

    {

    if (1 != m_nPoints)

    return;

    string[] txtStr = new string[2];

    Point3d[] txtPts = new Point3d[2];

    Element[] elems = new Element[3]; m_atPoints[1] = Point;

    txtStr[0] = (myForm.rdoEN.Checked ? "E=" : "X=") + m_atPoints[0].X.ToString("F2");

    txtStr[1] = (myForm.rdoEN.Checked ? "N=" : "Y=") + m_atPoints[0].Y.ToString("F2");

    double txtLen = app.ActiveSettings.TextStyle.Width * Math.Max(txtStr[0].Length,

    txtStr[1].Length);

    double txtLineSpacing = app.ActiveSettings.TextStyle.Height;

    if (myForm.rdoHoriz.Checked)

    {

    m_atPoints[2].X = m_atPoints[1].X + (m_atPoints[0].X > m_atPoints[1].X ? -txtLen :

    txtLen) * 1.2;

    m_atPoints[2].Y = m_atPoints[1].Y;

    txtPts[0].X = (m_atPoints[1].X + m_atPoints[2].X) / 2;

    txtPts[0].Y = m_atPoints[1].Y + txtLineSpacing;

    txtPts[1].X = txtPts[0].X;

    txtPts[1].Y = m_atPoints[1].Y - txtLineSpacing;

    }

    else

    {

    m_atPoints[2].X = m_atPoints[1].X;

  • m_atPoints[2].Y = m_atPoints[1].Y + (m_atPoints[0].Y > m_atPoints[1].Y ? -txtLen :

    txtLen) * 1.2;

    txtPts[0].X = m_atPoints[1].X - txtLineSpacing;

    txtPts[0].Y = (m_atPoints[1].Y + m_atPoints[2].Y) / 2;

    txtPts[1].X = m_atPoints[1].X + txtLineSpacing;

    txtPts[1].Y = txtPts[0].Y;

    }

    elems[0] = app.CreateLineElement1(null, ref m_atPoints);

    elems[0].LineStyle = app.ActiveDesignFile.LineStyles.Find("0");

    Matrix3d rMatrix = app.Matrix3dIdentity();

    for (int i = 1; i < 3; i++)

    {

    elems[i] = app.CreateTextElement1(null, txtStr[i-1], ref txtPts[i-1], ref rMatrix);

    elems[i].AsTextElement().TextStyle.Font =

    app.ActiveDesignFile.Fonts.Find(MsdFontType.MicroStation, "ENGINEERING");

    elems[i].AsTextElement().TextStyle.Justification = MsdTextJustification.CenterCenter;

    if (myForm.rdoVert.Checked)

    elems[i].RotateAboutZ(txtPts[i-1], Math.PI / 2);

    }

    CellElement elemCell = app.CreateCellElement1("NoteCoordCell", ref elems, ref

    m_atPoints[0]);

    elemCell.Redraw(DrawMode);

    if (MsdDrawingMode.Normal == DrawMode)

    app.ActiveModelReference.AddElement(elemCell);

    }

    public void Keyin(string Keyin)

    {

    }

    public void Reset()

    {

    m_nPoints = 0;

    app.CommandState.StartPrimitive(this);

    }

    public void Start()

    {

    myForm.AttachToToolSettings(MyAddin.s_addin);

    myForm.Show();

    app.ShowCommand ("Note Coordinate");

  • app.ShowPrompt("Please identify a point");

    app.CommandState.EnableAccuSnap();

    }

    }

    We load and show myForm into ToolSettings dialog in Start method of NoteCoordClass by calling

    AttachToToolSettings and Show method. When the command ends, we unload myForm in Cleanup

    method of NoteCoordClass by calling DetachFromMicroStation method. This process overcomes the

    shortcomings at the last chapter, in that, after myForm is loaded into ToolSettings it cant be

    dismissed except you close the ToolSettings dialog. Maybe you want to know when the current

    command end (e.g. when the Cleanup method is called). The answer is when the other same-level

    command start. The level of primitive command is higher than view command. The level of

    placement command and modification command in primitive command are the same. If you insert a

    view command during the execution of a primitive command the primitive command doesnt end but

    suspend, after the end of view command you can resume the primitive command by clicking the reset

    button (default is the right button of your mouse).

    6. Next we will implement the multiple scaled-copy command. We need add a new class derived from

    ILocateCommandEvents into DemoForm.cs as below. This class must overwrite 6 methods: Accept,

    Cleanup, Dynamics, LocateFailed, LocateReset and Start.

    class MultiScaleCopyClass : ILocateCommandEvents

    {

    private BCOM.Application app = Utilities.ComApp;

    private MultiScaleCopyForm myForm = new MultiScaleCopyForm();

    public void Accept(Element Elem, ref Point3d Point, BCOM.View View)

    {

    Element newEl;

    Point3d orgPnt;

    double dScale = double.Parse(myForm.txtScale.Text);

    Point3d offsetPnt = app.Point3dFromXYZ(double.Parse(myForm.txtXOffset.Text),

    double.Parse(myForm.txtYOffset.Text),

    double.Parse(myForm.txtZOffset.Text));

    for (int i = 0; i < int.Parse(myForm.txtCopies.Text); i++)

    {

    newEl = app.ActiveModelReference.CopyElement(Elem);

    newEl.Move(ref offsetPnt);

    orgPnt.X = (newEl.Range.Low.X + newEl.Range.High.X) * 0.5;

  • orgPnt.Y = (newEl.Range.Low.Y + newEl.Range.High.Y) * 0.5;

    orgPnt.Z = (newEl.Range.Low.Z + newEl.Range.High.Z) * 0.5;

    newEl.ScaleAll(ref orgPnt, dScale, dScale, dScale);

    newEl.Redraw(MsdDrawingMode.Normal);

    Elem = newEl.Clone();

    }

    }

    public void Cleanup()

    {

    myForm.DetachFromMicroStation();

    }

    public void Dynamics(ref Point3d Point, BCOM.View View, MsdDrawingMode DrawMode)

    {

    }

    public void LocateFailed()

    {

    app.CommandState.StartLocate(this);

    }

    public void LocateFilter(Element Element, ref Point3d Point, ref bool Accepted)

    {

    }

    public void LocateReset()

    {

    }

    public void Start()

    {

    myForm.AttachToToolSettings(MyAddin.s_addin);

    myForm.Show();

    app.ShowCommand("MultiScaleCopy");

    app.ShowPrompt("Please identify an element");

    app.CommandState.EnableAccuSnap();

    }

    }

    7. Finally, lets modify the TopLevel and ToolSettings method of DemoFrom class as below. The

    TopLevel method is used to start multiple scaled-copy command and the ToolSettings method is used

    to start note coordinate command. They call the StartLcate and StartPrimitive method of

  • CommandState object separately. A remark: the command of note coordinate is limited to work in 2D

    model and ignore the annotation scale of text.

    public static void TopLevel(string unparsed)

    {

    Utilities.ComApp.CommandState.StartLocate(new MultiScaleCopyClass());

    }

    public static void ToolSettings(string unparsed)

    {

    BCOM.Application app = Utilities.ComApp;

    if (app.ActiveModelReference.Is3D)

    {

    MessageBox.Show("This tool can only work in 2D model");

    return;

    }

    app.ActiveSettings.AnnotationScaleEnabled = false;

    app.CommandState.StartPrimitive(new NoteCoordClass());

    }

    8. Build your csAddins and test it. You can firstly place a square in a 2D design model, then load

    csAddins and click the third tool in our csAddins toolbox. By selecting a corner of this square you

    will see a note line and two coordinate texts displayed dynamically. Identify another point to place the

    note into design model. The two notes are shown as below picture

    .

    Click the second tool and set the parameters as the following dialog shown. Select this square and

    accept, you will see the below created drawings.

  • Actually, Mstn has provided us the approach to customize GUI without programming. The

    customized GUI can be saved in a file which extension is .DGNLIB. If this DGNLIB file is located

    under the folders pointed by configuration variable MS_DGNLIBLIST or MS_GUIDGNLIBLIST, it

    will be loaded when Mstn starts. The customization work can be done by opening this DGNLIB file

    and selecting the main menu Workspace > Customize. We dont dig into this topic deeply because

    this blog series arent focused on the customization of Mstn. The following pictures are the

    customization graphic interface and the result of my customization.

  • You can download the below attachment and put it under a folder which is contained in your

    MS_DGNLIBLIST. Restart your Mstn then you will see the above effect.

    csAddins.DGNLIB

    Learning MicroStation Addins Step by Step[7]

    We have captured the data and reset button events (certainly we can also capture the keyin event)

    using IPrimitiveCommandEvents and ILocateCommandEvents in the last chapter. Both of them

    are events occurred in Mstn. In fact, there are many events in Mstn we can capture. The following

    table lists the normal events and their event handlers.

    Type of Events Event Handlers

    Reference File Attachments The methods of IAttachmentEvents

    Raster File Attachments The methods of IRasterEvents

    Design File Opened or Closed NewDesignFileEventHandler (append to NewDesignFileEvent)

    SaveAs Command The methods of ISaveAsEvents

    Model Activation The methods of IModelActivateEvents

    Tracking Element Changes The methods of IChangeTrackEvents

    View Update The methods of IViewUpdateEvents

    Level Change The methods of ILevelChangeEvents

    Keyin, Datapoint, Reset The methods of IPrimitiveCommandEvents and ILocateCommandEvents

    Modal Dialog Opened or Closed The methods of IModalDialogEvents

  • In this chapter, we will demonstrate the usage of ILevelChangeEvents and

    NewDesignFileEventHandler by an example. This example will display the levels of active design file

    in a dialogbox. When you add, remove or rename levels in Mstns Level Manager the level

    information in our dialogbox will change automatically. Now lets construct this function step by step.

    1. Create a new WinForm in VS and name it LevelChangedForm. Then add a ListBox control to this

    form. The finished form is shown as below:

    2. Set the properties of this form as following. Please note: we set the Modifiers property of ListBox

    to Public which let us need not to change the source code of LevelChangedForm.Designer.cs directly

    like we do in the last chapter. Setting the Sorted property of ListBox to True makes it displays its

    content in alphabetic order.

    Form (Name) = LevelChangedForm / FormBorderStyle = FixedDialog / MaximizeBox = False /

    MinimumBox = False

    / ShowIcon = False / Size = 288,423 / Text = LevelChangedForm

    ListBox (Name) = listBox1 / Location = 5,13 / Modifiers = Public / Size = 272,372 / Sorted = True

    3. Open LevelChangedForm.cs by 'view code' mode and modify it. The final version of this file

    should look like as below. Some keypoints we need to emphasize here. a). Modify the base class of

    LevelChangedForm from Form to Adapter. b). When this form is opened, LevelChangedForm_load

    method will be called. In this method, we add DGN files levels to ListBox by calling

    listBox1.Items.Add method. c). When this form is closed, LevelChangedForm_FormClosed method is

    called. In this method, we remove two event handlers which we will describe in detail.

    using System;

    using System.Windows.Forms;

    using Bentley.MicroStation.WinForms;

    using Bentley.MicroStation.InteropServices;

    using Bentley.Interop.MicroStationDGN;

    using BCOM = Bentley.Interop.MicroStationDGN;namespace csAddins

    {

  • public partial class LevelChangedForm : Adapter

    {

    private BCOM.Application app = Utilities.ComApp;

    public LevelChangedForm()

    {

    InitializeComponent();

    }

    private void LevelChangedForm_Load(object sender, EventArgs e)

    {

    foreach (Level myLvl in app.ActiveDesignFile.Levels)

    listBox1.Items.Add(myLvl.Name);

    }

    private void LevelChangedForm_FormClosed(object sender, FormClosedEventArgs e)

    {

    app.RemoveLevelChangeEventsHandler(DemoForm.myLevelChanged);

    MyAddin.s_addin.NewDesignFileEvent -= DemoForm.myNewDGNHandler;

    }

    }

    }

    4. Open the file command.xml, add a new command csAddins DemoForm LevelChanged and

    identify its handler to csAddins.DemoForm.LevelChanged. If you are not familiar with the XML

    command table, please refer the related topics in chapter 4.

    5. Open the file DemoForm.cs and scroll to its end. Then add command handler LevelChanged as

    below. This snippet implements three things: a). Open LevelChangedForm in singleton mode. In other

    words, this form can only has one instance at the same time. b). Add level change handler by using

    AddLevelChangeEventshandler. c). Append our new design file event handler(myNewDGNHandler)

    to NewDesignFileEvent chain of current Addin instance.

    public static LevelChangedClass myLevelChanged = null;

    public static AddIn.NewDesignFileEventHandler myNewDGNHandler = null;

    private static LevelChangedForm myLevelForm = null;

    public static void LevelChanged(string unparsed)

    {

    if (null == myLevelForm || myLevelForm.IsDisposed)

    {

    myLevelForm = new LevelChangedForm();

    myLevelForm.AttachAsTopLevelForm(MyAddin.s_addin, false);

    myLevelForm.Show(); myLevelChanged = new LevelChangedClass();

    Utilities.ComApp.AddLevelChangeEventsHandler(myLevelChanged); myNewD

    GNHandler = new AddIn.NewDesignFileEventHandler

    (LevelChangedClass.MyAddin_NewDesignFileEvent);

    MyAddin.s_addin.NewDesignFileEvent += myNewDGNHandler;

    }

    else

    myLevelForm.Activate();

    }

    6. Next we will construct LevelChangedClass. We still add code into DemoForm.cs. We should

    notice the following points to this code snippet: a). This class should be derived from

    interface ILevelChangeEvents. b). Need to rewrite a unique method LevelChanged. In this method,

    we update the content of listBox1 in three cases. c). In order to call the common method

  • PopulateLevelList, we put the NewDesignFile event handler into this class. This is not necessary.

    Please note: The method MyAdd_NewDesignFileEvent is named by us but its prototype must

    conform to the delegate declaration we found from VS object browser shown as below picture. d).

    The method PopulateLevelList is defined by us. By iterating the collection

    System.Windows.Forms.Application.OpenForms, we can find the opened LevelChangedForm and

    populate it again.

    class LevelChangedClass : ILevelChangeEvents

    {

    public void LevelChanged(MsdLevelChangeType ChangeType, Level TheLevel,

    ModelReference TheModel)

    {

    if (MsdLevelChangeType.AfterCreate == ChangeType ||

    MsdLevelChangeType.AfterDelete == ChangeType ||

    MsdLevelChangeType.ChangeName == ChangeType)

    PopulateLevelList();

    }

    public static void MyAddin_NewDesignFileEvent

    (AddIn sender, AddIn.NewDesignFileEventArgs eventArgs)

    {

    if (AddIn.NewDesignFileEventArgs.When.AfterDesignFileOpen == eventArgs.WhenCode)

    PopulateLevelList();

    }

    private static void PopulateLevelList()

    {

    LevelChangedForm myLevelChangedForm = null;

    foreach (Form myForm in System.Windows.Forms.Application.OpenForms)

    if ("LevelChangedForm" == myForm.Name)

    {

    myLevelChangedForm = (LevelChangedForm)myForm;

    break;

    }

    if (null != myLevelChangedForm)

    {

    myLevelChangedForm.listBox1.Items.Clear();

    foreach (Level myLvl in Utilities.ComApp.ActiveDesignFile.Levels)

    myLevelChangedForm.listBox1.Items.Add(myLvl.Name);

  • }

    }

    }

    7. Lets test our assembly after finishing the code writing. Firstly, build csAddins again from VS.

    Then start Mstn and keyin mdl load csAddins,,MyDomain to load csAddins. Open LevelChanged

    form by keyining csaddins demoform levelchanged and open Mstns Level Manager by selecting

    the menuSettings > Levels > Manager. The two opened dialogbox are shown as below. When you

    now add, remove a level or change a levels name, the content of LevelChanged form updates

    immediately. You can also test the file opening action. You will find the content of LevelChanged

    form updated automatically after you open a new design file.

    Learning MicroStation Addins Step by Step[8]

    Chapter 8. Calling MDL API function from Addins

    Maybe you have noticed that neither mesh surface nor SmartSolid can be found in our

    MicroStationDGN object model. In fact, these two important 3D functions havent wrapped into

    current object model as of MstnV8iSS2. Then does it say we can not implement these advanced

    functions by C# language? The answer is NO. By PInvoke of C# we can call functions in Win32 API

    and by the same approach we can call functions in MDL API.

    I strongly recommend you to install Mstn V8iSS2 SDK before you learn the example of this chapter.

    You can download this development kit from website http://selectservices.bentley.com shown as

    below. After you install this kit you will find three chm format help documents under the folder

    documentation of your Mstn. One of them is MDLAPIFunctionReference.chm in which all of

    published MDL functions are listed and you can find the prototype of

    mdlMesh_newPolyfaceFromXYTriangulation from it. Now lets start to create a Mesh surface

    element from C# addin step by step.

  • 1. Open the file command.xml, add a new command csAddins CreateElement Mesh and identify its

    handler to csAddins.CreateElement.Mesh. If you are not familiar with the XML command table,

    please refer the related topics in chapter 4.

    2. Open the file CreateElement.cs and add three using statements at the beginning of its code. These

    using statements are used for file processing, generic programming and interop.

    using System.IO;

    using System.Collections.Generic;

    using System.Runtime.InteropServices;

    3. Scroll to the end of this file; add command handler Mesh and DllImport attribute delaration which

    PInvoke needed. The finished code is shown as below. We need to point out : a). The prototype of

    mdlMesh_newPolyfaceFromXYTriangulation can be found from MDLAPIFunctionReference.chm;

    b). The data this function needed comes from an external text file named data-13.asc. We use a .NET

    class StreamReader to implement file reading; c). Because the amout of data points is unpredicated,

    we have to use a generic programming collection class List which begins from C# 2.0 to dynamically

    get these points data; d). We also call the important method MdlCreateElementFromElementDescrP

    to convert the MDL element descriptor ppMeshDescr returned by

    mdlMesh_newPolyfaceFromXYTriangulation to an element meshEl in object model.

    [DllImport("stdbspline.dll")]

    public static extern int mdlMesh_newPolyfaceFromXYTriangulation

    (out int ppMeshDescr, Point3d[] xyzArray, int numXYZ);

    public static void Mesh(string unparsed)

    {

    Application app = Utilities.ComApp;

    char[] delimiterChars = { ' ' };

    string myLine;

    List meshPnts = new List();

    StreamReader sr = new StreamReader(@"d:\data-13.asc");

    while (null != (myLine = sr.ReadLine()))

    {

    string[] sArray = myLine.Split(delimiterChars);

  • meshPnts.Add(app.Point3dFromXYZ(

    double.Parse(sArray[0]), double.Parse(sArray[1]), double.Parse(sArray[2])));

    }

    sr.Close();

    int ppMeshDescr;

    Point3d[] xyzArray = meshPnts.ToArray();

    if (0 == mdlMesh_newPolyfaceFromXYTriangulation(out ppMeshDescr, xyzArray,

    meshPnts.Count))

    {

    Element meshEl = app.MdlCreateElementFromElementDescrP(ppMeshDescr);

    app.ActiveModelReference.AddElement(meshEl);

    }

    }

    4. Please download data-13.zip file and uncompress it to your root folder of drive D. Certainly, this

    data file can be placed at any other position as long as you keep the same position in your source

    code.

    data-13.zip

    5. Build csAddins in VS; Start Mstn and open a 3D model; Keyin mdl load csAddins,,MyDomain to

    load csAddins and then continue to keyin csaddins createelement mesh to generate a mesh surface

    shown as below. Its render effect is also shown.

    The below link is the final version of csAddins source code, for your reference.

    csAddins-Final.zip

    Learning MicroStation Addins Step by Step[9]

    Chapter 9. Writing Addins using C++/CLI

    From the last chapter, maybe you have found that in order to call the MDL API function we have to

    declare it by using DllImport attribute. Fortunately, the function

    mdlMesh_newPolyfaceFromXYTriangulation is simple and we just need one such function to create a

    mesh surface. But if you want to create smart solid by programming, you will have no luck because it

    normally needs to call several MDL functions. In this case, it will be cumbersome and difficult to

    declare every function by DllImport attributes. Luckily Microsoft provides us a powerful mixed

    assembly programming language, it is C++/CLI. When we change to write our assembly with

    C++/CLI, it is possible to use .NET functionality and to call MDL functions directly. Now, lets write

    an Addin application by using C++/CLI. We will demonstrate creating a mesh surface and a smart

    solid.

    1. Open our csAddins solution in VS and open the Create New Project form by selecting menu File >

    New > Project. Then add a new project cppAddins according to the below picture. Please note, the

    type of new project is Class Library under the category Visual C++/CLR and the Solution option

    should be selected as Add to solution.

  • 2. Select solution csAddins (not the project csAddins), then press the function key to rename the

    csAddins to mstnAddins. Exit VS and rename the folder name csAddins to mstnAddins in Windows

    explorer. Restart VS, open the new mstnAddins solution. Now you should see two projects cppAddins

    and csAddins in the solution mstnAddins shown as below:

    3. Make sure you have a correct MS variable definition in you system environment because our

    following settings will heavily depend on this environment variable. This variable should point to the

    installation location of your MstnV8iSS2. The below picture shows the case in my box. If you havent

    this setting, please exit VS, add this new environment variable followed by the below picture which

    you can begin from the property of "My Computer" and then restart VS. Why we need to exit and

    restart VS? Because VS searches system environment variables only at its starting time.

  • 4. Next we will set the default path of C++ project. This step is needed to do only once and all C++

    projects will inherit these settings. Open property manager by selecting menu View > Property

    Manager, expand cppAddins and Release|Win32 (or Debug|Win32), then double click

    Microsoft.Cpp.Win32.user to open the project default property page dialogbox. Select the VC++

    Directories on the left pane of this dialogbox, and then set the Include Directories, Reference

    Directories and Library Directories on the right pane like the following picture shown.

  • 5. Set the project property of cppAddins. Open the project property dialogbox by right clicking the

    cppAddins and selecting the Properties menu item on the popup menu. Set Output Directory to

    $(MS)mdlapps\ under the category General. This makes the cppAddins.dll is created under the

    mdlapps folder of Mstn. Set Command Line to $(MS)assemblies\UstnXOM ValidateAddIn

    $(TargetPath) under the Post-Build Event of Build Events (Certainly, you can set Command Line to

    $(MS)mdl\bin\UstnXOM ValidateAddIn $(TargetPath) if you have installed Mstn SDK). This setting

    makes the assembly validation check after finishing the project build. Please refer to chapter 4 for the

    detailed description of assemblys validation.

  • 6. Copy file commands.xml from project csAddins to cppAddins under Windows explorer. Back to

    VS, add commands.xml under the Resource Files of cppAddins. Open this files property page, set its

    Item Type to Compiled Managed Resource. This is very important because it can make

    commands.xml embedded into the final assembly cppAddins.dll as resource.

    7. Double click commands.xml to open it, and then edit it. The two commands we created are

    cppAddins CreateElement Mesh and cppAddins CreateElement Solid. The final edition of this file is

    as below:

  • 8. Double click the file stdafx.h under Header Files to open it, and then edit it as below. To call MDL

    functions, we define winNT and include one header file (.h) and three function definition file (.fdf).

    To construct Mstn Addin assembly with C++, we use #using directive to reference five necessary

    DLLs. Meantime, we use using namespace declaration to declare namespace std and

    System::IO. Special emphasis here, we eliminate the line using namespace System because it will

    bring ambiguous symbol error when you try to use UInt32 type in your program. UInt32 is a global

    type definition in MDL, it conflicts with the System::UInt32 when we omit the System namespace.

    #pragma once

    #define winNT 1

    #include

    #include

    #include

    #include

    #using

    #using

    #using

    #using

    #using

    using namespace std;

    using namespace System::IO;

    9. Double click the file stdafx.cpp under Source Files to open it, and then edit is as below. In this file,

    we use pragma comment to identify the static libraries we need to link. Certainly, you can also

    specify these library files under the Linker category of project property.

    #include "stdafx.h"

    #pragma comment(lib, "mdlbltin.lib")

    #pragma comment(lib, "BentleyDgn.lib")

    #pragma comment(lib, "toolsubs.lib")

    #pragma comment(lib, "mdllib.lib")

    #pragma comment(lib, "msbspline.lib")

    #pragma comment(lib, "kisolid.lib")

  • 10. Double click the file cppAddins.cpp under Source Files to open it, and then edit is as below. This

    file is the main framework of Mstn Addin assembly. Please refer to the previous related topics of

    writing Mstn Addin assembly by using C# to understand their meanings.

    #include "stdafx.h"

    namespace cppAddins

    {

    typedef Bentley::MicroStation::AddIn Base;

    [Bentley::MicroStation::AddInAttribute(KeyinTree="commands.xml", MdlTaskID="CppAddins")]

    public ref class MyAddin sealed : public Base

    {

    private: MyAddin(System::IntPtr mdlDesc) : Base(mdlDesc)

    {

    }

    public: virtual int Run(array^ commandLine) override

    {

    return 0;

    }

    };

    }

    11. Right click Source Files under the project cppAddins, select Add > New Item in the popup menu,

    and then select file type is C++ File(.cpp) in the new popup form and input file name CreateElement.

    Click Ok button to add a new source file CreateElement.cpp under the Source Files. This is our main

    work file.

    12. Input the following contents in CreateElement.cpp. We explain several keypoints here: a) We

    define two command handlers Mesh and Solid to process command cppAddins CreateElement Mesh

    and cppAddins CreateElement Solid. b) In Mesh function, we change to use C++STL vector to store

    datapoints which come from data file data-13.asc dynamically. And we use mdlCnv_

    masterUnitsToUors to convert datapoints from master unit to resolution unit because the default unit

    in MDL function is resolution unit not master unit. (If you arent familiar with the concept of working

    unit please to learn it from the help documentation of Mstn). c) Please note, we use a lot of MDL

    functions, such as mdlCnv_ masterUnitsToUors, mdlMesh_newPolyfaceFromXYTriangulation,

    mdlElmdscr_add and mdlKISolid_xxx. We dont need to declare them with DllImport attributes and

    this is just the main reason we change to C++/CLI. d) In order to call mdlKISolid_sweepBodyWire,

    we have to call several extra MDL functions in Solid function. We firstly create a linestring as path,

    and then create a circle as profile. By sweeping the profile along with the path, we create a 3D pipe

    which type is smart solid (type=2) not solid (type=19). Detailed explanation of KISolid functions is

    out of the range of this blog. If you are interested in the code and have some doubts, welcome to

    contact me or write them directly in the comments of this blog.

    #include "stdafx.h"

    namespace cppAddins

    {

    public ref class CreateElement

  • {

    public:

    static void Mesh (System::String^ unparsed)

    {

    DPoint3d pt, *pPt = NULL, *xyzArray = NULL;

    vector meshPnts;

    array^ delimiterChars = { ' ' };

    System::String^ myLine;

    StreamReader^ sr = gcnew StreamReader("d:\\atemp\\data-13.asc");

    while (nullptr != (myLine = sr->ReadLine()))

    {

    array^ sArray = myLine->Split(delimiterChars);

    pt.x = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[0]));

    pt.y = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[1]));

    pt.z = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[2]));

    meshPnts.push_back (pt);

    }

    sr->Close();

    xyzArray = new DPoint3d[meshPnts.size()];

    pPt = xyzArray;

    for (vector::iterator iter=meshPnts.begin(); iter != meshPnts.end(); ++iter,++pPt)

    *pPt = *iter;

    MSElementDescrP pMeshDescr = NULL;

    if (SUCCESS == mdlMesh_newPolyfaceFromXYTriangulation(&pMeshDescr, xyzArray,

    meshPnts.size()))

    {

    mdlElmdscr_add (pMeshDescr);

    mdlElmdscr_freeAll (&pMeshDescr);

    }

    delete[] xyzArray;

    }

    static void Solid (System::String^ unparsed)

    {

    MSElement el;

    MSElementDescrP pPathDescr = NULL, pProfileDescr = NULL, pSolidDescr = NULL;

    KIBODY *pPathBody = NULL, *pProfileBody = NULL;

    Transform fwdBodyTransform, tmpBodyTransform, toolTransform;

    RotMatrix rMatrix;

  • DPoint3d pts[4] = {{0,0,0}, {5000,0,0}, {5000,5000,0}, {5000,5000,5000}};

    DVec3d zVec; mdlLineString_create (&el, NULL, pts, 4);

    mdlElmdscr_new (&pPathDescr, NULL, &el); mdlVec_subtractDPoint3dDPoint3d

    (&zVec, &pts[1], &pts[0]);

    mdlVec_normalizeDVec3d (&zVec, &zVec);

    mdlRMatrix_fromNormalVector (&rMatrix, &zVec);

    mdlRMatrix_getInverse (&rMatrix, &rMatrix);

    mdlEllipse_create (&el, NULL, pts, 200, 200, &rMatrix, 0);

    mdlElmdscr_new (&pProfileDescr, NULL, &el);

    if (SUCCESS == mdlKISolid_elementToBody2 (&pPathBody, &fwdBodyTransform,

    pPathDescr, ACTIVEMODEL, 1L, FALSE) &&

    SUCCESS == mdlKISolid_elementToBody2 (&pProfileBody, &tmpBodyTransform,

    pProfileDescr, ACTIVEMODEL, 1L, FALSE)

    && NULL != pPathBody && NULL != pProfileBody)

    {

    Transform invBodyTransform;

    mdlTMatrix_getInverse (&invBodyTransform, &fwdBodyTransform);

    mdlTMatrix_multiply (&toolTransform, &invBodyTransform, &tmpBodyTransform);

    mdlKISolid_applyTransform (pProfileBody, &toolTransform);

    if (SUCCESS == mdlKISolid_sweepBodyWire (&pProfileBody, pPathBody, NULL, 0, true,

    ACTIVEMODEL))

    {

    if (SUCCESS == mdlKISolid_bodyToElement (&pSolidDescr, pProfileBody, -1, -1, NULL,

    ACTIVEMODEL))

    {

    mdlKISolid_beginCurrTrans (ACTIVEMODEL);

    mdlElmdscr_transform (pSolidDescr, &fwdBodyTransform);

    mdlKISolid_endCurrTrans ();

    mdlElmdscr_add (pSolidDescr);

    mdlElmdscr_freeAll (&pSolidDescr);

    }

    }

    }

    if (NULL != pProfileBody) mdlKISolid_freeBody (pProfileBody);

    if (NULL != pPathBody) mdlKISolid_freeBody (pPathBody);

    if (NULL != pPathDescr) mdlElmdscr_freeAll (&pPathDescr);

  • if (NULL != pProfileDescr) mdlElmdscr_freeAll (&pProfileDescr);

    }

    };

    };

    13. Build and test cppAddins assembly. Right click cppAddins project and select Build menu item in

    the popup menu to create cppAddins.dll. Or you can simple click the first icon tool in the Build

    toolbox to create cppAddins.dll. After you have successfully created the assembly, you can start Mstn

    and open a DGN with 3D model. Next, keyin MDL LOAD cppAddins to load cppAddins assembly.

    And then, keyin cppAddins CreateElement Mesh to draw a mesh surface which is same as the surface

    drawn in last chapter. Finally, keyin cppAddins CreateElement Solid to draw a 3D pipe shown as

    below pictures.

    Thus far, we finished our Learning MicroStation Addins Step by Step series. The final source code

    of solution mstnAddins is as below. Warmly welcome to identify my mistakes and warmly welcome

    to share this blog to other people.

    mstnAddins.zip