locked
How to change the color dynamically? RRS feed

  • Question

  • Hi,

    How to change the color of dsl element while dynamically creating an element.

    Thanks

    Manikanth R

    Wednesday, August 4, 2010 4:57 AM

Answers

  • Simplest method yet. <sigh> My last answer on this topic, I hope! I've added some notes to my previous replies. Sorry about the churn.

    The original answer - an AddRule on the shape - works properly if you set the FireTime to TopLevelCommit - which I missed when I posted it.

    The AddRule on the PresentationViewsSubject relationship works OK, but it's a bit long.

    This one's a lot simpler. OnChildConfigured is called after the view "fixup", which creates a shape and links it to the model element.

     partial class MyDiagram
     {
      protected override void OnChildConfigured(ShapeElement child, bool childWasPlaced, bool createdDuringViewFixup)
      {
       base.OnChildConfigured(child, childWasPlaced, createdDuringViewFixup);
       CarShape shape = child as CarShape;
       if (shape == null) return;
       Car element = shape.ModelElement as Car;
       shape.FillColor = element.Name.EndsWith("3") ? System.Drawing.Color.Red : System.Drawing.Color.Green;
      }
    }

    This only deals with the initial values. If you want to change values after the shape has been created:

    • OnAssociatedPropertyChanged to set shape features that are not under transaction control, such as visibility or connector features like arrowheads; or color, if you set it directly instead of through a domain property.
    • ChangeRule to update domain properties. 

    - Alan -MSFT
    Friday, August 13, 2010 12:17 AM

All replies

  • In the DSL Definition, right click the shape class, point to Add Exposed, click Fill Color.

    The shape now has a domain property that you can set in program code or as a user.


    - Alan -MSFT
    Wednesday, August 4, 2010 1:29 PM
  • Hi Alan,

    Thanks for your reply,

    While creating elements dynamically(newCar), I am not able to find shape(newCarShape) element immediately after creating element, as I want to change the newCarShape color based on someproperty of Car.

    newCarShape = PresentationViewsSubject

     

     

    .GetPresentation(newCar as Car).FirstOrDefault() as CarShape;

    Thanks :

    Manikanth

    Wednesday, August 4, 2010 4:42 PM
  • [UPDATE: added "FireTime = TimeToFire.TopLevelCommit" in the RuleOn attribute. This makes the rule fire after the shape and its links have all been set up.]

    Yes, the shape won't be created until the fixup rules fire at the end of the transaction.

    Create an AddRule on your ShapeClass, and set its color from the state of the shape's ModelElement. That's in addition to exposing the color as above.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.VisualStudio.Modeling;
    using Microsoft.VisualStudio.Modeling.Diagrams;
    namespace ExampleNamespace
    {
     // Attribute associates the rule with class:
     [RuleOn(typeof(CarShape), FireTime = TimeToFire.TopLevelCommit)]
     // The rule is a class derived from one of the abstract rules:
     class CarShapeAddRule : AddRule
     {
     // Override the abstract method:
     public override void ElementAdded(ElementAddedEventArgs e)
     {
     base.ElementAdded(e);
     CarShape shape = e.ModelElement as CarShape;
     Store store = shape.Store;
     // Ignore this call if we're currently loading a model:
     if (store.TransactionManager.CurrentTransaction.IsSerializing) 
      return;
     Car car = shape.ModelElement as Car;
     // Code here propagates change as required - for example:
     shape.FillColor = car.Somebool ? System.Drawing.Color.Red : System.Drawing.Color.Green; 
     }
    }
     // The rule must be registered:
     public partial class ExampleDomainModel
     {
     protected override Type[] GetCustomDomainModelTypes()
     {
      List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
      types.Add(typeof(CarShapeAddRule));
      // If you add more rules, list them here. 
      return types.ToArray();
     }
     }
    }

    - Alan -MSFT
    Wednesday, August 4, 2010 5:38 PM
  • [UPDATE: This method is good for updating style and shape features that aren't coupled to domain properties. But it is called even in an Undo, so it isn't so useful for color and other properties that are exposed as domain properties, because they are under transaction control, and will be automatically rolled back on Undo.]

    Actually, there's a more purpose-built way of coupling shape color and so on to domain properties.

    It's described in page 408 of the DSL book http://www.domainspecificdevelopment.com/

    public partial class CarShape
    { // NEED TO SET DOUBLE DERIVED ON THE SHAPE CLASS TO OVERRIDE THIS:
     protected override void InitializeResources(StyleSet classStyleSet)
     {
     base.InitializeResources(classStyleSet);
     CarShape.AssociateValueWith(this.Store, 
      Car.PropertyThatDeterminesColorId);
     }
     protected override OnAssociatedPropertyChanged (PropertyChangedEventArgs e)
     {
     if ("PropertyThatDeterminesColor" == e.PropertyName)
     {
     // UPDATE:
     // Better to use this method for features not under transaction control.
     // For example:
     

    this

     

     

     

    .SetShowHideState(!e.NewValue.Equals("hide"));
     /* UPDATE: don't use for transaction-controlled properties like this:
      if (e.NewValue == "bad")
      this.FillColor = System.Drawing.Color.Red;
      else
      //this.FillColor = System.Drawing.Color.Green;
     */
     }
     }
    }
    
    
    

    - Alan -MSFT
    Wednesday, August 4, 2010 5:56 PM
  • Hi,

    Is there any specific reason for not getting car object from carShape object?I am not able to get car obj from the below line in the rule.

     Car car = shape.ModelElement as Car;

     

    Thanks :

    Manikanth

    Wednesday, August 4, 2010 6:27 PM
  • Oops. That's possibly because the rule is firing before the fixup rule, which links the shape to the car.

    Please try the second method.


    - Alan -MSFT
    Wednesday, August 4, 2010 6:53 PM
  • I still don't like this! The trouble with OnAssociatedProperty is that it is called after the transaction ends. Therefore when the user hits undo, the color will be changed to the original value, but the element will still be there. It requires two undo's to remove the new element.

    OnAssociatedProperty is really designed for updating shape properties that are outside the store, but part of the graphical engine - for example, the size of the shape.

    So here's the proper way of doing it. This is an AddRule on PresentationViewsSubject, which is the relationship between model elements and their shapes. At the end of "view fixup" - the process that creates a shape for each element - this relationship is instantiated between the new shape and the new element. Because the same type of relationship is created between all classes of elements and shapes, the first thing the rule should do is to verify the type of the shape and/or model element.

    To allow for changing the properties after the element is created, you might also want to add a change rule.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.VisualStudio.Modeling;
    using Microsoft.VisualStudio.Modeling.Diagrams;
    
    namespace ExampleNamespace
    {
     
     // Attribute associates the rule with class:
     [RuleOn(typeof(PresentationViewsSubject))]
     // The rule is a class derived from one of the abstract rules:
     class CarShapeAddRule : AddRule
     {
      // Override the abstract method:
      public override void ElementAdded(ElementAddedEventArgs e)
      {
       base.ElementAdded(e);
       PresentationViewsSubject relation = e.ModelElement as PresentationViewsSubject;
    
       // Is this viewing the right type of element?
       CarShape shape = relation.Presentation as CarShape;
       Car car = relation.Subject as Car;
       if (shape == null || car == null) return; 
    
    
       Store store = relation.Store;
       if (store.TransactionManager.CurrentTransaction.IsSerializing) return;
       // Code here propagates change as required - for example:
       shape.FillColor = car.Name.EndsWith("3") ? System.Drawing.Color.Red : System.Drawing.Color.Green;
      }
     }
    
     [RuleOn(typeof(Car))]
     class CarPropertyChangeRule : ChangeRule
     {
      public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
      {
       base.ElementPropertyChanged(e);
       Car car = e.ModelElement as Car;
       if (e.DomainProperty.Id == Car.NameDomainPropertyId)
       {
        // There is usually only one shape:
        foreach (PresentationElement pel in PresentationViewsSubject.GetPresentation(car))
        {
         ExampleShape shape = pel as ExampleShape;
         shape.FillColor = car.Name.EndsWith("3") ? System.Drawing.Color.Red : System.Drawing.Color.Green;
        }
       }
      }
     }
     // The rules must be registered:
     public partial class ExampleDomainModel
     {
    
      protected override Type[] GetCustomDomainModelTypes()
      {
       List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
       types.Add(typeof(CarShapeAddRule));
       types.Add(typeof(CarPropertyChangeRule));
       // If you add more rules, list them here. 
       return types.ToArray();
      }
     }
    }
    
    

    - Alan -MSFT
    Tuesday, August 10, 2010 8:49 PM
  • Simplest method yet. <sigh> My last answer on this topic, I hope! I've added some notes to my previous replies. Sorry about the churn.

    The original answer - an AddRule on the shape - works properly if you set the FireTime to TopLevelCommit - which I missed when I posted it.

    The AddRule on the PresentationViewsSubject relationship works OK, but it's a bit long.

    This one's a lot simpler. OnChildConfigured is called after the view "fixup", which creates a shape and links it to the model element.

     partial class MyDiagram
     {
      protected override void OnChildConfigured(ShapeElement child, bool childWasPlaced, bool createdDuringViewFixup)
      {
       base.OnChildConfigured(child, childWasPlaced, createdDuringViewFixup);
       CarShape shape = child as CarShape;
       if (shape == null) return;
       Car element = shape.ModelElement as Car;
       shape.FillColor = element.Name.EndsWith("3") ? System.Drawing.Color.Red : System.Drawing.Color.Green;
      }
    }

    This only deals with the initial values. If you want to change values after the shape has been created:

    • OnAssociatedPropertyChanged to set shape features that are not under transaction control, such as visibility or connector features like arrowheads; or color, if you set it directly instead of through a domain property.
    • ChangeRule to update domain properties. 

    - Alan -MSFT
    Friday, August 13, 2010 12:17 AM