How to create Enterprise Architect packages, classes, attributes programmatically - c#

I am trying to use/understand Interop.EA and EA class in C# and how to create diagrams programmatically.
I'm working with user data (.XML file (not XMI)) from another web application.
I have tried CSAddinDemo from Sparx and it works fine, but does not show how to add/create new.
My goal is to create EA class diagram form XML data, in C#.
Something like this??
public void EA_create()
{
EA.DiagramObject d = new DiagramObject();
EA.Element e = new Element();
EA.Element elementEa = EA.Element.AddNew("Requirement", "non-functional");
elementEa.Update();
elements.Refresh();
//MessageBox.Show("Class created");
}

First off, you need an EA project (aka "repository") to work in. In order to create a new EA project (.EAP file), use Repository.CreateModel(). In order to open an existing one, use Repository.OpenFile().
Other than the Repository class, which is the top-level class for all interactions with EA, you don't create objects yourself. Instead, you call AddNew() on the various Collections you traverse -- this goes for packages, elements, diagrams, diagram objects, connectors, attributes, tagged values, etc etc.
Repository.Models is such a collection (of Packages).
A Package then contains additional collections, such as Packages, Elements and Diagrams. A Diagram has a collection of DiagramObjects.
A DiagramObject is the graphical representation of an element within one (1) diagram (remember that an element is stored in exactly one package but can be shown in any number of diagrams).
EA does not allow you to create any diagrams or elements at the top level of the package tree (called the root node); you must first create a child package. So at the very least you need to create one Package and one Diagram, a number of Elements and one DiagramObject for each element.
So in the code you've got, AddNew() doesn't work because you're trying to call it on an Element and you need to call it on a Collection.
Your use of Update() and Refresh() are correct, assuming that elements is a Collection. You must always call Update after any changes to an object, and Refresh after any changes to a collection.
There's some good getting-started stuff in the help file. Look in Automation and Scripting -- Enterprise Architect Object Model, and read the section titled Using the Automation Interface.
Then, look at Reference -- Code Samples, especially Open the Repository and Add and Manage Diagrams.
Finally, note that an Add-In is a piece of code which extends EA and is run from within an EA process. This isn't necessary if you want to create a new model or make changes to an existing one; you only need to write an Add-In if you want to integrate with EA's GUI or respond to various events triggered by EA.

Related

How to open the Property dialog of selected objects in Management Console?

I am searching for the possibility to open default property dialogues for particular Windows objects, like:
the property dialogue for a particular service in services.msc
the property dialogue for a particular scheduled taks in taskschd.msc
etc.
I do not want to interact with that dialogues or change any of the properties. I just want to open them to give the user direct access to a single items properties (instead of opening the listings (by calling the *.msc executables) in which the user has to search the object again).
I have already partially copied the dialogues functions into own forms and code for other purposes, by the way, but I want to give the user the option to open the default ones and make any changes directly.
Now, I have found some hints but I am stuck as there is always some crucial information missing:
1. Using so-called SnapIns of MMC (Microsoft Management Console)
There is this relatively new answer which uses VB code but I have no clue how I could use the MMC Automation Object Model in C# .NET Framework.
Furthermore, there is no clean and easy example/explanation of how to simply call an existing .msc process/list/window by usage of the Microsoft.ManagementConsole. Instead, there are several horrifying complex tutorials how to implement SnapIns into C#.
To be clear here: What I want to do is to reference a dll, go through some list (if necessary) and just call the properties dialogue.
2. COM invoke of old API
There is this old answer where someone recommends using invoke on an outdated ITaskScheduler class which does not solve the general dialogue call but at least the one for scheduled tasks. Perhaps it is also possible to use something similar for services, etc. - but, again, there is no clear example or explanation of how to implement this approach.
It's relatively simple.
Add a COM Reference to Microsoft Management Console 2.0.
Add the using MMC20 directive.
Create a new MMC20.Application object
Use the Application.Load() method to load a Snap-In (services.msc here)
The ActiveView of the Application Document contains the list of items: ListItems Property
Select a Node by name or Index and call the DisplaySelectionPropertySheet() method to show its Property pane
For example:
Note: setting mmcApp.UserControl = 1; leaves the Console open, otherwise it would close.
using MMC20;
// [...]
MMC20.Application mmcApp = new MMC20.Application();
mmcApp.UserControl = 1;
mmcApp.Load("services.msc");
var doc = mmcApp.Document;
var view = doc.ActiveView;
var node = view.ListItems.OfType<Node>().FirstOrDefault(n => n.Name == "Base Filtering Engine");
if (node != null) {
view.Select(node);
view.DisplaySelectionPropertySheet();
}
To enumerate the ListItems, use a standard loop or an extension method as shown above:
var nodes = view.ListItems;
foreach (MMC20.Node node in nodes) {
Console.WriteLine(node.Name);
}
Here's documentation on how to use the MMC SDK. It's a Win32 API, so you'll have to use COM, PInvoke, or other interop to use it.
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/mmc/microsoft-management-console-start-page#developer-audience
The C++ examples are probably more informative than the VB ones. The .h files are part of the windows sdk so you should be able to find the clsid and other constants that you need in there: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/mmc/using-c-with-the-mmc-2-0-automation-object-model

How do I handle variations in HTMLControl Inner Text in a Page Object Pattern?

Here is a little background on the specifications of my project:
We use Specflow and Microsoft CodedUI Framework for UI Automation
I have built a PageFactory that combines three Abstract Base Classes : BasePage, BaseMap, and BaseValidator that all Maps, Pages, and Validators inherit
Our Application that we are automating has numerous workflows that make defined HTML Controls have different InnerText Values (HTMLComboBoxes for example)
Everything is and needs to be abstracted from the actual Specflow Test Code in the Page Object Pattern, no unique code can exist within a Specflow Step
In my Maps I have certain controls like a combobox that has an InnerText change if a certain workflow is selected. I need to build assertion and verification statements to make sure the InnerText is correct for the workflow that is selected. This is not a problem. However, I do not want to just define a new variable for every InnerText change(There are A LOT).
Is there any way I can account for the InnerText variations in the Page Object Pattern and not have to code a new variable for every single one?
Here is an example of a Map Entry:
public HtmlComboBox NextActionControlDropDownList()
{
var NextActionControlDropDownList = new PropertyExpressionCollection {
new PropertyExpression(HtmlComboBox.PropertyNames.Id, "MEDCHARTContent_EmmpsContent_nextActionControl_ActionDropDownList", PropertyExpressionOperator.EqualTo)
};
return Window.Find<HtmlComboBox>(NextActionControlDropDownList);
}
This is the Base Control definition. It can also be this:
public HtmlComboBox NextActionControlARFormalComplReview()
{
var NextActionControlARFormalComplReview = new PropertyExpressionCollection {
new PropertyExpression(HtmlComboBox.PropertyNames.Id, "MEDCHARTContent_EmmpsContent_nextActionControl_ActionDropDownList", PropertyExpressionOperator.EqualTo),
new PropertyExpression(HtmlComboBox.PropertyNames.InnerText, "--Select Action-- Return to USARC ", PropertyExpressionOperator.EqualTo)
};
return Window.Find<HtmlComboBox>(NextActionControlARFormalComplReview);
}
My thoughts so far were to maybe make another map and inherit it? But that wouldn't solve my initial problem of too many variables for a single control. I don't see how If statements would help either because it needs to be defined for the framework to find the control. Maybe I could store the differing values in a collection of sorts and have a parameter key value to access them... but that seems like I would run into a lot of issues.
If you try and see the methods under PropertyExpressionOperator you would see something called Contains.
new PropertyExpression(HtmlComboBox.PropertyNames.InnerText, "--Select Action--", PropertyExpressionOperator.Contains)

Modify FieldDefinitions programmatically

There is currently a need within our shop to be able to add/modify/delete the FieldDefinitions on a WorkItemStore programmatically, without/before messing with the WorkItemType definitions. Is this possible? I know that i can GET all the fields on a project and see them, but what about adding new ones, or modifying existing ones ? (delete not as important).
i've been searching google for the past 2 days and couldn't find what i'm looking for...
Right now, this is how i am reading all the fields from the server.
List<FieldDefinition> all_defs = new List<FieldDefinition>();
FieldDefinitionCollection defs = wis.FieldDefinitions;
foreach(FieldDefinition def in defs)
all_defs.Add(def);
I was able to do a workaround. Basically, in code, I was able to get all of the fields on the server, create all of the fields that I needed to add or modify and then export one WIT as XML into a separate string.
From there, I extracted the new fields that I needed by comparison, modified the fields that I needed to modify (in Xml), changed the name of the WIT (in Xml) into a temporary one (e.g.: Missing_fields), then modified the WIT Xml to include both the new fields and the modified ones inside the Fields tag...
Then I used the Import WIT method on the temporary WIT I just created...
Then I refreshed the connection cache for the API... (this ensures the API has access to the new WIT so we can delete it)
Then I used the DestroyWIT action package to destroy the temporary WIT from the server, which essentially destroys the WIT itself, but not the fields as those are a separate entity on the server, and voilĂ ! You've got yourself a FieldDefinition import and modify tool.
I still can't delete but that's not too bad...

C# Factory Pattern

I am building a search application that has indexed several different data sources. When a query is performed against the search engine index, each search result specifies which data source it came from. I have built a factory pattern that I used to display a different template for each type of search result, but I've realized that this pattern will become more difficult to manage as more and more data sources are indexed by the search engine (i.e new code template has to be created for each new data source).
I created the following structure for my factory based off of an article by Granville Barnett over at DotNetSlackers.com
factory pattern http://img11.imageshack.us/img11/8382/factoryi.jpg
In order to make this search application easier to maintain, my thought was to create a set of database tables that can be used to define individual template types that my factory pattern could reference in order to determine which template to construct. I figured that I'd need to have a look up table that would be used to specify the type of template to build based off of the search result data source. I'd then need to have a table(s) to specify which fields to display for that template type. I'd also need a table (or additional columns within the template table) that would be use to define how to render that field (i.e. Hyperlink, Label, CssClass, etc).
Does anyone have any examples of a pattern like this? Please let me know.
Thanks,
-Robert
I would offer that this proposed solution is no less maintainable than simply associating a data source to the code template, as you currently have now. In fact, I would even go so far as to say you're going to lose flexibility by pushing the template schema and rendering information to a database, which will make your application harder to maintain.
For example, let's suppose you have these data sources with attributes (if I'm understanding this correctly):
Document { Author, DateModified }
Picture { Size, Caption, Image }
Song { Artist, Length, AlbumCover }
You then may have one of each of these data sources in your search results. Each element is rendered differently (Picture may be rendered with a preview image anchored to the left, or Song could display the album cover, etc.)
Let's just look at the rendering under your proposed design. You're going to query the database for the renderings and then adjust some HTML you are emitting, say because you want a green background for Documents and a blue one for Pictures. For the sake of argument, let's say you realize that you really need three background colors for Songs, two for Pictures, and one for Documents. Now, you're looking at a database schema change, which is promoted and pushed out, in addition to changing the parameterized template you're applying the rendering values to.
Let's say further you decide that the Document result needs a drop-down control, the Picture needs a few buttons, and Songs need a sound player control. Now, each template per data source changes drastically, so you're right back where you started, except now you have a database layer thrown in.
This is how the design breaks, because you've now lost the flexibility to define different templates per data source. The other thing you lose is having your templates versioned in source control.
I would look at how you can re-use common elements/controls in your emitted views, but keep the mapping in the factory between the template and the data source, and keep the templates as separate files per data source. Look at maintaining the rendering via CSS or similar configuration settings. For making it easier to maintain, considering exporting the mappings out as a simple XML file. To deploy a new data source, you simply add a mapping, create the appropriate template and CSS file, and drop them in to expected locations.
Response to comments below:
I meant a simple switch statement should suffice:
switch (resultType)
{
case (ResultType.Song):
factory = new SongResultFactory();
template = factory.BuildResult();
break;
// ...
Where you have the logic to output a given template. If you want something more compact than a long switch statement, you can create the mappings in a dictionary, like this:
IDictionary<ResultType, ResultFactory> TemplateMap;
mapping = new Dictionary<ResultType, ResultFactory>();
mapping.Add(ResultType.Song, new SongResultFactory());
// ... for all mappings.
Then, instead of a switch statement, you can do this one-liner:
template = TemplateMap[resultType].CreateTemplate();
My main argument was that at some point you still have to maintain the mappings - either in the database, a big switch statement, or this IDictionary instance that needs to be initialized.
You can take it further and store the mappings in a simple XML file that's read in:
<TemplateMap>
<Mapping ResultType="Song" ResultFactoryType="SongResultFactory" />
<!-- ... -->
</TemplateMap>
And use reflection et. al. to populate the IDictionary. You're still maintaining the mappings, but now in an XML file, which might be easier to deploy.

Properties embedded within another object

I am trying to use a business object that I am passing to the report.rdlc. The properties in my object are not directly exposed. The properties I require are embedded within another object inside the top level object. As this is a WCF project I can't control what goes on at the server end. I am just able to request these objects or Insert/Update/Delete their info from the database. It is done in this way as the back end can use multiple flavors of database.
Here is what I can see after adding my business object as a DataSource:
-BusinessObject
-CustomerInfo
-ClientName
-ColumnName
-DisplayName
-FieldName
-IsNull
-KeyColumn
-SenondKeyColumn
-StringValue
-ClientID
-ColumnName
-DisplayName
-FieldName
-IntValue
-IsNull
-KeyColumn
-SenondKeyColumn
+ClientAddress
+Instrument
+Telephone
etc etc
I need to be able to display, for example, the ClientName.StringValue field.
If I drag the field I want onto the report I get:
=First(Fields!StringValue.Value)
This doesn't display anything when the report is run, I assume because it can't qualify what StringValue it is talking about and there could be many.
If I try dragging the ClientName object I get:
=First(Fields!ContactName.Value)
However this gives:
#ERROR
When the report is run.
I would have thought you could use:
=First(Fields!ClientName.StringValue.Value)
but this won't even let me build.
The problem was that the info wasn't at the root level. I worked it out though.
=First(Fields!ClientName.Value.StringValue, "BusinessObject_CustomerInfo")
I've got a pretty good grip of the ReportViewer component now cheers.
If you set the data source to the CustomerInfo instance (or list) returned from the service it should work. The ReportViewer control can be a little complicated when you start dealing with object hierarchies, but you don't have to do anything crazy or special if all the information is at the root level.

Categories

Resources