In this chapter, you'll learn how to create and use Modal, Modeless, Tool Settings, and Dockable dialogs in MicroStation Add-ins. Modal, modeless, and dockable dialogs are common user interface patterns found in many desktop applications, and there are numerous resources available online covering their implementation.
The Tool Settings dialog is unique to MicroStation. Although it behaves as a modeless window, its contents automatically update based on the currently active tool, providing a seamless and dynamic user experience.
In MicroStation Add-ins, dialogs can be developed using either Windows Forms (WinForms) or Windows Presentation Foundation (WPF). Both technologies allow developers to visually design user interfaces using Visual Studio's designer, significantly reducing development effort compared to the traditional MDL .r resource-based approach. Developers who have previously spent time creating and maintaining .r resource files will appreciate the productivity and flexibility offered by modern Add-in UI development.
Step-by-step process demonstrating how to add Windows Forms to Add-ins.
Begin by creating a new Add-in project by following the steps described in the section, Building a Simple Add-in Application, name the new project as csAddins5
If you have already created a Visual Studio project template from the previous section, you can use that template to quickly generate a new project. Simply create a new project from the template and assign it the name csAddins5.
Before creating the dialog layout, it is helpful to become familiar with the Visual Studio design environment.
The Toolbox contains a variety of controls that can be added to your form.
This window allows you to configure the appearance and behaviour of the form and its controls.
Add the following controls to the form:
This allows the calling class (DemoForm) to access the value entered by the user in ModalForm.
Next, configure the form itself:
You do not need to manually enter the Location and Size property values. Visual Studio automatically updates these settings as controls are repositioned and resized within the designer, making it easy to create the layout visually.
The completed ModalForm design should resemble the figure shown below.
Form (Name) = ModalForm / AcceptButton = btnOk / ControlBox = False / FormBorderStyle = FixedDialog
/ Size = 288,145 / Text = CSHelloForm
Label (Name) = label1 / Location = 22,24 / Size = 82,17 / TabIndex = 0 / Text = Enter 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
Add the following controls to the form:
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
Event handlers allow your form to respond to user actions such as loading the form, pressing keys, or clicking buttons.
You can do this by right-clicking MultiScaleCopyForm.cs in
Solution Explorer and selecting View Designer.
to automatically create the following event handler methods:
Next, add event handlers for the text boxes:
it to create the txtScale_KeyPress event handler.
Since the txtYOffset and txtZOffset controls require the same validation as txtXOffset, they can share the same event handler:
drop-down list instead of creating a new event handler.
Finally, add a click event for the Load Default button:
This approach keeps your code simpler by reusing the same event handler for controls that require identical behaviour.
Open the source code for MultiScaleCopyForm by right-clicking MultiScaleCopyForm.cs in Solution Explorer and selecting View Code. Update the code as shown in the example below.
As you review the code, note the following important points:
add a reference to the Microsoft.Win32 namespace.
change the form's base class from Form to
Bentley.MicroStationPlatformNET.WinForms.Adapter.
prevent invalid characters from being entered into the text boxes.
Click method restores the default values by copying the Tag property
of each text box into its Text property.
stores the current values from the five text boxes in the Windows Registry.
event reads the saved values from the Registry and restores them automatically.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32;
using Bentley.MstnPlatformNET.WinForms;
namespace csAddins5
{
public partial class MultiScaleCopyForm : //Form
Adapter
{
public MultiScaleCopyForm()
{
InitializeComponent();
}
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());
}
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 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();
}
}
}
Important
After changing the form's base class from Form to Adapter, Visual Studio can no longer display the form in the Windows Forms Designer. If you attempt to open the designer, an error message will appear.
To continue editing the form visually, temporarily change the base class back to Form, make your design changes, and then change it back to Adapter before building and running the Add-in. This is a common workflow when developing MicroStation WinForms-based Add-ins.
The form can be designed using standard Windows Forms controls, and no custom event handlers are required for this example.
The main requirement is to change the form's base class from Form to Adapter, just as you did with MultiScaleCopyForm. This allows the form to integrate correctly with the MicroStation environment.
This form will be used later as a Tool Settings dialog.
Add four RadioButton controls to the form with the following names:
Configure the radio buttons as follows:
one of them can be selected at a time.
one of these options can be selected.
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=
To make the user interface more intuitive, add images that represent the three dialog types used in this example:
First, prepare three image files that will be used as icons or illustrations for these dialog types.
Next, add the images to your project resources:
Once added, the images become part of the application and can be accessed directly from code without needing to distribute the image files separately.
Create a new Windows Form named ToolbarForm. This form will serve as a simple toolbar that allows users to launch the different dialog types created earlier.
Add the following controls to the form:
Next, assign the image resources added in the previous step to the three buttons, using one image for each dialog type.
You can set the button images through the Image property in the Properties window by selecting the appropriate resource.
Optionally, use the ToolTip control to display a short description when the user hovers over each button. This helps users quickly understand the purpose of each button.
Form (Name) = ToolbaForm / FormBorderStyle = FixedToolWindow / MaximizeBox = False / MinimumBox = False
/ ShowIcon = False / ShowInTaskbar = False / Size = 141, 83/ 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
11. Add Button Event Handlers
Double-click each of the three buttons to create their click event handlers. Then update the ToolbarForm.cs source code as shown below.
Keep the following points in mind:
To support docking, the form must implement the GetDockedExtent
and WindowMoving methods required by IGuiDockable.
The code that processes these commands will be added in the next step.
using Bentley.MstnPlatformNET;
using Bentley.MstnPlatformNET.GUI;
using Bentley.MstnPlatformNET.WinForms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace csAddins5
{
public partial class ToolbarForm : //Form
Adapter, IGuiDockable
{
public ToolbarForm()
{
InitializeComponent();
}
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)
{
Session.Instance.Keyin("csAddins5 DemoForm Modal");
}
private void btnTopLevel_Click(object sender, EventArgs e)
{
Session.Instance.Keyin("csAddins5 DemoForm TopLevel");
}
private void btnToolSettings_Click(object sender, EventArgs e)
{
Session.Instance.Keyin("csAddins5 DemoForm ToolSettings");
}
}
}
12. Create the DemoForm Class
Create a new class named DemoForm in a file called DemoForm.cs. This class is responsible for opening the different dialog types used in the example and processing commands sent from the toolbar.
For each dialog type, use the appropriate Adapter method:
The complete source code for DemoForm is shown below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace csAddins5
{
class DemoForm
{
public static void Toolbar(string unparsed)
{
ToolbarForm myForm = new ToolbarForm();
myForm.AttachAsGuiDockable(MyAddin.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.Addin, false);
myForm.Show();
}
public static void ToolSettings(string unparsed)
{
NoteCoordForm myForm = new NoteCoordForm();
myForm.AttachToToolSettings(MyAddin.Addin);
myForm.Show();
}
}
}
Four new commands are added:
csAddins5 DemoForm Toolbar | Modal | TopLevel | ToolSettings
Copy and paste the following lines into the commands.xml file.
<SubKeyinTables>
<KeyinTable ID="CreateElement">
<Keyword SubtableRef="Commands" CommandWord="CreateElement">
<Options Required="true"/>
</Keyword>
<Keyword SubtableRef="DemoForm" CommandWord="DemoForm">
<Options Required="true"/>
</Keyword>
</KeyinTable>
</SubKeyinTables>
<SubKeyinTables>
<KeyinTable ID="Commands">
<Keyword CommandWord="LineAndLineString1"> </Keyword>
<Keyword CommandWord="LineAndLineString2"> </Keyword>
<Keyword CommandWord="LineAndLineString3"> </Keyword>
<Keyword CommandWord="ShapeAndComplexShape"> </Keyword>
<Keyword CommandWord="TextString"> </Keyword>
<Keyword CommandWord="Cell"> </Keyword>
<Keyword CommandWord="Dimension"> </Keyword>
<Keyword CommandWord="BsplineCurve"> </Keyword>
</KeyinTable>
<KeyinTable ID="DemoForm">
<Keyword CommandWord="Toolbar"/>
<Keyword CommandWord="Modal"/>
<Keyword CommandWord="TopLevel"/>
<Keyword CommandWord="ToolSettings"/>
</KeyinTable>
</SubKeyinTables>
<KeyinHandlers>
<KeyinHandler Keyin="csAddins5 CreateElement LineAndLineString1"
Function="csAddins5.CreateElement.LineAndLineString1"/>
<KeyinHandler Keyin="csAddins5 CreateElement LineAndLineString2"
Function="csAddins5.CreateElement.LineAndLineString2"/>
<KeyinHandler Keyin="csAddins5 CreateElement LineAndLineString3"
Function="csAddins5.CreateElement.LineAndLineString3"/>
<KeyinHandler Keyin="csAddins5 CreateElement ShapeAndComplexShape"
Function="csAddins5.CreateElement.ShapeAndComplexShape"/>
<KeyinHandler Keyin="csAddins5 CreateElement TextString"
Function="csAddins5.CreateElement.TextString"/>
<KeyinHandler Keyin="csAddins5 CreateElement Cell"
Function="csAddins5.CreateElement.Cell"/>
<KeyinHandler Keyin="csAddins5 CreateElement Dimension"
Function="csAddins5.CreateElement.Dimension"/>
<KeyinHandler Keyin="csAddins5 CreateElement BsplineCurve"
Function="csAddins5.CreateElement.BsplineCurve"/>
<KeyinHandler Keyin="csAddins5 CreateElement Cone"
Function="csAddins5.CreateElement.Cone"/>
<KeyinHandler Keyin="csAddins5 DemoForm Toolbar"
Function="csAddins5.DemoForm.Toolbar"/>
<KeyinHandler Keyin="csAddins5 DemoForm Modal"
Function="csAddins5.DemoForm.Modal"/>
<KeyinHandler Keyin="csAddins5 DemoForm TopLevel"
Function="csAddins5.DemoForm.TopLevel"/>
<KeyinHandler Keyin="csAddins5 DemoForm ToolSettings"
Function="csAddins5.DemoForm.ToolSettings"/>
</KeyinHandlers>
Under menus, select Build > Build Solution to compile the solution.
Clicking each button opens the modal, non-modal, and tool settings dialog boxes respectively. At the same time, this toolbar dialog box can be docked.
You can download the source code for this wiki here.