Skip to content

Companies

TextUML Toolkit 1.6 RC1 is now available

abstratt: news from the front - Tue, 07/27/2010 - 00:21

TextUML Toolkit 1.6 RC1 is now available! You can install it using the Marketplace Client or by pointing Eclipse to the update site:

http://abstratt.com/update

If you find any problems installing this build, please let us know asap so it can be addressed before the final release.

New features

Much of the work in this release went into improving the model building infrastructure to be even more notation agnostic. That work is still ongoing and should be completed in 1.7. But there were plenty of user-facing feature additions as well:

  • preconditions on operations (2986923 and 3002571)
  • support for a default notation (so file extensions can be optional) (2995372)
  • support for implicitly applying profiles/stereotypes (so models are less verbose) (2981580)
  • support for derived properties (2928428)
  • support for initial values in properties (2115439)
  • advanced features (closures, constraints) are now implemented using profiles instead of metamodel extensions (2933692)

In other news

The reason it took so long for a new TextUML Toolkit release to come about was that I have been busy working on AlphaSimple, which went on public beta today. AlphaSimple is an online tool for domain-driven prototyping that currently uses TextUML as modeling notation. Thus, AlphaSimple is also the driving force behind most of the changes that happened in the 1.6 cycle, and you can try them right away by starting a guest session and studying the example projects.

Categories: Companies

MDL File importer

CapableObjects - Sun, 04/11/2010 - 19:47

This is the third and last post on the Rational rose file importer.

Having this Rational Rose model:

image

I can now get this Modlr model:

image

The code is in the demos folder: PluginRoseFileRead

More importantly I tested this on a model I worked on some years ago for the swedish parliament:

image

It took almost two minutes to import it.

Notice to round magnifier in the diagram overview – we added this to Modlr some weeks ago, looks cool.

Categories: Companies

From Goals to Use Cases

The Use Case Blog - CaseComplete - Wed, 04/07/2010 - 23:30

Here’s a common scenario for many of us who have learned about uses cases but haven’t written one yet: You’ve identified the actors for your project. You’ve brainstormed a list of goals for each actor. You’re ready to create your first set of use cases. And you feel stuck. Or at least uncertain. Call it use case writer’s block. You could point to a use case if you saw one, but what are your use cases?

When this happens, remember that use cases are based on actor goals. If a use case doesn’t describe how an actor gets something done, it’s not really a use case. Does that mean that every goal becomes use case? Not necessarily. Goals address needs at many levels – from very high levels to very low levels and infinite shades in between. Consider the following goals:iStock_000007623639XSmall-resize

  • Relax
  • Plan a vacation
  • Reserve a room
  • Find a resort
  • Pick a destination
  • Type the name of a city into a text box
  • Open a web browser

These are all related goals, but at different levels. Not all of them will make for a good use case. Pick a high level goal and the use case will either be too big to comprehend or too imprecise to guide decisions. Pick a low level goal and the use case will be too small and you’ll need too many of them to describe the system. This is starting to sound like a fairy tale involving three bears, but – you guessed it – the best goals are neither too high, nor too low, but just right.

If you’re wondering what goal levels are just right, here’s a more concrete guideline from Craig Larman’s Applying UML and Patterns: Write use cases at the level of the elementary business process (EBP):

A task performed by one person in one place at one time, in response to a business event, which adds measurable business value and leaves the data in a consistent state.

There are exceptions to this guideline, of course. You might want a high-level use case that puts context around the EBP use cases. You might occasionally want a low-level use case that describes a complex sub-process. When you’re identifying the bulk of your use cases, however, most will address goals at the EBP level – something one actor can complete in one place at one time.

To identify the use cases in your system, look at the list of goals you’ve brainstormed. Is each goal written at the elementary business process level? For goals that are written at a level lower than a business event, consider why the actor wants to achieve that goal. It’s probably just a step towards a higher level goal. For goals that are written at a level higher than a business event, consider how the actor would go about achieving that goal. There’s probably an intermediate step that has more business meaning in the system you’re defining.

So, back to our use case writer’s block. You’ve just broken through. Each of the EBP-level goals you identified answers the question: What are your use cases?

Categories: Companies

Declarative ViewModels and Taborder

CapableObjects - Sat, 03/27/2010 - 14:40

A very good friend of mine, Lars Olofsson, complained about the tab-order between controls in the ViewModels. The project he is working on uses the ViewModels A LOT. And as the work of developing this system goes on, little changes to the ViewModels get applied and the Taborder gets messier and messier.

He suggested that the Taborder should be made visible to the eye so that anyone touching the viewmodel definition could see if it was ok or not. That was a brilliant idea – why have it not always been that way:

ViewModel definition

Categories: Companies

Rational Rose Modeler

CapableObjects - Sat, 03/27/2010 - 13:55

So I had set out for doing the RationalRose file-format reader for Modlr. As I discussed earlier I was intimidated by the non-xml-file-format, and discouraged by the details in the format.

So I first made some logic that turned it into XML, then I made a plugin to Modlr that could deduce a Model from XML… So there is no escaping this I guess – I need to actually read and interpret the data…

BTW If anyone wonders why I have not done this until now it was because I was skiing – first in Sälen, Sweden – brilliant weather, ok hills, then in BadGastain, Austria – ok weather , brilliant hills!

   1: RoseImport ri = new RoseImport();  // This reads the mdl and turns it into XML
   2: ri.OpenFile(textBoxFileName.Text, true);
   3: _es = EcoSpace;
   4: _createdobjects = new Dictionary<string, IEcoObject>();
   5:  
   6: InstansiateObjects(ri.XDocument);       // This should interpret the xml content and match it with the model I have
   7: foreach (XDocument xd in ri.CatFiles)   // RoseImport also find CAT file references and reads those too
   8: {
   9:     InstansiateObjects(xd);
  10: }

Since I am lazy, stressed and living in Sweden, the country with the highest taxes in the world, I really need to be efficient about this…

My model was deduced from the XML so I am pretty sure that the names in the XML will be the same in my model.

Using the meta information from eco I can go like this to instantiate the correct objects:

   1: IEcoObject existing = null;
   2: XElement keyelement = xe.Elements("key") as XElement;
   3: if (keyelement != null && keyelement.Value != null)
   4: {
   5:     string Key = keyelement.Value;
   6:     if (_createdobjects.ContainsKey(keyelement.Value))
   7:         existing = _createdobjects[keyelement.Value];
   8: }
   9: if (existing == null)
  10: {
  11:     IClass iclass = EcoServiceHelper.GetTypeSystemService(_es).TypeSystem.
  12:             GetClassifierByName("ro_" + xe.Name.LocalName) as IClass; // My model prefixed all rose classes with ro_
  13:     existing = EcoServiceHelper.GetObjectFactoryService(_es).CreateNewObject(iclass);
  14:     if (keyelement != null && keyelement.Value != null)
  15:     {
  16:         _createdobjects.Add(keyelement.Value, existing);
  17:     }
  18:  
  19: }

Ok , moving on to the attributes:

   1: foreach (XElement child in xe.Elements())
   2: {
   3:     if (!child.HasElements)
   4:     {
   5:         IElement elem = existing.AsIObject().Properties[child.Name.LocalName];
   6:         if (elem != null && !(elem is IObjectList))
   7:             elem.SetValue<string>(child.Value);
   8:     }
   9:     else
  10:     {
  11:         // load association
  12:     }
  13: }
If my model had better typed values, like double, int, etc I would need to work on the parsing, but I am fine with strings.

And the associations:

   1: // load association
   2: IElement elem = existing.AsIObject().Properties[child.Name.LocalName];
   3: if (elem is IObjectList)
   4: {
   5:     foreach (XElement associationobject in child.Elements())
   6:     {
   7:         IEcoObject associaionInstansiatedObject=InstansiateObjects(associationobject);                       
   8:         if (associaionInstansiatedObject.AsIObject().UmlClass.ConformsTo((elem as IObjectList).UmlClass))
   9:             (elem as IObjectList).Add(associaionInstansiatedObject);
  10:         else

11: System.Diagnostics.Debug.WriteLine(string.Format("Tried to add a {0} to a list of {1}, association was {2}",

associaionInstansiatedObject.AsIObject().UmlClass.Name,

(elem as IObjectList).UmlClass.Name, child.Name.LocalName));

  12:     }
  13: }

Note the last line where I check if the loaded data actually will be valid to add in this association… Since my model was deduced from XML it is probably lacking important abstractions (inheritance). And sure enough, running this on a mdl file gave me this output:

   1: Tried to add a ro_Class to a list of ro_Class_Category, association was logical_models
   2: Tried to add a ro_Class to a list of ro_Class_Category, association was logical_models
   3: Tried to add a ro_Class to a list of ro_Class_Category, association was logical_models
   4: Tried to add a ro_Class to a list of ro_Class_Category, association was logical_models
   5: Tried to add a ro_Class to a list of ro_Class_Category, association was logical_models
   6: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
   7: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
   8: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
   9: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
  10: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
  11: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
  12: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
  13: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
  14: Tried to add a ro_Association to a list of ro_Class_Category, association was logical_models
  15: Tried to add a ro_ClassDiagram to a list of ro_UseCaseDiagram, association was logical_presentations

 

This leads me to believe that the association logical_models really should point to some abstraction rather than to the ro_Class_Category itself… Keep in mind that I know nothing about what all this mean yet, I am just as ignorant to stuff maintained by IBM as IBM is of me (wow that is a lot).

So I change the model by introducing a superclass called LogicalModel (made sense since the association was called that):

image

BTW: I moved around the attributes (removing them from the subclasses to the superclass) with the newly implemented copy-paste function of the Modlr-tree (Yippy!).

I found some more missing abstractions that I added. Finally the output list was empty. No more errors.

At this point I need to actually understand what it is I am doing – after all I am supposed to understand the content of the rose file in order to turn the artifacts within into modlr objects (defined by, what we call, extendedmodellayer).

No better way than to use the EcoSpaceDebugger and autoforms. Load my sample mdl again:

image

There is a ro_ClassDiagram that looks interesting:

image

…it has a multi link called Items that contain ro_ClassView and ro_AssociationView…

Looking closer in the ro_ClassView – I guessing that this is what ModelLayer calls PlacedClass:

image

…I wonder how if finds the ro_class… I am guessing it uses the quidu reference…

Using the ecospace debugger I wrote this ocl: ro_Class.allinstances->select(quid='"3BDC6B1A02D7"') and sure enough:

image

So I will amend the model to keep this knowledge in a derived association.

The Derivation expression is “ro_Class.allinstances->select(quid=self.quidu)->first”

image

Not only does the derived association help me when looking in autoforms and writing code; it also ensures that things stay optimized. A derived association is subscribed to by the eco-runtime, and the value is cached until it changes. And when it changes it is merely marked as out of date, so it is only updated if someone tries to read it again.

It is easy to imagine that I would write code and look up the ro_Class from the ro_ClassView.quidu whenever I needed it; but the derived association removes the need for repetitive, error prone, performance hurting, hard to find code and brings it up in the light, as a first class member of the ro_ClassView ready to serve whenever I need it – available in the model for everyone to see. How beautiful is that?

So I am not going to bore you with all the details – I will continue to work on this – the main purpose of this post was to explain how I use eco to move away from time consuming hand coding techniques, towards building and using tools to get the job done.

Get out of the middle ages – become model driven.

Categories: Companies

Strongly typed thank you!

CapableObjects - Sun, 02/21/2010 - 19:46

This weekend I set out for doing a Rational Rose file import plugin for Modlr. I wrote the code that successfully traversed the rather strange file format that they use. Rational Rose is a pre-xml product:

   1:  
   2: (object Petal
   3:     version        47
   4:     _written       "Rose 8.0.0303.1400"
   5:     charSet        0)
   6:  
   7: (object Design "Logical View"
   8:     is_unit        TRUE
   9:     is_loaded      TRUE
  10:     quid           "4B7FCC8D02EF"
  11:     defaults       (object defaults
  12:     rightMargin     0.250000
  13:     leftMargin     0.250000
  14:     topMargin      0.250000
  15:     bottomMargin     0.500000
  16:     pageOverlap     0.250000
  17:     clipIconLabels     TRUE
  18:     autoResize     TRUE
  19:     snapToGrid     TRUE
  20:     gridX          0
  21:     gridY          0
  22:     defaultFont     (object Font
  23:         size           12
  24:         face           "Arial"
  25:         bold           FALSE
  26:         italics        FALSE
  27:         underline      FALSE
  28:         strike         FALSE
  29:         color          0
  30:         default_color     TRUE)
  31:     showMessageNum     3
  32:     showClassOfObject     TRUE
  33:     notation       "Unified")
  34:     root_usecase_package     (object Class_Category "Use Case View"
  35:     quid           "4B7FCC8D02F1"

So when I had all that done I thought that it would an easy and quick task to wrap that information into a model designed in eco so that I could populate it with objects from the file. Once that was done I planned to write the code that transformed from that rose model to the eco modellayer model…

But I immediately got super tired when I realized that I had to create some 30-40 classes with a lot of attributes and relations all by hand in order to hold the rational rose data from the file…

It seemed silly that I would need to do stuff by hand at all. After all we are the tool making species! So I started to think that this is a common scenario – I have a description, or data adhering to a description, in one format, and I want to have that as a model so I can code towards it with strongly typed classes ( the older (and wiser?) I get, I get increasingly more allergic to scripting ).

So if this is a common problem that at least I come across from time to time maybe I should solve that first?

I asked myself what format the data I need to handle usually come in : XML.

So to avoid losing focus of my initial problem all together I used my Rational Rose File decoder logic and made it spit out the same information as XML:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <root>
   3:   <Petal>
   4:     <name></name>
   5:     <key></key>
   6:     <version>47</version>
   7:     <_written>"Rose 8.0.0303.1400"</_written>
   8:   </Petal>
   9:   <Design>
  10:     <name>Logical View</name>
  11:     <key></key>
  12:     <is_unit>TRUE</is_unit>
  13:     <is_loaded>TRUE</is_loaded>
  14:     <quid>"4B7FCC8D02EF"</quid>
  15:     <defaults>
  16:       <defaults>
  17:         <name></name>
  18:         <key></key>
  19:         <rightMargin>0.250000</rightMargin>
  20:         <leftMargin>0.250000</leftMargin>
  21:         <topMargin>0.250000</topMargin>
  22:         <bottomMargin>0.500000</bottomMargin>
  23:         <pageOverlap>0.250000</pageOverlap>
  24:         <clipIconLabels>TRUE</clipIconLabels>
  25:         <autoResize>TRUE</autoResize>
  26:         <snapToGrid>TRUE</snapToGrid>
  27:         <gridX>0</gridX>
  28:         <gridY>0</gridY>
  29:         <defaultFont>
  30:           <Font>
  31:             <name></name>

The general idea was to write a plugin that given this XML, or a similar XML, or preferably any XML, could derive a model for me, saving me from doing stuff manually.

I know that some of you are thinking “oh boy this guy is writing a plugin in order to write a plugin in order to convert a rose file?”, and yes you are correct. But one plugin will be very generic – taking almost any XML and turning it into a modlr model.

So nothing fancy:

   1: XDocument xd = XDocument.Load(file);
   2: foreach (XElement xe in xd.Root.Elements())
   3: {
   4:     Analyze(xe);
   5: }

And the magic of the plugin:

   1: private Eco.ModelLayer.Class Analyze(XElement xe)
   2: {
   3:     Eco.ModelLayer.Class c = EnsureClass(GetNameFromXE(xe));
   4:     foreach (XAttribute xa in xe.Attributes())
   5:     {
   6:         EnsureAttribute(c, xa.Name.ToString());
   7:     }
   8:     foreach (XElement subxe in xe.Elements())
   9:     {
  10:  
  11:         if (subxe.FirstNode is XText || subxe.FirstNode==null)
  12:         {
  13:             // treat as model attribute
  14:             EnsureAttribute(c, GetNameFromXE(subxe));
  15:         }
  16:         else
  17:         {
  18:             // treat as model association
  19:             /*
  20:                 We treat to types of linking:
  21:                  single link may look like this:
  22:                      obj1
  23:                         attr1
  24:                         link
  25:                             attr1
  26:              
  27:              *   multilink often looks like this:
  28:                      obj1
  29:                         attr1
  30:                         link
  31:                             obj2
  32:                                attr1
  33:              */

34: if (subxe.Elements().Count() > 0 &&

(subxe.Elements().First().FirstNode is XText || subxe.Elements().First().FirstNode == null))

  35:             {
  36:                 Eco.ModelLayer.Class targetclass = Analyze(subxe);
  37:                 AssociationEnd ae = EnsureAssociation(c, targetclass, GetNameFromXE(subxe));
  38:                 if (ae!=null)
  39:                     ae.Multiplicity = "0..1";
  40:             }
  41:             else
  42:             {
  43:                 List<Eco.ModelLayer.Class> classesfoundinrelation = new List<Eco.ModelLayer.Class>();
  44:                 foreach (XElement xeObjectInAssoc in subxe.Elements())
  45:                 {
  46:                     Eco.ModelLayer.Class targetclass = Analyze(xeObjectInAssoc);
  47:                     if (classesfoundinrelation.IndexOf(targetclass) == -1)
  48:                         classesfoundinrelation.Add(targetclass);
  49:                 }
  50:  
  51:                 if (classesfoundinrelation.Count > 0)
  52:                 {

53: // if classesfoundinrelation.Count>1 the association is most likely

pointing to a super class of the classes found, but we dont have enough info - pick first

  54:                     AssociationEnd ae = EnsureAssociation(c, classesfoundinrelation[0], GetNameFromXE(subxe));
  55:                     if (ae!=null)
  56:                         ae.Multiplicity = "0..*";
  57:                 }
  58:             }
  59:         }
  60:     }
  61:     return c;
  62: }
  63:  

The logic to actually create a Class, an Attribute or a Relation looks like this:

   1: private AssociationEnd EnsureAssociation(Eco.ModelLayer.Class cfrom, Eco.ModelLayer.Class cto, string xName)
   2: {
   3:  
   4:     if (_esp != null)
   5:     {
   6:         var res = (from x in cfrom.AssociationEnd where x.OtherEnd != null && x.OtherEnd.Name == xName select x);
   7:         if (res.Count() == 0)
   8:         {
   9:             AssociationEnd ae1=new AssociationEnd(_esp);
  10:             AssociationEnd ae2 = new AssociationEnd(_esp);
  11:             Association a = new Association(_esp);
  12:             a.AssociationEnd.Add(ae1);
  13:             a.AssociationEnd.Add(ae2);
  14:             a.Package_ = _package;
  15:             ae2.Name = xName;
  16:             ae2.IsNavigable = true;
  17:             cfrom.AssociationEnd.Add(ae1);
  18:             cto.AssociationEnd.Add(ae2);
  19:             return ae2;
  20:         }
  21:         return res.First().OtherEnd;
  22:     }
  23:     return null;
  24: }
  25:  
  26: private void EnsureAttribute(Eco.ModelLayer.Class c, string xName)
  27: {
  28:     if (_esp != null)
  29:     {
  30:         foreach (ModelElement me in c.AllFeatures)
  31:         {
  32:             if (me is Eco.ModelLayer.Attribute)
  33:             {
  34:                 if (xName == (me as Eco.ModelLayer.Attribute).Name)
  35:                 {
  36:                     return;
  37:                 }
  38:             }
  39:         }
  40:         // not found
  41:  
  42:         Eco.ModelLayer.Attribute a=new Eco.ModelLayer.Attribute(_esp);
  43:         a.Name = xName;
  44:         if (_stringDataType==null)

45: _stringDataType=(from x in _esp.GetEcoService<IExtentService>().AllInstances<Datatype>()

where x.Name.ToLower()=="string" select x).First<Datatype>();

  46:         a.Type = _stringDataType;
  47:         c.Feature.Add(a);
  48:  
  49:     }
  50: }
  51:  
  52: private Eco.ModelLayer.Class EnsureClass(string xName)
  53: {
  54:     if (_esp != null)
  55:     {
  56:         string completename = _prefix + xName;
  57:         var result = (from x in _esp.GetEcoService<IExtentService>().AllInstances<Eco.ModelLayer.Class>() 
  58:                       where (x.Name == completename && x.OwningPackage == _package) select x).ToArray<Eco.ModelLayer.Class>();
  59:         if (result.Count() == 0)
  60:         {
  61:             Eco.ModelLayer.Class c = new Eco.ModelLayer.Class(_esp);
  62:             c.Name = completename;
  63:             c.Package_=_package;
  64:             return c;
  65:         }
  66:         else
  67:             return result[0];                
  68:     }
  69:     return null;
  70: }
  71:     }

I tried the logic on several different xml files that I found in my temp folder and found some special cases. This lead me to the conclusion that I needed at least two different strategies: one where the xml element has a generic name and the real name is given in some attribute. One example of this is the EcoMdl file format:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <contents>
   3:   <object type="Diagram" href="ClassDiagram_Diagram1">
   4:     <doc><![CDATA[]]></doc>
   5:     <modlrdiagram>diagramimages/efcd3388-264f-469f-b6e2-5e0a1dc68839.jpg</modlrdiagram>
   6:     <attribute name="ColorOnNew">0</attribute>
   7:     <attribute name="Id">efcd3388-264f-469f-b6e2-5e0a1dc68839</attribute>
   8:     <attribute name="SnapGridSize">15</attribute>
   9:     <attribute name="PresentationName">Diagram1</attribute>
  10:     <attribute name="ShowMethodSignatures">False</attribute>
  11:     <attribute name="ShowAssociationNames">DimDefaults</attribute>
  12:     <attribute name="ShowVisibility">False</attribute>
  13:     <attribute name="SquareNewAssociations">False</attribute>
  14:     <attribute name="SquareNewGeneralizations">False</attribute>
  15:     <attribute name="ShowMethodReturnTypes">False</attribute>
  16:     <attribute name="Name">Diagram1</attribute>
  17:     <link name="PlacedClass">
  18:       <object type="PlacedClass" href="Class1">
  19:         <attribute name="RenderedWidth">200,8</attribute>
  20:         <attribute name="id">0aac4fb8-5dd6-4082-b16b-73c07565fc57</attribute>
  21:         <attribute name="Color">1692800256</attribute>
  22:         <attribute name="Size">1</attribute>
  23:         <attribute name="LastKnownName">Class1</attribute>
  24:         <attribute name="RenderedHeight">49,25</attribute>

But the more common scenario is the format where the xml elements have specific names. Like the pad format:

   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <XML_DIZ_INFO>
   3:   <MASTER_PAD_VERSION_INFO>
   4:     <MASTER_PAD_VERSION>3.01</MASTER_PAD_VERSION>
   5:     <MASTER_PAD_EDITOR>PADManager 2.0.46</MASTER_PAD_EDITOR>
   6:     <MASTER_PAD_INFO>Portable Application Description, or PAD for short, is a data set that is used by shareware authors to disseminate information to anyone interested in their software products. To find out more go to http://www.asp-shareware.org/pad</MASTER_PAD_INFO>
   7:   </MASTER_PAD_VERSION_INFO>
   8:   <RoboSoft>
   9:     <Company_UIN>CMP-009701CA593</Company_UIN>
  10:     <Company_Description></Company_Description>
  11:     <Product_UIN>APP-009701FB886</Product_UIN>
  12:     <Publish_on_CD>FALSE</Publish_on_CD>
  13:     <Search_String></Search_String>
  14:     <Press_Release_Search_String></Press_Release_Search_String>
  15:     <NewsFeed_Search_String></NewsFeed_Search_String>
  16:     <Search_Engine_Search_String></Search_Engine_Search_String>
  17:     <Web_Directories_Search_String></Web_Directories_Search_String>
  18:     <Search_String_Unique></Search_String_Unique>
  19:     <Comments_For_Reviewer></Comments_For_Reviewer>
  20:     <Additional_Categories></Additional_Categories>
  21:     <Backlink></Backlink>
  22:   </RoboSoft>
  23:   <Company_Info>
  24:     <Company_Name>CapableObjects.com</Company_Name>
  25:     <Address_1>Box 1200</Address_1>
  26:     <Address_2>Huddinge</Address_2>
  27:     <City_Town>Stockholm</City_Town>

 

 

I added a window that pops up when executing the plugin, it lets you pick the xml file, choose strategy, choose target package and GO:

image

And it gave me this (I dragged them onto the diagram myself)

image

Puh, now when I have a good starting point for the rational rose object model I can back to my original task… But that will be some other day…

(the plugin is in the demos folder for the next available eco build and it is called PluginXmlToModel)

Categories: Companies

Modlr plugins

CapableObjects - Wed, 02/17/2010 - 16:27

We added a plugin interface for Gaffr and ECO. We want to enable you to extend Modlr so that you can implement a special file format import, or export, or what have you.

This is how it works:

Given these two interfaces…

   1: namespace Modlr.Plugins
   2: {
   3:     public interface IModlrPlugin
   4:     {
   5:         string GetName();
   6:         List<IModlrPluginMenuOperation> ProvideOperations();
   7:     }
   8:  
   9:     public interface IModlrPluginMenuOperation
  10:     {
  11:         string GetName();
  12:         void CheckEnableOrExecute(IEcoServiceProvider esp, bool execute, 
                        IEcoObject maincontext, IEcoObject subcontext,out bool enabled );
  13:     }
  14: }

…you can implement a plugin in an assembly like this:

   1: public class Class1 : IModlrPlugin
   2: {
   3:     #region IModlrPlugin Members
   4:  
   5:     public string GetName()
   6:     {
   7:         return "Demo plugin";
   8:     }
   9:  
  10:  
  11:     public List<IModlrPluginMenuOperation> ProvideOperations()
  12:     {
  13:         List<IModlrPluginMenuOperation> ops = new List<IModlrPluginMenuOperation>();
  14:         ops.Add(new OperationShowDialog());
  15:         ops.Add(new OperationActOnClass());
  16:  
  17:         return ops;
  18:     }   
  19:  
  20:     #endregion
  21: }
  22:  

The code above installs two Operations; OperationShowDialog and OperationActOnClass.

Looking closer on the operation ActOnClass:

   1: public class OperationActOnClass : IModlrPluginMenuOperation
   2: {
   3:     #region IModlrPluginMenuAction Members
   4:  
   5:     public void CheckEnableOrExecute(IEcoServiceProvider esp,
                      bool execute, Eco.ObjectRepresentation.IEcoObject maincontext,
                      Eco.ObjectRepresentation.IEcoObject subcontext, out bool enabled)
   6:     {
   7:         enabled = false;
   8:         if (maincontext != null && maincontext.AsIObject().AsObject is Eco.ModelLayer.Class)
   9:         {
  10:             enabled = true;
  11:             if (execute)
  12:             {
  13:                 (maincontext.AsIObject().AsObject as Eco.ModelLayer.Class).Name += "X";
  14:             }
  15:         }
  16:     }
  17:  
  18:     public string GetName()
  19:     {
  20:         return "Operation on class";
  21:     }
  22:  
  23:     #endregion
  24: }

The code checks that the context is a class, and if it is the code says that the operation should be enabled. If this was an “execute” call we perform the desired logic – in this case I just add an “X” to the back of the class name.

You can do a lot of stuff in a plugin. You get access to the IEcoServerProvider for the EcoModelLayer. The Modlr application is a standard ECO-built-application so you can use the IOCLService to get to any object, of any class that builds up your model content. You can access Diagrams, PlacedClasses, StateMachines etc. You can inspect and set TaggedValues, you can run thru every Attribute and check for spelling errors. You can create reports that you share with other ECO/Gaffr users…

So even if the interface is very small and simple, it is very powerful.

When you build your Plugin you will want to reference the following assemblies (found in the eco or gaffr install directory):

image

Compile your plugin and put the assembly in the folder %ProgramData%/CapableObjects/Plugins

Restart Gaffr.net or VisualStudio and you will find your plugin operations in the context menu:

image

Simple and powerful.

Categories: Companies

ViewModels in ASP.NET

CapableObjects - Sun, 02/14/2010 - 21:07

I got a couple of questions about ViewModels in ASP.NET; and my conscience has stirred me to action. I was aiming to finish the immensely cool  project that we call GaffrServer and I want to use the ASP.NET viewmodels to make that happen.

Side note on what GaffrServer is: Imagine that you prototype in Gaffr.net using the upcoming Application WPF framework WECPOF (oh I just love inventing code names for our projects: WECPOF - ECO + WPF – get it?), and you want persistence. You can write to a XML file, or set up a database… But you will also be able to connect to GaffrServer – GaffrServer is cloud service that you upload your model to, then you get back an url that is the up and running server side persistence mapper with the synchronization service running – WOW! I WANT IT NOW.

Anyway the GaffrServer project is sort of dragging its feet so I am better of just showing ASP.NET ViewModels.

I wrapped up a sample that looks like this:

image

 

Notice the derived attributes on OrderItem and Order:

   1: CostForRow = (self.Quantity*self.Article.Cost)+(self.Quantity*self.Article.Weight*self.Order.DeliveryType.CostPerKiloGram)
   2:  
   3: TotalCost = self.OrderItem->Collect(a|a.CostForRow)->sum()

(Note that the TotalCost actually use the derivation in CostForRow – super cool imho)

Then I did a ViewModel:

image

I know some of you might initially frown at the complexity, but trust me, you just click away and use the suggested columns that are available on the context menu – I seldom have a specific plan when I start – I think and do at the same time. That is the hallmark of a tool that is helping you…

The basic idea was to fill in an order, add OrderLines, set the correct Article on the OrderLine, see the price per row, and the total price, taking into account what delivery type you use…

There you go, a neat little list of requirements so easily said – and easily implemented.

I created a ECO ASP.NET application from the ECO Solution Wizard, Added the scriptmanager and an UpdatePanel to get Ajax behavior.

Then I add the ViewModelASPNET component, hook it up to the EcoSpaceManager, and tell it to use ViewModel ViewModelOrder:

image

I need to write some code to get test data and to hook up the ViewModelASPNET control to an order:

   1: private void Page_Load(object sender, System.EventArgs e)
   2: {
   3:  
   4:     if (!Page.IsPostBack)
   5:     {
   6:         Order order;
   7:         if (EcoSpace.GetEcoService<IExtentService>().AllInstances<Order>().Count == 0)
   8:         {
   9:             order = InitSomeTestData();
  10:         }
  11:         else
  12:             order = EcoSpace.GetEcoService<IExtentService>().AllInstances<Order>()[0];
  13:         ViewModelASPNET1.SetObject(order);
  14:     }
  15: }
  16:  
  17: private Order InitSomeTestData()
  18: {
  19:     Order order=new Order(EcoSpace);
  20:     order.CustomerName = "SomeName";
  21:     order.CustomerAddress = "SomeAddress";
  22:     order.DeliveryType = new DeliveryType(EcoSpace) { Name = "UPS", CostPerKiloGram = 20.5 };
  23:     new DeliveryType(EcoSpace) { Name = "Schenker", CostPerKiloGram = 19.5 };
  24:     new DeliveryType(EcoSpace) { Name = "DHL", CostPerKiloGram = 29.5 };
  25:  
  26:     new Article(EcoSpace) { Name = "Knife", Cost=127.5, Weight=0.25 };
  27:     new Article(EcoSpace) { Name = "Vobbler", Cost = 17.1, Weight = 0.15 };
  28:     new Article(EcoSpace) { Name = "Fishing rod", Cost = 527.5, Weight = 1.05 };
  29:  
  30:     EcoSpace.UpdateDatabase();
  31:     return order;
  32:  
  33: }

I also must init the viewmodels before they are used:

   1: protected void ScriptManager1_Init(object sender, EventArgs e)
   2: {
   3:     ViewDefinitionsInApplication.Init(EcoSpace);
   4:         
   5: }

Then I get something that looks like this:

image

Notice the crazy style on CostForRow, the style on TotalCost and on Selected orderitem – I get these by providing a css stylesheet that has styles using the names I set in the ViewModelColumns StyleRef property…

As you change deliverytype or anything else the TotalCost will be updated on the next postback.

In this demo I chose to use the setting ViewModelASPNET.PostbackOnComboboxChange="true"
I also chose to use the setting ViewModelASPNET.GridSizeFixedWithScrollbar="True"

The bits to run this is in the eco build after this date, it is in the demos called EcoASPNETWithViewModel

Categories: Companies

Using Blend with ECO

CapableObjects - Sun, 01/17/2010 - 15:18

Blend is the XAML-high-level tool. It is very powerful if you know what you are doing. Having that special designer eye that helps you to stop messing with something when it is done is probably a very useful skill when working with Blend – I do not have it, yet. But still here is my 5 minutes crash course into Microsoft Blend for ECO developers.

First: Blend is all about UI. Nothing about data. You can bind the UI to a datasource, but you cannot do anything else useful with the datasource in Blend – just use it.

Second: XAML is all about binding – in that sense it is very declarative. You do not do any imperative coding in Blend (ideally). Instead you declare rules that makes things happen for you; i.e. you declare bindings and animations.

I continued on the WPF-Hockey sample I wrote about earlier. To do anything cool in Blend you need to have an idea on what you want (that statement is sort of generic) – if that idea includes data then ECO is your best friend.

I find it useful to declare the processes in ECO, it helps me to keep my objectives clear and focused:

image

The SetUpGame thing I did in the previous post. Now I am going to do the “Show teams and players”.

I added LineUp and Player to the model:

image

And a view model that supports this model:

image

That was the easy part. The above steps are done quickly with Enterprise core objects. Good! More time left to spend in Blend (and boy will you need it!).

Start up Blend (I use Microsoft Expression Blend 3). File, Open, Project/Solution:

image

You can add UserControls just as you would have done in Visual studio:

image

So I did a user control looking like a hockey rink (took me forever!) ; that will come in handy. Doing stuff like this in Blend is just as complicated as in any other competent drawing tool (Photoshop or what have you). But Blend has an advantage with the split xaml view. You can fine tune stuff just by changing the xaml.

I then create a new WPF-Window and add my user control:

image

I add some comboboxes to show the lineups for each team. I also add some textblocks to show the players for each LineUp. I also add two images up on the ice, to show the team images. All of these controls should show data. So I need to bind…

   1: //This is how I bind the Image to my ViewModel
   2: <Image  x:Name="imageHome" Margin="153,111,224.54,0" 
   3:     Source="{Binding Path=Class[GameDay]/Home_Image,Mode=OneWay,Converter={StaticResource ImageBlobConverter} }" 
   4:     Stretch="Fill" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Top" Height="107">
   5:  
   6: //This is how I bind one of the textblocks
   7: <TextBlock  x:Name="textBlock" 
   8:         HorizontalAlignment="Left" VerticalAlignment="Bottom" 
   9:     Text="{Binding Path=Class[LineUp]/CenterForward_Name}" 
  10:    TextWrapping="Wrap" Margin="76.088,0,0,88.08" RenderTransformOrigin="0.515,0.055" Style="{DynamicResource playerstyle}">
  11:  
  12: // And the comboboxes
  13: <ComboBox DisplayMemberPath="Name" 
  14:     IsSynchronizedWithCurrentItem="True" 
  15:     ItemsSource="{Binding Path=Class[LineUp]}"  
  16:     Height="23" Margin="225,0,140,110" Name="comboBox1" VerticalAlignment="Bottom" />

Ok, now I have said what I want to bind to, that is not enough, I need to get the data there as well.

   1: // Add a ViewModelContent component to the resource section, tell it what your ViewModel is called, and what type your EcoSpace has:
   2: <Window.Resources>    
   3:     <ecoVM:ViewModelContent x:Key="VM1" ViewModelName="GameDay" EcoSpaceType="{x:Type ecospace:HockeyContEcoSpace}" ></ecoVM:ViewModelContent>

I now switch back to Visual Studio to actually put a “Game” into the GameDay ViewModel:

   1: {
   2:     public WindowGameDay()
   3:     {
   4:         this.InitializeComponent();
   5:  
   6:         // Insert code required on object creation below this point.
   7:     }
   8:  
   9:     private EcoSpace _es;
  10:     internal void SetEcoSpace(EcoSpace es, Game aGame)
  11:     {
  12:         _es = es;
  13:         (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).SetEcoSpace(_es);
  14:         (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).RootObject = aGame;
  15:         (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).GetCollectionView("LineUp").MoveCurrentToFirst();
  16:         (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).GetCollectionView("LineUpVisitor").MoveCurrentToFirst();
  17:         LayoutRoot.DataContext = Resources["VM1"];
  18:  
  19:     }

Notice that I expect to be handed a EcoSpace and a Game for this window to display. Also I use the method GetCollectionView of the ViewModel so that I can position the LineUp combos to show the first line up when we start.

TestRun:

image

Ok. Now to Animations. This is how animations work: They change a value over time, from an initial value to a desired value. Animations can be grouped together into a Storyboard.

Switching back to Blend. Go to menu Window, Workspaces, Animation:

image

Now your Blend workspace switch and you will see this pane at the bottom of your screen:

image

Use the plus sign (I marked it with red) to add a new storyboard.

PAY ATTENTION TO THE LITTLE DOT MARKED WITH GREEN!!!! When this is red – recording is on, click it to turn off recording (it toggles, so you can start and stop).

When Recording is on: WHEN YOU MOVE AROUND CONTROLS, THE NEW POSITION IS THE DESIRED POSITION FOR THE TIME INDICATED BY THE YELLOW MARKER.

Animation will make a linear move from start to goal over time (you can control this if linear is not what you want).

So this is how you go about animating the Image Opacity from 100% to 0% in 3 seconds:
- Turn recording on
- Move the Yellow time line to zero
- Set Image Opacity to 100% (you use the standard panes to set the values)
- Move the yellow line to 3
- Set Image Opacity to 0%
- Turn Animation off.

You repeat this process for all the stuff you want to move around. This is a very powerful tool and you have every chance to destroy all the work you have done so far – but it is sort of fun.

The sample now animates the Players for the selection LineUp up onto the Ice, while the team images fade out and disappear:

image

And when you change the Line up, the names change no matter what the current position of the animation is.

I was about to create a video to show the animation, but I went for a ClickOnce install instead:

http://www.capableobjects.com/publishedsamples/Hockey/publish.htm

Define a valid Game, then click Start…

Categories: Companies

The ViewModel

CapableObjects - Mon, 01/04/2010 - 23:15

In large applications with large models the UI will soon get filled with business logic that does not belong there. The logic is left there but the developer is someday going to return and clean it all up (Sure!).

Every developer knows that the degrees of freedom rapidly decrease once you fill your UI with business logic:

  1. You cannot easily reuse the logic placed in a UI so you copy it and increase the maintenance (BAD!)
  2. You forget about rules placed in UI so your system gets a life of its own (BAD!)
  3. Once you have logic in the UI you cannot be expected to know it all so you get afraid to make changes to your system because something will or may break (BAD!)
  4. You dare not give the UI to that brilliant designer because the designer will break logic for sure (BAD!)

Still I see business logic in the UI everywhere I go. When I ask about it I always get answers like: “Well this is not finished yet”, or “Well this is really special stuff, only used in one place”, or “Yea I know it sucks, I will fix that later”. But “Later” never comes, does it? It will always be just “Later”.

I am no superman for sure. I see business logic in UI I have written myself too.

If everyone or at least most of us is getting into these situations could it be that we are doing things the wrong way? Could it be that doing things the right way is just a tad bit too complicated?

These are some strategies to make developers do the correct thing and actually follow the coding guidelines:

  1. Automated review tools like FXCop
  2. Peer review (that usually will be done “later”)
  3. Some other strategy that will force violators (you and me) to mend our ways – even if the correct way is really complicated (maniac team leader with rabies foam in the face)
  4. Make it easier and faster to follow the “coding guidelines” then to be quick and dirty.

To no surprise I am in favor of making things easier. But to be able to make things easier we need to understand what cause things to go wrong in the first place: Why is the UI filling up with business logic? I think there are a couple of reasons, depending on how far you are from being model driven some of these reasons will apply:

  1. The UI will need to transform data (could be business logic) in order to display it according to specification.
  2. Actions performed in UI act on selections from UI-components, that has data in the transformed presentation format, and need to be transformed back (could be business logic) to model-scope in order to let the model work on the data (business logic). And you also need to check that the parameters sent to the model method are valid (could be business logic).
  3. The existing business logic may not match 100% what your action should do, you may want to call two, three of more methods to get the job done (new business logic) and you want to do some small checks based on the results of each method (could be business logic).
  4. Validation – your UI will do a lot of small checks to see that the data fulfills the overall rules for your application (business logic)

How do we make these reasons go away?

  1. We do it by offering a good and easy way to transform model-elements (or data if you will) into data elements suitable for render to match the specification.
  2. We do it by making it real easy to add business logic where it belongs (in the model), and make it easy to call it.
  3. And we offer a clean and easy way to add validation logic.

By setting the model in focus, and making it dirt simple to add methods, derived attributes and derived associations you can do everything you need for a specific UI in the model. This is sort of a good thing . The problem is that if you have a 100 UI’s your model will get filled with 100 times x derived attributes and derived links. And that does not sound like a good thing to me. It will eventually get hard to see the trees for the forest in a model like that.

And further more when a UI is dropped and deleted for some reason, will we remember to drop the derived associations and derived attributes we added to the model to accommodate it? Probably not.

A transformation layer between the UI and the Model can fix this. The transformation layer is allowed to have business logic, as long as it is unique for the context it works on. If the logic is not unique, it should go in the model ready to be re-used. We call this transformation Layer for a ViewModel.

A ViewModel transforms a piece of the model for a specific purpose. The purpose is often, but not always, to prepare the model information for interaction with a user for a specific use case – a UI. I said often but not always; it can also be for creating data transfer objects that are suitable for exposure for another application or system, or for some reporting engine or the like.

Why is it having the rules in a ViewModel is much better than having them in the UI? There are a lot of reasons:

  1. Testing; it is a good thing to be able to test the logic separated from the UI, because it is awkward and error prone to test and read the results back from the UI.
  2. ViewModel re-use ; you may have different UI’s for the exact same use case (beginner/advanced, Web-api/Rich client etc).
  3. Design time type checking; most UI-binding strategies rely on using strings that can only be checked at runtime (true for winforms, WPF, Silverlight and ASP.NET), whereas a good ViewModel is type checked at design or compile time.
  4. The designer working on the UI can harm important logic if the logic is in the UI.
  5. If we have dedicated designers we will not want to wait for them to release a UI file in order to fix business logic within.
  6. The UI may be on the other side of a network (another physical tier) so the UI cannot have access to the domain layer tier
  7. UI and logic have very different motivators and hence will often change for different reasons (looking good versus working correctly), mixing them up add confusion regarding the distinction between these important aspects.
  8. Security, designer that get access to the ViewModel cannot go beyond the ViewModel and unintentionally expose information that should not get exposed in the use case at hand.

The thing is that you do not have to use a ViewModel pattern to create a great application, it is just that is a good idea to use the ViewModel pattern when building applications that are going to be worked on for a long time, released in several versions over several iterations, and will most likely see different developers and UI-designer, and may very well be radically changed with regards to presentation framework during its lifespan. In short – it is always a good idea for successful applications to use a ViewModel pattern.

The declarative ViewModel

Presented with a model that I got from Rick Weyrauch, that incidentally helps out as a Hockey Referee when he is not coding, I wanted to create a ViewModel for the use-case “Set up new Hockey game”.

image

The Game class has a state machine:

image

I took a piece of paper and draw the UI I wanted to achieve:

image

Now I know what the requirements are on the ViewModel since I can see from the drawing what data the ViewModel needs to hold.

And then created this ViewModel That I named GameSetup:

image

Notice that it is just a series of named ocl expressions. Some expressions are nested to other list definitions like Home_PickList that states that if the Game has a picked GameType, then we know what teams that can be picked – namely those teams that are associated to that GameType

I created some test data so that UI can show something:

   1: private Game CreateSomeTestData()
   2: {
   3:     // Game types
   4:     var gtboys15 = new GameType(_es) { Name = "15 years, Boys" };
   5:     var gtboys16 = new GameType(_es) { Name = "16 years, Boys" };
   6:     var gtgirls15 = new GameType(_es) { Name = "15 years, Girls" };
   7:  
   8:     // team types
   9:     var ttb15=new TeamType(_es) { Name = "Boys 15 years" };
  10:     var ttb16 = new TeamType(_es) { Name = "Boys 16 years" };
  11:     var ttg15 = new TeamType(_es) { Name = "Girls 15 years" };
  12:  
  13:     // Valid team-game combinations
  14:     gtgirls15.TeamTypes.Add(ttg15);
  15:  
  16:     gtboys15.TeamTypes.Add(ttb15);
  17:     gtboys15.TeamTypes.Add(ttg15); // girls can play in boys 15 year
  18:     gtboys16.TeamTypes.Add(ttb15); // 15 year boys can enter 16 year games 
  19:     gtboys16.TeamTypes.Add(ttb16);
  20:     gtboys16.TeamTypes.Add(ttg15); // girls can play in boys 16 year
  21:  
  22:  
  23:  
  24:     new Team(_es) { Name = "Brynäs",Image=GetImage(imagebrynäs), TeamType=ttb15 };
  25:     new Team(_es) { Name = "Brynäs", Image = GetImage(imagebrynäs), TeamType = ttb16 };
  26:     new Team(_es) { Name = "Brynäs", Image = GetImage(imagebrynäs), TeamType = ttg15 };
  27:  
  28:     new Team(_es) { Name = "LuleĂĄ", Image = GetImage(imageluleĂĄ), TeamType = ttb15 };
  29:     new Team(_es) { Name = "LuleĂĄ", Image = GetImage(imageluleĂĄ), TeamType = ttb16 };
  30:     new Team(_es) { Name = "LuleĂĄ", Image = GetImage(imageluleĂĄ), TeamType = ttg15 };
  31:  
  32:     new Team(_es) { Name = "DjurgĂĄrden", Image = GetImage(imagedjurgĂĄrden), TeamType = ttb15  };
  33:     new Team(_es) { Name = "DjurgĂĄrden", Image = GetImage(imagedjurgĂĄrden), TeamType = ttb16 };
  34:     new Team(_es) { Name = "DjurgĂĄrden", Image = GetImage(imagedjurgĂĄrden), TeamType = ttg15 };
  35:  
  36:     return new Game(_es)
  37:     {
  38:         ScheduledDate = DateTime.Now
  39:     };
  40:  
  41: }

Now I am ready to build the UI.

   1: <Window x:Class="WPFBinding.Window2"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:eco="clr-namespace:Eco.WPF;assembly=Eco.WPF"
   5:     xmlns:ecoVM="clr-namespace:Eco.ViewModel.WPF;assembly=Eco.WPF"
   6:     xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
   7:     xmlns:local="clr-namespace:WPFBinding"
   8:     xmlns:ecospace="clr-namespace:WPFBinding;assembly=WPFBinding.EcoSpace"
   9:     Title="Window2" Height="300" Width="500" >
  10:     <Window.Resources>
  11:         <ecoVM:ViewModelContent x:Key="VM1" ViewModelName="GameSetup" EcoSpaceType="{x:Type ecospace:WPFBindingEcoSpace}" ></ecoVM:ViewModelContent>
  12:         <local:ImageBlobConverter x:Key="ImageBlobConverter"/>
  13:     </Window.Resources>
  14:     <Grid>
  15:         <Grid Name="vmrootStackPanel" DataContext="{StaticResource VM1}">
  16:             <Grid.ColumnDefinitions>
  17:                 <ColumnDefinition></ColumnDefinition>
  18:                 <ColumnDefinition></ColumnDefinition>
  19:                 <ColumnDefinition Width="50"></ColumnDefinition>
  20:             </Grid.ColumnDefinitions>
  21:             <Grid.RowDefinitions>
  22:                 <RowDefinition></RowDefinition>
  23:                 <RowDefinition></RowDefinition>
  24:                 <RowDefinition></RowDefinition>
  25:                 <RowDefinition></RowDefinition>
  26:                 <RowDefinition></RowDefinition>
  27:                 <RowDefinition></RowDefinition>
  28:                 <RowDefinition></RowDefinition>
  29:                 <RowDefinition></RowDefinition>
  30:             </Grid.RowDefinitions>
  31:  
  32:             <TextBlock Grid.Row="0" Grid.Column="0" Text="GAME : " HorizontalAlignment="Right" ></TextBlock>
  33:             <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding  Path=Class[GameSetup]/Presentation,Mode=OneWay}"></TextBox>
  34:  
  35:             <TextBlock Grid.Row="1" Grid.Column="0" Text="Type of game : " HorizontalAlignment="Right" ></TextBlock>
  36:             <ComboBox Grid.Row="1" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[GameType_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/GameType}"  ></ComboBox>
  37:  
  38:             <TextBlock Grid.Row="2" Grid.Column="0" Text="Home team : " HorizontalAlignment="Right" ></TextBlock>
  39:             <ComboBox Grid.Row="2" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[Home_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/Home}"></ComboBox>
  40:             <Image Grid.Row="2" Grid.Column="2"  Source="{Binding Path=Class[GameSetup]/Home_Image,Mode=OneWay,Converter={StaticResource ImageBlobConverter} }"></Image>
  41:  
  42:  
  43:             <TextBlock Grid.Row="3" Grid.Column="0" Text="Visitor team : " HorizontalAlignment="Right" ></TextBlock>
  44:             <ComboBox Grid.Row="3" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[Visitor_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/Visitor}"></ComboBox>
  45:             <Image Grid.Row="3" Grid.Column="2"  Source="{Binding Path=Class[GameSetup]/Visitor_Image,Mode=OneWay,Converter={StaticResource ImageBlobConverter} }"></Image>
  46:  
  47:             <TextBlock Grid.Row="4" Grid.Column="0" Text="Scheduled date : " HorizontalAlignment="Right" ></TextBlock>
  48:             <Controls:DatePicker Grid.Row="4" Grid.Column="1"  ></Controls:DatePicker>
  49:             
  50:             <Button Grid.Row="5" Grid.Column="0" IsEnabled="{Binding Path=Class[GameSetup]/CanStartGame}" Click="ButtonStartGame_Click">
  51:                 <TextBlock Text="Start Game"></TextBlock>
  52:             </Button>
  53:  
  54:             <Button  Grid.Row="5" Grid.Column="1"  IsEnabled="{Binding Path=Class[GameSetup]/CanEndGame}" >
  55:                 <TextBlock Text="End Game" ></TextBlock>
  56:             </Button>
  57:  
  58:         </Grid>
  59:          <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
  60:             <Image Height="50" Name="imagebrynäs" Stretch="Fill" Width="50" Source="/WPFBinding;component/brynäs.jpg" />
  61:             <Image Height="50" Name="imagedjurgĂĄrden" Stretch="Fill" Width="50" Source="/WPFBinding;component/djurgĂĄrden.jpg" />
  62:             <Image Height="50" Name="imageluleĂĄ" Stretch="Fill" Width="50" Source="/WPFBinding;component/LuleĂĄ.jpg" />
  63:         </StackPanel>
  64:     </Grid>
  65: </Window>

What happens in the xaml above is that we have a ViewModelContent component in the resource section. We initiate it to the name of the ViewModel, and we also provide the Type of the ecospace.

The ViewModelContent object, now keyed as VM1, is put in the DataContext of the Grid where I place the UI components. If a WPF Binding does not get an explicit source it will use whatever it finds in the DataContext (the datacontext is propagated down the logical tree, so for us it is everywhere).

In code behind we hook up the EcoSpace and the rootobject-property (dependencyproperty so that you bind as target and as source) to our demo Game object:

   1: (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).SetEcoSpace(_es);
   2:  
   3: (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).RootObject=CreateSomeTestData();

The UI looks like this:

image

Yes it looks bad; but hey, you can hand it to any WPF savvy designer in the world –  the data and the rules are safe in the ViewModel.

It already shows some of the good effects of separating UI from logic. If you run the sample you will see and hopefully appreciate that:

  1. The PickLists for Home and Visitor are filtered based on Type of Game
  2. The Picklist for Home team filters away the Visitor team if set (and vice versa)
  3. Start game is enabled only after both home and visitor are set
  4. The End game button is disabled until the Game is started

These are some examples of business logic that would have easily ended up in the UI if we did not have a good place to define it.

Taking it further still

If the cost of creating and maintaining a ViewModel is high fewer ViewModels will be created. So our mission is to reduce the cost of creating and maintaining them. Can we do more? I will argue that we can.

WPF is a declarative way to describe the UI. This mean that the same basic lookless components like TextBlock, TextBox, CheckBox, Combobox and Image etc will be used again and again and they will be given a look by an external style or template. 

What if we use this fact and provide some basic rendering/placing hints for the ViewModel columns? We could then use those clues to spill out the correct lookless control in the intended relative position so we would not need to mess about with xaml every 5 minutes… I am excited… Xaml is a bit too scripty for my old strongly typed ways…

This is what the ViewModel-Editor looks like without rendering hints:

image

And this is the way it looks when I have checked the “Use Placing Hints” checkbox:

image

Given the extra fields for “Presentation”, “Column”,”Row”,”Span” etc I can work the ViewModel - preview to look like this:

image

Now I really need to stress this so that I do not get misunderstood: We do not mix presentation with UI, we do however allow for adding optional placing hints or clues on what you have in mind while designing the ViewModel.

Having a ViewModel with placing hints, you can add a ViewModelWPFUserControl  to your form with just one row:

   1: <ecoVM:ViewModelWPFUserControl Grid.Row="2" x:Name="VMU1" EcoSpaceType="{x:Type ecospace:WPFBindingEcoSpace}" ViewModelName="GameSetup" ></ecoVM:ViewModelWPFUserControl>

And the result is:

image

And remember that these auto layouted controls also adheres to external set styles. (Notice the datetime picker is gone? Datetime picker is in WPFToolkit so we do not use it by default. Implement ViewModelWPFUserControl.OnColumnUIOverride to add your own components to its layout engine.)

Summing it up

This has been a brief overview of the ViewModel concept. Things intentionally left out for now was Actions, Validation-rules, Variables, Master-detail (since there were no details in the sample ), Style references and Tab order.

I have written about the benefits of having a ViewModel in the first place. Then I wrote about the Modlr approach with a strictly declarative ViewModel. We looked at the sample using such a ViewModel and some of the effects it gave in separating logic from UI. Then I showed you a ViewModel with placing hints – a bit un-orthodox for sure, but efficient and easy to maintain.

Thanks goes out to everyone that has been involved in the Modlr ViewModel approach by giving feedback, and to Rick for providing the sample model – Hope I did not abuse it.

Categories: Companies

Exploring the UML Metamodel with our new free UML2 Metamodel Viewer

EmPowerTec UML and modeling blog - Tue, 12/08/2009 - 21:17

Hello,

if you you need to use the UML2 Metamodel in some way for your work you will have noticed, that it is not easy to develop an understanding of the Metamodel, even less to learn quickly about the classes, their attributes and their relations to other classes. Basically there are only the official specifications from the OMG which are a bit dry to read.

Enter our new, free UML2  Metamodel Viewer!

The UML2 Metamodel Viewer is a standalone Windows application that can be used to browse the package structure of the UML2 Metamodel and explore the contained classes. In addition, it is possible to draw diagrams from these classes in a semi automatic way.

Here is a screenshot of the main window:

Main Window of the UML Metamodel ViewerMain Window of the UML Metamodel Viewer

To create a diagram simply add a new diagram to your project and drag one or more classes from the Model Explorer to the diagram. For example, here is a diagram with the class Kernel::Classes::Class:

UML Metamodel Viewer with a diagram

UML Metamodel Viewer with a diagram

One of the neat features is that the related classes (superclasses and classes that are used in relations) are automatically added to the diagram. This is an easy way to see the “context” of a given class.

The installer comes with a manual that should get you started using the UML2 Metamodel Viewer.

There is a catch: currently UML2 Metamodel Viewer relies on some third party software (namely the Graphviz package and the UmlGraph package – the latter additionally requires a Java Development Kit) that must be installed and configured so that diagrams can be created.

This is a 1.0 release,  so you will surely find bugs using this software.  Please report bugs and feature suggestions to support@empowertec.de. We definitely appreciate your feedback! We plan to actively support this software, which means of course fixing bugs but also implementing new features depending on demand and effort.

The software is based on the cmof-files that define the UML2 Metamodel.

Best regards,
Andreas

Technorati Tags: , ,

Categories: Companies