Design-time versus Runtime Code Generation

This technical article by Rockford Lhotka hits again on the eternal debate about the advantages of code generation and the differences between generating at design-time or runtime…

Design Time Code Generation and Runtime Model-Driven Generation

The ancient history of my experience was completely around runtime generation. In fact, our products are full of instances of that.

Over the last years, you’ve moved to the design-time side of the field. And there are a number of reasons for that.

This article got me thinking again. Lhotka goes out to say that the advantages of code generation are guaranteed by both approaches but “(design-time automation) doesn’t offer the flexibility and adaptability provided by runtime automation”.

The first thought that comes to my mind is that the affirmation is obviously true; but it comes at the expense of (at least) two very important “features”.

  • Complexity – just consider the complexity of a runtime that is capable of interpreting a set of models that describe a very large application (an ERP for example).
  • Performance – as dynamic behavior will always (!) be slower than static.

The first one alone is enough to make me prefer design-time! Since I’m really sick of spending so much energy solving bugs and “design issues”… :)

Microsoft Tech.Days’2010

As I prepare for Tech.Days, I have to recommend this session in particular.

The Age of Software Industrialization (Pedro Salgueiro)

I haven’t really studied the agenda in detail but this one will be interesting for sure.

For more than one reason. :)

Posted 19 April 10 04:16 by hgr | 0 Comments   
Filed under ,
This Social Movement...

I’m not a fan of how things seam to be going with all these people sharing instant thoughts, comments, and links via Facebook, Twitter, Buzz, etc...

One of these days I was reading this post by Joel Spolsky and I found myself sharing his ideas about Twitter and this new “instant society”...

Having said this, my own company keeps investing more resources in sharing information using Facebook and Twitter...

So, I will give it a try for some time... In “consumer mode”. :)

http://www.facebook.com/people/Hugo-Ribeiro/626367682

http://twitter.com/hgr2012

Posted 18 March 10 10:05 by hgr | 0 Comments   
Filed under
Work Item Manager

This tool by Telerik is a gem that made my work with TFS a lot easier and enjoyable:

TFS Work Item Manager & TFS Project Dashboard

Go get it. It’s free.

Posted 03 December 09 10:58 by hgr | 0 Comments   
Filed under , ,
Running that VM Faster

I’ve been running Windows 7 for a while and one of the things that I decided to try is to have all the development environment on a virtual machine.

That’s because my Windows 7 is running on 64 bits and I still need to run a lot of things on 32 bits.

The problem with virtual machines on this scenario is performance.

This little tweak makes my VMWare virtual machine fly. So, if anyone is having issues, try this out.

mainmem.usenamedfile="FALSE"

Posted 21 October 09 12:16 by hgr | 0 Comments   
Filed under
GASP@Porto: RIA Services

This week – tomorrow (21) to be more precise – GASP will hold a new meeting here in our premises.

This session will be about RIA Services and will be conducted by Nuno Godinho, expert on Silverlight and RIA Services and ASP.NET MVP.

If anyone wants to join us in the discussion, feel free to request it at GASP LinkedIn Group.

Posted 20 October 09 02:41 by hgr | 0 Comments   
Filed under , ,
LOB

Great presentation about technologies (becoming) available for developing Line-of-Business Applications (whatever you may think that is…):

http://www.slideshare.net/simonguest/next-generation-lob-line-of-business-applications

Cool stuff there.

Posted 20 October 09 02:30 by hgr | 0 Comments   
Filed under ,
Embarrassing…

It’s absolutely incredible that I published only 6 posts this year.

The scenario is even worst if I think that 3 were just to put Feedburner running again and other 2 were just notes about something that as little to do with my work…

Well, this is the 7th… And it’s worst than the previous 6…

Well… management is really making me miserable. :)

Posted 25 September 09 11:44 by hgr | 1 Comments   
Filed under
Why Google Rocks…

Well, it’s not because of Google Docs, not because of Chrome, Wave, Mail, or whatever they do “outside the box”.

It’s just because of this:

Google

Google

The best search engine out there. Ever!

Posted 25 September 09 11:37 by hgr | 1 Comments   
Filed under
FeedBurner and the 512K Maximum File Size III

That’s it! The feed is back online.

Posted 10 July 09 10:54 by hgr | 0 Comments   
Filed under
FeedBurner and the 512K Maximum File Size II

It didn’t. I think that it’s just another post that needs to go...

Posted 10 July 09 10:50 by hgr | 0 Comments   
Filed under
FeedBurner and the 512K Maximum File Size

Apparently FeedBurner doesn’t swallow RSS sources that exceed 512K...

Since mine is now at 522K – I suppose because of the source code size in previous posts – this post is just a try to make the older posts go away so that the file size decreases... :)

Let’s hope it will work…

Posted 10 July 09 10:49 by hgr | 0 Comments   
Filed under
Add-in Express

These past weeks I’ve playing around with Excel add-ins.

Developing an Excel add-in with ribbon customizations, formulas, and task panes is not an easy task.

VSTO doesn’t help a lot. So I’ve been using this great toolset from Add-in Express:

Add-in Express

Try it out if you’re thinking of doing something with Office…

Posted 08 July 09 10:56 by hgr | 0 Comments   
Filed under ,
No Configuration Enterprise Library

From time to time I end up starting a new C# project and bumping into the same old infrastructure requirements like exception handling, logging, caching, etc.

At that point I always get back to Microsoft Enterprise Library... :)

I really like EntLib but this time I had other requirements that prevented me from having a configuration file.

So how the heck can you use EntLib without configuration? It turns out that it’s relatively easy but not so well documented, so that even Google had trouble finding me the solution. :)

I’ll leave it here also for future reference...

Scenario 1 – No Configuration Caching Block

Using the caching block with configuration is very simple. You create the configuration and then simply use the CacheFactory to create the manager. Something like this:

   1: ICacheManager productsCache = CacheFactory.GetCacheManager();
   2: productsCache.Add(product.ProductID, product)

To use it without configuration is a little harder.

First you need to use the CacheManagerFactory to create a factory passing in the configuration settings. Then you use that factory to create the manager.

The magic happens when you create your own class implementing the IConfigurationSource interface (available in Microsoft.Practices.EnterpriseLibrary.Common).

The final code is as follows:

   1: using Microsoft.Practices.EnterpriseLibrary.Caching.Configuration;
   2: using Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations;
   3:  
   4: namespace MyApplication
   5: {
   6:     internal class CacheServiceConfigurationSource : IConfigurationSource
   7:     {
   8:         #region Members
   9:  
  10:         private int expirationFrequencyInSeconds;
  11:         private int maximumElementsBeforeScavenging;
  12:  
  13:         #endregion
  14:  
  15:         #region Constructors
  16:  
  17:         private CacheServiceConfigurationSource()
  18:         {
  19:         }
  20:  
  21:         public CacheServiceConfigurationSource(int expirationFrequencyInSeconds, int maximumElementsBeforeScavenging)
  22:             : this()
  23:         {
  24:             this.expirationFrequencyInSeconds = expirationFrequencyInSeconds;
  25:             this.maximumElementsBeforeScavenging = maximumElementsBeforeScavenging;
  26:         }
  27:  
  28:         #endregion
  29:  
  30:         #region Public Methods
  31:  
  32:         public void Add(IConfigurationParameter saveParameter, string sectionName, ConfigurationSection configurationSection)
  33:         {
  34:         }
  35:  
  36:         public void AddSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
  37:         {
  38:         }
  39:  
  40:         public ConfigurationSection GetSection(string sectionName)
  41:         {
  42:             if (sectionName.Equals("cachingConfiguration", StringComparison.CurrentCulture))
  43:             {
  44:                 CacheManagerSettings result = new CacheManagerSettings();
  45:                 result.DefaultCacheManager = "DefaultCacheManager";
  46:  
  47:                 CacheStorageData storage = new CacheStorageData();
  48:                 storage.StorageEncryption = string.Empty;
  49:                 storage.Name = "NullStorage";
  50:                 storage.Type = typeof(NullBackingStore);
  51:                 result.BackingStores.Add(storage);
  52:  
  53:                 CacheManagerData manager = new CacheManagerData();
  54:                 manager.Name = "DefaultCacheManager";
  55:                 manager.NumberToRemoveWhenScavenging = 10;
  56:                 manager.MaximumElementsInCacheBeforeScavenging = this.maximumElementsBeforeScavenging;
  57:                 manager.ExpirationPollFrequencyInSeconds = this.expirationFrequencyInSeconds;
  58:                 manager.CacheStorage = "NullStorage";
  59:                 result.CacheManagers.Add(manager);
  60:  
  61:                 return result;
  62:             }
  63:  
  64:             return null;
  65:         }
  66:  
  67:         public void Remove(IConfigurationParameter removeParameter, string sectionName)
  68:         {
  69:         }
  70:  
  71:         public void RemoveSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
  72:         {
  73:         }
  74:  
  75:         #endregion
  76:     }
  77: }
  78:  
  79: using System;
  80: using System.Configuration;
  81: using System.Text;
  82: using Microsoft.Practices.EnterpriseLibrary.Caching;
  83: using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;
  84:  
  85: namespace MyApplication
  86: {
  87:     public static class CacheService
  88:     {
  89:         #region Members
  90:  
  91:         private static ICacheManager manager;
  92:  
  93:         #endregion
  94:  
  95:         #region Properties
  96:  
  97:         private static ICacheManager Manager
  98:         {
  99:             get
 100:             {
 101:                 if (manager == null)
 102:                 {
 103:                     CacheManagerFactory factory = new CacheManagerFactory(new CacheServiceConfigurationSource(Context.Current.Settings.Cache.ExpirationFrequencyInSeconds, Context.Current.Settings.Cache.MaximumElementsBeforeScavenging));
 104:                     manager = factory.CreateDefault();
 105:                 }
 106:  
 107:                 return manager;
 108:             }
 109:         }
 110:  
 111:         #endregion
 112:  
 113:         #region Public Methods
 114:  
 115:         public static void AddData<T>(string key, T value)
 116:         {
 117:             AddData<T>(key, value, Context.Current.Settings.Cache.ExpirationTimeInSeconds);
 118:         }
 119:  
 120:         internal static void AddData<T>(string key, T value, int expirationInSeconds)
 121:         {
 122:             // Expiration handler
 123:  
 124:             SlidingTime expirationHandler = new SlidingTime(new TimeSpan(0, 0, expirationInSeconds), DateTime.Now);
 125:  
 126:             // Add it
 127:  
 128:             Manager.Add(key, value, CacheItemPriority.Low, null, expirationHandler);
 129:         }
 130:  
 131:         #endregion
 132:     }
 133: }

Notice the GetSection method in CacheServiceConfigurationSource and the Manager property in the CacheService class.

Scenario 2 – No Configuration Logging Block

The logging application block is similar.

With configuration you use the Logger class.

Without configuration you must create a LogWriter using your own IConfigurationSource also.

   1: using System;
   2: using System.Configuration;
   3: using System.Text;
   4: using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
   5: using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
   6: using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
   7: using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
   8:  
   9: namespace MyApplication
  10: {
  11:     internal class LoggingConfigurationSource : IConfigurationSource
  12:     {
  13:         #region Public Methods
  14:  
  15:         public void Add(IConfigurationParameter saveParameter, string sectionName, ConfigurationSection configurationSection)
  16:         {
  17:         }
  18:  
  19:         public void AddSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
  20:         {
  21:         }
  22:  
  23:         public ConfigurationSection GetSection(string sectionName)
  24:         {
  25:             if (sectionName.Equals("loggingConfiguration", StringComparison.CurrentCulture))
  26:             {
  27:                 // Logging configuration
  28:  
  29:                 LoggingSettings result = new LoggingSettings();
  30:                 result.DefaultCategory = "General";
  31:                 result.TracingEnabled = true;
  32:                 result.LogWarningWhenNoCategoriesMatch = true;
  33:  
  34:                 // Listners
  35:  
  36:                 FormattedEventLogTraceListenerData listener = new FormattedEventLogTraceListenerData();
  37:                 listener.Name = "EventLogListener";
  38:                 listener.Source = "Office Extensions";
  39:                 listener.Formatter = "TextFormatter";
  40:                 listener.Log = "MyApplication";
  41:                 listener.MachineName = string.Empty;
  42:                 listener.TraceOutputOptions = System.Diagnostics.TraceOptions.None;
  43:                 listener.Filter = System.Diagnostics.SourceLevels.All;
  44:                 listener.ListenerDataType = typeof(FormattedEventLogTraceListenerData);
  45:                 listener.Type = typeof(FormattedEventLogTraceListener);
  46:                 result.TraceListeners.Add(listener);
  47:  
  48:                 TraceListenerReferenceData listenerReference = new TraceListenerReferenceData();
  49:                 listenerReference.Name = "EventLogListener";
  50:  
  51:                 // Formatters
  52:  
  53:                 StringBuilder template = new StringBuilder();
  54:                 template.AppendLine("Timestamp: {timestamp}");
  55:                 template.AppendLine("Message: {message}");
  56:                 template.AppendLine("Category: {category}");
  57:                 template.AppendLine("Priority: {priority}");
  58:                 template.AppendLine("EventId: {eventid}");
  59:                 template.AppendLine("Severity: {severity}");
  60:                 template.AppendLine("Title:{title}");
  61:                 template.AppendLine("Machine: {machine}");
  62:                 template.AppendLine("Application Domain: {appDomain}");
  63:                 template.AppendLine("Process Id: {processId}");
  64:                 template.AppendLine("Process Name: {processName}");
  65:                 template.AppendLine("Win32 Thread Id: {win32ThreadId}");
  66:                 template.AppendLine("Thread Name: {threadName}");
  67:                 template.AppendLine("Extended Properties: {dictionary({key} - {value})}");
  68:  
  69:                 TextFormatterData formatter = new TextFormatterData();
  70:                 formatter.Name = "TextFormatter";
  71:                 formatter.Type = typeof(TextFormatter);
  72:                 formatter.Template = template.ToString();
  73:                 result.Formatters.Add(formatter);
  74:  
  75:                 // Category Sources
  76:  
  77:                 TraceSourceData exceptionsSource = new TraceSourceData();
  78:                 exceptionsSource.TraceListeners.Add(listenerReference);
  79:                 exceptionsSource.Name = "Exceptions";
  80:                 exceptionsSource.DefaultLevel = System.Diagnostics.SourceLevels.All;
  81:                 result.TraceSources.Add(exceptionsSource);
  82:  
  83:                 TraceSourceData traceSource = new TraceSourceData();
  84:                 traceSource.TraceListeners.Add(listenerReference);
  85:                 traceSource.Name = "Trace";
  86:                 traceSource.DefaultLevel = System.Diagnostics.SourceLevels.All;
  87:                 result.TraceSources.Add(traceSource);
  88:  
  89:                 TraceSourceData generalSource = new TraceSourceData();
  90:                 generalSource.TraceListeners.Add(listenerReference);
  91:                 generalSource.Name = "General";
  92:                 generalSource.DefaultLevel = System.Diagnostics.SourceLevels.All;
  93:                 result.TraceSources.Add(generalSource);
  94:  
  95:                 // Special Sources
  96:  
  97:                 result.SpecialTraceSources.AllEventsTraceSource.Name = "AllEvents";
  98:                 result.SpecialTraceSources.AllEventsTraceSource.DefaultLevel = System.Diagnostics.SourceLevels.All;
  99:  
 100:                 result.SpecialTraceSources.NotProcessedTraceSource.Name = "NotProcessed";
 101:                 result.SpecialTraceSources.NotProcessedTraceSource.DefaultLevel = System.Diagnostics.SourceLevels.All;
 102:  
 103:                 result.SpecialTraceSources.ErrorsTraceSource.Name = "LoggingErrorsAndWarnings";
 104:                 result.SpecialTraceSources.ErrorsTraceSource.DefaultLevel = System.Diagnostics.SourceLevels.All;
 105:                 result.SpecialTraceSources.ErrorsTraceSource.TraceListeners.Clear();
 106:                 result.SpecialTraceSources.ErrorsTraceSource.TraceListeners.Add(listenerReference);
 107:  
 108:                 // Return
 109:  
 110:                 return result;
 111:             }
 112:  
 113:             return null;
 114:         }
 115:  
 116:         public void Remove(IConfigurationParameter removeParameter, string sectionName)
 117:         {
 118:         }
 119:  
 120:         public void RemoveSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
 121:         {
 122:         }
 123:  
 124:         #endregion
 125:     }
 126: }
 127:  
 128: using System;
 129: using System.Diagnostics;
 130: using System.Globalization;
 131: using Microsoft.Practices.EnterpriseLibrary.Logging;
 132:  
 133: namespace MyApplication
 134: {
 135:     public static class LoggingHandler
 136:     {
 137:         #region Constants
 138:  
 139:         private const string CategoryExceptions = "Exceptions";
 140:         private const int PriorityExceptions = 1000;
 141:         private const int EventIdExceptions = 1000;
 142:         private const string TitleExceptions = "Errors Log";
 143:         private const TraceEventType SeverityExceptions = TraceEventType.Error;
 144:  
 145:         #endregion
 146:  
 147:         #region Members
 148:  
 149:         private static LogWriter writer;
 150:  
 151:         #endregion
 152:  
 153:         #region Properties
 154:  
 155:         internal static LogWriter Writer
 156:         {
 157:             get
 158:             {
 159:                 if (writer == null)
 160:                 {
 161:                     writer = CreateWriter();
 162:                 }
 163:  
 164:                 return writer;
 165:             }
 166:         }
 167:  
 168:         #endregion
 169:  
 170:         #region Public Methods
 171:  
 172:         public static void Exception(Exception ex)
 173:         {
 174:             // Validation
 175:  
 176:             if (ex == null)
 177:             {
 178:                 throw new ArgumentNullException();
 179:             }
 180:  
 181:             // Log
 182:  
 183:             WriteEntry(CategoryExceptions, TitleExceptions, ex.ToString(), PriorityExceptions, EventIdExceptions, SeverityExceptions);
 184:         }
 185:  
 186:         #endregion
 187:  
 188:         #region Private Methods
 189:  
 190:         private static void WriteEntry(string category, string title, string message, int priority, int eventId, TraceEventType severity)
 191:         {
 192:             LogEntry entry = new LogEntry();
 193:             entry.Categories.Add(category);
 194:             entry.TimeStamp = DateTime.Now;
 195:             entry.EventId = eventId;
 196:             entry.Message = message;
 197:             entry.Priority = priority;
 198:             entry.Severity = severity;
 199:             entry.Title = title;
 200:  
 201:             Writer.Write(entry);
 202:         }
 203:  
 204:         private static LogWriter CreateWriter()
 205:         {
 206:             LogWriterFactory factory = new LogWriterFactory(new LoggingConfigurationSource());
 207:             return factory.Create();
 208:         }
 209:  
 210:         #endregion
 211:     }
 212: }

I hope this helps someone in the future.

It turns out that the greater strength of EntLib – configuration – is also, at least in my opinion, its greater weakness. Just think of how many “real-world applications” would you want to have the user messing with the configuration file to setup event viewer listeners and things like that. :)

DSL Tools #14 - Double-click in the Model Explorer

Every DSL Tools project generates this handy model explorer where you can browse the domain classes that are on your domain model.

Something like this:

Model Explorer Preview

This is particularly helpful for example to change the properties of a particular domain class. You browse for in the explorer, click on it, and simply edit the properties.

Now there is a fundamental feature that the guys at Microsoft missed in the explorer. The ability to double click a domain class there and have the domain model view show that domain class centered. A feature very useful if you have a model with a few dozens of domains classes (which is very typical).

So I put my mind to implement such behavior on my various DSL explorers. It turns out that it isn't very difficult.

1. Determine the explorer class name

The model explorer class is generated as part of your DSLPackage project. The name of the class depends on the name of your DSL. To find out the correct name, browse the DSLPackage project, under GeneratedCode, and you'll find a file named ModelExplorer.cs. The name of the explorer is the name of the first class on that file.

In my example that is UserInterfaceModelExplorer:

   1: /// <summary>
   2: /// Double-derived class to allow easier code customization.
   3: /// </summary>
   4: internal partial class UserInterfaceModelExplorer : UserInterfaceModelExplorerBase
   5: {
   6:     /// <summary>
   7:     /// Constructs a new UserInterfaceModelExplorer.
   8:     /// </summary>
   9:     public UserInterfaceModelExplorer(global::System.IServiceProvider serviceProvider)
  10:         : base(serviceProvider)
  11:     {
  12:     }
  13: }

2. Create a custom class with that same name

Under the DSLPackage, you will need to create a new custom partial class with the name of your model explorer. All the magic will happen here.

3. Override the CreateElementVisitor method

Here you should subscribe to the ObjectModelBrowser.DoubleClick event:

   1: /// <summary>
   2: /// Executed when the model explorer creates the tree element visitor.
   3: /// </summary>
   4: /// <returns>The tree element visitor.</returns>
   5: protected override IElementVisitor CreateElementVisitor()
   6: {
   7:     // Subscribe double click in the tree view
   8:  
   9:     this.ObjectModelBrowser.DoubleClick += new EventHandler(this.ObjectModelBrowser_DoubleClick);
  10:  
  11:     // Default behavior
  12:  
  13:     return base.CreateElementVisitor();
  14: }

4. Implement the corresponding event handler

The magic consists in:

  • First, finding the element that is selected in the explorer,
  • Second, getting the corresponding model element,
  • And, finally, selecting that shape in the model viewer.

Check the full source code bellow for the implementation of these steps.

5. Transform All templates

Before you test the new behavior of the model explorer, you'll need to transform all the templates to let the DSL know that you have customized your model explorer.

And that's it. Double-click any domain class in the explorer and the model viewer will be updated accordingly.

Full Source Code

   1: using System;
   2: using System.Collections.ObjectModel;
   3: using Microsoft.VisualStudio.Modeling;
   4: using Microsoft.VisualStudio.Modeling.Diagrams;
   5: using Microsoft.VisualStudio.Modeling.Shell;
   6:  
   7: namespace MyDSL.UserInterface
   8: {
   9:     /// <summary>
  10:     /// Custom behavior for UserInterfaceModelExplorer.
  11:     /// </summary>
  12:     internal partial class UserInterfaceModelExplorer
  13:     {
  14:         #region Public Methods
  15:  
  16:         /// <summary>
  17:         /// Executed when the model explorer creates the tree element visitor.
  18:         /// </summary>
  19:         /// <returns>The tree element visitor.</returns>
  20:         protected override IElementVisitor CreateElementVisitor()
  21:         {
  22:             // Subscribe double click in the tree view
  23:  
  24:             this.ObjectModelBrowser.DoubleClick += new EventHandler(this.ObjectModelBrowser_DoubleClick);
  25:  
  26:             // Default behavior
  27:  
  28:             return base.CreateElementVisitor();
  29:         }
  30:  
  31:         #endregion
  32:  
  33:         #region Private Methods
  34:  
  35:         /// <summary>
  36:         /// Selects the specified shape using the given modeling document data.
  37:         /// </summary>
  38:         /// <param name="shapeElement">The shape element that should be selected.</param>
  39:         /// <param name="docData">The modeling document data.</param>
  40:         private static void SelectShape(ShapeElement shapeElement, DocData docData)
  41:         {
  42:             // Validation
  43:  
  44:             if (shapeElement == null)
  45:             {
  46:                 throw new ArgumentNullException("shapeElement");
  47:             }
  48:  
  49:             if (docData == null)
  50:             {
  51:                 throw new ArgumentNullException("docData");
  52:             }
  53:  
  54:             // Select the shape
  55:  
  56:             ModelingDocView docView = docData.DocViews[0];
  57:             if (docView != null)
  58:             {
  59:                 docView.SelectObjects(1, new object[] { shapeElement }, 0);
  60:             }
  61:         }
  62:         
  63:         /// <summary>
  64:         /// Gets the first shape the represents the specified model element.
  65:         /// </summary>
  66:         /// <param name="modelElement">The model element whose shape will be returned.</param>
  67:         /// <returns>The first shape the represents the specified model element.</returns>
  68:         private static ShapeElement GetModelElementFirstShape(ModelElement modelElement)
  69:         {
  70:             // Presentation elements
  71:  
  72:             LinkedElementCollection<PresentationElement> presentations = PresentationViewsSubject.GetPresentation(modelElement);
  73:             foreach (ModelElement element in presentations)
  74:             {
  75:                 ShapeElement shapeElement = (element as ShapeElement);
  76:                 if (shapeElement != null)
  77:                 {
  78:                     return shapeElement;
  79:                 }
  80:             }
  81:  
  82:             // Default result
  83:  
  84:             return null;
  85:         }
  86:  
  87:         /// <summary>
  88:         /// Return the model element which is the parent of the specified model element considering that
  89:         /// this element is placed in a compartment.
  90:         /// </summary>
  91:         /// <param name="modelElement">The model element whose parent will be returned.</param>
  92:         /// <returns>
  93:         /// The model element which is the parent of the specified model element considering that
  94:         /// this element is placed in a compartment.
  95:         /// </returns>
  96:         private static ModelElement GetCompartmentElementFirstParent(ModelElement modelElement)
  97:         {
  98:             // Get the domain class associated with model element.
  99:  
 100:             DomainClassInfo domainClass = modelElement.GetDomainClass();
 101:             if (domainClass != null)
 102:             {
 103:                 // A element is only considered to be in a compartment if it participates in only 1 embedding relationship
 104:                 // This might be wrong for some models
 105:  
 106:                 if (domainClass.AllEmbeddedByDomainRoles.Count == 1)
 107:                 {
 108:                     // Get a collection of all the links to this model element
 109:                     // Since this is in a compartment there will only be one
 110:  
 111:                     ReadOnlyCollection<ElementLink> links = DomainRoleInfo.GetAllElementLinks(modelElement);
 112:                     if (links.Count == 1)
 113:                     {
 114:                         // Get the model element participating in the link that isn't the current one
 115:                         // That will be the parent
 116:                         // Probably there is a better way to achieve the same result
 117:  
 118:                         foreach (ModelElement linkedElement in links[0].LinkedElements)
 119:                         {
 120:                             if (!modelElement.Equals(linkedElement))
 121:                             {
 122:                                 return linkedElement;
 123:                             }
 124:                         }
 125:                     }
 126:                 }
 127:             }
 128:  
 129:             // Default result
 130:  
 131:             return null;
 132:         }
 133:  
 134:         #endregion
 135:  
 136:         #region Event Handlers
 137:  
 138:         private void ObjectModelBrowser_DoubleClick(object sender, EventArgs e)
 139:         {
 140:             // Get the node selected in the model explorer tree
 141:  
 142:             ModelElementTreeNode node = (this.ObjectModelBrowser.SelectedNode as ModelElementTreeNode);
 143:             if (node != null)
 144:             {
 145:                 // Get the corresponding model element
 146:  
 147:                 ModelElement element = node.ModelElement;
 148:                 if (element != null)
 149:                 {
 150:                     // Get the corresponding shape
 151:                     // If the model element is in a compartment the result will be null
 152:  
 153:                     ShapeElement shape = GetModelElementFirstShape(element);
 154:                     if (shape == null)
 155:                     {
 156:                         // If the element is in a compartment, try to get the parent model element to select that
 157:  
 158:                         ModelElement parentElement = GetCompartmentElementFirstParent(element);
 159:                         if (parentElement != null)
 160:                         {
 161:                             // Get the corresponding shape
 162:  
 163:                             shape = GetModelElementFirstShape(parentElement);
 164:                         }
 165:                     }
 166:  
 167:                     // Select the shape
 168:  
 169:                     if (shape != null)
 170:                     {
 171:                         SelectShape(shape, this.ModelingDocData);
 172:                     }
 173:                 }
 174:             }
 175:         }
 176:  
 177:         #endregion
 178:     }
 179: }

Special thanks are in order to Oleg Sych for his fundamental tips to get this working.

Posted 16 December 08 04:22 by hgr | 1 Comments   
Filed under ,
More Posts Next page »