none
Custom activity designer with wpf user control

    Question

  • Hello,

    I'm trying to develop some activities to the new wf 4.0 and i'm using some wpf user control's that i have from my activity library from wf 3.5.
    What i would like to do is use that user control direct in the activity designer, but how can i bind to the properties in the activity from the user control? and how could i do that if that user control uses mvvm already.

    Thanks,
    Frederico
    Frederico Regateiro
    Tuesday, December 29, 2009 4:18 PM

Answers

  • >I tried to pass the ModelItem from the code beind file to my view model, but the Model Item is null after the InitializeComponent in the activity designer construter ?? So how to get the ModelItem in the View Model?


    Yes, it's not initialized in InitializeComponent(). The ModelItem property is only set after construction. Could you wait to get the ModelItem until WorkflowViewElement.OnModelItemChanged() has been called?


    >Well it works, but its a lot of work and i don't now if i can unit test that code.


    This might also be helpful - if you want to temporarily wrap an Activity in a ModelItem for unit testing it's possible to create a throwaway model item tree with the following:

    EditingContext ec = new EditingContext();
    ModelTreeManager mtm = new ModelTreeManager(ec);
    mtm.Load(theActivity);
    Tim
    • Marked as answer by fregateiro Wednesday, January 13, 2010 9:09 AM
    Tuesday, January 12, 2010 6:31 PM
    Moderator

All replies

  • Hi Frederico,

    I think you just need a sample of how to do binding in wf 4.0, which lets you bind against a public property on a WF4 activity, even though it is not a dependency property. You just bind against property subpath of ActivityDesigner.ModelItem (the data context of ActivityDesigner is itself).

    e.g.

    public class MyActivity
    {
        public int PropertyX { get; set; } //must be a property, not a field
    }

    and in your WPF XAML

    <myxmlns:MyCustomControl ControlProperty={Binding Path=ModelItem.PropertyX} ... />

    Does this help?
    Things will be a bit different for InArguments, but there are some other threads on that if you find you need to bind to arguments.
    Tim
    Saturday, January 09, 2010 12:26 AM
    Moderator
  • Hi tilovell,
    thanks for the reply, but what i'm trying to do is do databinding to my viewmodel and to ModelItem.

    the user controls that i have are wpf user controls with mvvm, so i wanted to use my uc in the new activities, a simple example:

    My view has a textbox:
    <TextBox Text="{Binding MyMame}"/>

    My view Model contains this property:
    private string myMame;
    public string MyMame
    {
         get{return myMame;}
         set{myMame = value; Changes("MyMame");}
    }

    My activity look like this:
    public sealed class ReadName  : NativeActivity
    {
            public ReadName() { }
    
            public string Name { get; set; }
    
            protected override void Execute(NativeActivityContext context)
            {
                //do something with Name
            }
    }

    How can i bind the Name property from my viewmodel to Name property from my activity.

    Thanks


    Frederico Regateiro
    Monday, January 11, 2010 5:00 PM
  • I did a little reading on MVVM and also that code helped me understand your scenario, but one thing I don't fully understand. Your view model seems to get/set properties based on its own fields. When would the properties be set on the actual activity? Is your view model acting as a mock or stand-in for the activity?

    Anyway I'll try to explain more based on what I've guessed so far...

    By default ActivityDesigner (and all its subclasses) set its DataContext = this (the ActivityDesigner itself).

    I'm going to imagine that your activity designer for ReadName activity is called ReadNameDesigner. ReadNameDesigner also already has a sort of auto-generated ViewModel which is based on the ReadName activity. I call it an auto-generated ViewModel because it is bindable, like a ViewModel, i.e. it has automatically generated a bindable Name property. This auto-generated view model, as I have been calling it, is actually the ModelItem type, and is available through the ModelItem property on the ReadNameDesigner.

    This lets you do

    <TextBox Text="{Binding Path=ModelItem.Name}"/>

    as opposed to

    <TextBox Text="{Binding Path=ModelItem.MyMame}"/>

    At this point your question might be 'How can I change it to bind against my view model instead of ModelItem? If your view model is called ReadNameViewModel, it seems like the easiest way would be to add it as another property on your ReadNameDesigner, and bind to that. e.g.

    <TextBox Text="{Binding Path=MyReadNameViewModel.MyMame}"/>

    Doing it this way, and not changing the DataContext like this seems advantageous to me because I am more used to this auto-generated view model. Also there are practical considerations like that the default template for ActivityDesigner probably expects the DataContext to be 'this'.

    There are still a couple issues you might see with this approach. One is getting undo/redo working. Undo/redo works automatically when using ModelItem to set properties. In order to take advantage of that, you might find yourself modifying the implementation to

    public string MyMame
    {
         get{return designer.ModelItem.Properties["myMame"];}
         set{ designer.ModelItem.Properties["myMame"].SetValue(value); Changes("MyMame");}
    }
    


    What would Changes("MyMame") do here? Probably the main thing is implementing INotifyPropertyChanged. But ModelItem also does that for you already...

    So what I'm wondering is whether you actually need to reuse the ViewModel, or whether you can just go with this auto-generated one, or if there is a problem how might it fall short of what you want to do e.g. does your view model need to look different from the activity? (e.g. calling it MyMame rather than Name) do you need to hook in custom change handling logic, or validation?

    Tim
    Monday, January 11, 2010 7:32 PM
    Moderator
  • Thanks for the answer Tim,

    In my senario i have to do some processing logic when the workflow its been designed, when the user is setting some properties in the activity designer, Name property, i have to call some web services to do some validation on that name. And i all ready had a user control that did just that, so i wanted to re use it.

    One thing that i didn't succed was in this code:

    public string MyMame
    {
         get{ return designer.ModelItem.Properties["myMame"];}
         set{ designer.ModelItem.Properties["myMame"].SetValue(value); Changes("MyMame"); }
    }

    I tried to pass the ModelItem from the code beind file to my view model, but the Model Item is null after the InitializeComponent in the activity designer construter ?? So how to get the ModelItem in the View Model?

    What i acomplish so far was:

    - Pasted all the properties and commands from my view model in the code behind of the activity designer;
    - Implemented INotityPropertyChanged in the code behind of the activity designer, like this i can control my update in the designer;
    - Set this.DataContext = this; in the activity construter;

    private string myName;
    public string MyName
    {
          get
          {
               if (string.IsNullOrEmpty(myName))
               {
                    if (this.ModelItem != null
                       && this.ModelItem.Properties != null
                       && this.ModelItem.Properties["Name"].Value != null
                       && this.ModelItem.Properties["Name"].Value.GetCurrentValue() != null)
                    {
                         myName = this.ModelItem.Properties["Name"].Value.GetCurrentValue().ToString();
                    }
                }
                return myName;
           }
           set
           {
                //cheks the name in my web service
                if(CheckName(myName))
                {
                     myName = value; //sets the value
                     Changes("MyName"); //notify the view
                     this.ModelItem.Properties["Name"].SetValue(myName); //update the activity property
                }
            }
    }
    


    Well it works, but its a lot of work and i don't now if i can unit test that code.

    Thanks


    Frederico Regateiro
    Tuesday, January 12, 2010 9:45 AM
  • >I tried to pass the ModelItem from the code beind file to my view model, but the Model Item is null after the InitializeComponent in the activity designer construter ?? So how to get the ModelItem in the View Model?


    Yes, it's not initialized in InitializeComponent(). The ModelItem property is only set after construction. Could you wait to get the ModelItem until WorkflowViewElement.OnModelItemChanged() has been called?


    >Well it works, but its a lot of work and i don't now if i can unit test that code.


    This might also be helpful - if you want to temporarily wrap an Activity in a ModelItem for unit testing it's possible to create a throwaway model item tree with the following:

    EditingContext ec = new EditingContext();
    ModelTreeManager mtm = new ModelTreeManager(ec);
    mtm.Load(theActivity);
    Tim
    • Marked as answer by fregateiro Wednesday, January 13, 2010 9:09 AM
    Tuesday, January 12, 2010 6:31 PM
    Moderator
  • Thanks, this tips are very helpfull

    I didn't find this anyware, just what i wanted, thanks again
    Frederico Regateiro
    Wednesday, January 13, 2010 9:09 AM