locked
Communication between data model and ModelItem RRS feed

  • Question

  • I have a custom activity that I want to display with a custom activity designer. The activity has a Property called DemoObject of type DemoObject. A DemoObject has an ObservableCollection<string> property and an ICommand. When the command is executed, a string is added to the collection. Unfortunately I have no luck with databinding. I would like to bind a button to the action and a ListView to the collection. My XAML lookls like this:

    <StackPanel>
        <ListView ItemsSource="{Binding Path=ModelItem.DemoObject.Collection,Mode=TwoWay}"
                  SelectedItem="{Binding Path=ModelItem.DemoObject.SelectedItem}"
                  IsSynchronizedWithCurrentItem="True"
                  MinHeight="50"/>
        <Button Content="+"
                Command="{Binding Path=ModelItem.DemoObject.AddCommand,Converter={StaticResource converter}}" />
    </StackPanel>

    Every time I click the button, a new string is added to the Collection property. It is neither displayed in the ListView nor is it saved in my XAML, though. I realize this is because the ModelItem is not notified about the change.

    If I use a converter for the collection, which returns modelItemCollection.GetCurrentValue(), the data is displayed in the ListView, but the XAML is still not updated. Again, the ModelItem does not know that the Collection property got updated.

    But how can I inform the ModelItem about the change, and where is the best place to do that? I already tried to write a converter which forwards CollectionChanged events from my Collection to the ModelItemCollection. But this did not work because the ModelItemCollection then changed the original collection again, which resulted in an endless update loop.

    Your help would be much appreciated
        Andreas
    Thursday, July 19, 2012 9:08 AM

Answers

  • Hi Tim,

    I started to try your solution and it would probably work. But along the way I had another idea, inspired by your answer (mentioning parameters).

    In my DataTemplate's Button definition I just pass the collection as parameter now.

    <Button Grid.Row="0" Grid.Column="1"
        Content="+"
        Command="{Binding Path=AddCommand,Converter={StaticResource converter}}" 
        CommandParameter="{Binding Path=Collection}" />
    In my Command implementation I can cast the parameter to a ModelItemCollection and everything works fine. While still a little bit hackish, it is a much more satisfying solution. At least until ObservableCollections are supported out of the box in the future :)

    Thank you for your time

      Andreas

    • Marked as answer by abartho Friday, July 27, 2012 9:54 AM
    Friday, July 27, 2012 9:54 AM
  • >I don't know if it is possible to have the commands for all "+" and "-" buttons directly in the activity designer class and still be able to bind them correctly from within my DataTemplate.

    I think that should be achievable, just as long as your command class (may need a custom class) has some bindable dependency property which can be set to the current data object of your data template. The command can use the property as an extra parameter during Command.Execute(). A small custom command class might do this quite easily.
    Tim

    • Marked as answer by abartho Friday, July 27, 2012 9:54 AM
    Thursday, July 26, 2012 8:06 AM

All replies

  • How did you implement the add command? Does it use ModelItemCollection.Add?
    Tim
    Friday, July 20, 2012 5:25 PM
  • No. It is merely a call to the Add() method of my ObservableCollection, because I have no idea how to access the ModelItemCollection from within the class. If it helps I can post some code tomorrow.
    Monday, July 23, 2012 12:33 PM
  • Since your binding is via the ModelItem you would need to update the ModelItemCollection using .Add() otherwise the correct change notifications to make the binding work will not be raised.

    Inside your custom designer code you can navigate to the model item collection by such as (I am partly guessing, not knowing your exact class definitions):

    ModelItemCollection mic = this.ModelItem.Properties["DemoObject"].Value.Properties["Collection"].Collection;

    Tim

    Tuesday, July 24, 2012 1:09 AM
  • Hi Tim,

    I used the direct approach you proposed.

    In my ActivityDesigner I register the ModelItemCollection with the DemoObject class, so the Add-Command can directly work on the ModelItemCollection.
    I find this ugly, because the DemoObject must be aware of ModelItems and ModelItemCollections, but it works. Here is code in case someone else has a similar problem:

    //Codebehind file of DemoDesinger.xaml
    public partial class DemoDesigner
    {
        public DemoDesigner()
        {
            InitializeComponent();
            //defer model item collection registration until data context is set
            DataContextChanged += RegisterModelItemCollection;
        }


        private void RegisterModelItemCollection(object sender, DependencyPropertyChangedEventArgs args)
        {
            ModelItemCollection micCollection =
                    (ModelItemCollection)this.ModelItem.Properties["DemoObject"].Value.Properties["Collection"].Value;
            DemoActivity demoActivity = ModelItem.GetCurrentValue() as DemoActivity;
            //save the model item collection in a private field in DemoObject,
            //so it can be manipulated instead of the original Collection property
            demoActivity.DemoObject.RegisterModelItemCollection(micCollection);
        }
    }


    If you have time I'd like to know the reason why a ModelItemCollection is not informed about changes in an underlying ObservableCollection. Is it an oversight? Are there technical issues?

    Thank you for your help
        Andreas
    Tuesday, July 24, 2012 8:59 AM
  • >I find this ugly, because the DemoObject must be aware of ModelItems and ModelItemCollections

    I would agree it's ugly, I'm still not exactly understanding why you're doing this.

            //save the model item collection in a private field in DemoObject,
            //so it can be manipulated instead of the original Collection property
            demoActivity.DemoObject.RegisterModelItemCollection(micCollection);

    As for the reason why a ModelItemCollection is not informed about changes in an underlying ObservableCollection. I think it is mainly an oversight. All of the System.Activities objects which ModelItemCollection was tested with were not ObservableCollections, they just use plain collection objects. At design time those objects are manipulated by designers purely via the model tree, so there is no need for the Activities classes to be 'design time aware'. Which is kind of nice.
    Tim


    PS, one question, is the command located on your DemoObject class? Wouldn't it simplify things to make it part of the designer class instead?
    Tuesday, July 24, 2012 7:49 PM
  • Hi Tim,

    your two questions are related:

    > I would agree it's ugly, I'm still not exactly understanding why you're doing this.

    //save the model item collection in a private field in DemoObject,
    //so it can be manipulated instead of the original Collection property
    demoActivity.DemoObject.RegisterModelItemCollection(micCollection);

    > PS, one question, is the command located on your DemoObject class? Wouldn't it simplify things to make it part of the designer class instead?

    Yes, the command is located at my DemoObject class. This is the reason why I pass the ModelItemCollection to the DemoObject. Otherwise the command could not lay hands on the ModelItemCollection.
    You are right, moving the command to the designer would make things easier. However, I only posted a simplified version of my problem for the sake of brevity. Actually, I have multiple collections, each of which has its own commands for adding and removing elements:

    This is an activity which wraps a state machine. For each state the user should be able to add a task, together with a follow-up state. This information is used to populate a task list during runtime. In the example, the user would see "TakeOffer" in the task list when the state machine is in state "Offered".

    Because I have multiple ModelItemCollections and multiple Add/Remove commands (one for each state of the state machine), I use an ItemsControl which displays a collection of "DemoObjects". Each DemoObject contains a collection of <TaskId,NextState> pairs and an Add action and a Remove action for that collection. The DemoObjects are styled via a DataTemplate in my activity designer XAML file. The binding to the collection and the commands is also done in the DataTemplate.

    I don't know if it is possible to have the commands for all "+" and "-" buttons directly in the activity designer class and still be able to bind them correctly from within my DataTemplate.

            
    Wednesday, July 25, 2012 8:28 AM
  • >I don't know if it is possible to have the commands for all "+" and "-" buttons directly in the activity designer class and still be able to bind them correctly from within my DataTemplate.

    I think that should be achievable, just as long as your command class (may need a custom class) has some bindable dependency property which can be set to the current data object of your data template. The command can use the property as an extra parameter during Command.Execute(). A small custom command class might do this quite easily.
    Tim

    • Marked as answer by abartho Friday, July 27, 2012 9:54 AM
    Thursday, July 26, 2012 8:06 AM
  • Hi Tim,

    I started to try your solution and it would probably work. But along the way I had another idea, inspired by your answer (mentioning parameters).

    In my DataTemplate's Button definition I just pass the collection as parameter now.

    <Button Grid.Row="0" Grid.Column="1"
        Content="+"
        Command="{Binding Path=AddCommand,Converter={StaticResource converter}}" 
        CommandParameter="{Binding Path=Collection}" />
    In my Command implementation I can cast the parameter to a ModelItemCollection and everything works fine. While still a little bit hackish, it is a much more satisfying solution. At least until ObservableCollections are supported out of the box in the future :)

    Thank you for your time

      Andreas

    • Marked as answer by abartho Friday, July 27, 2012 9:54 AM
    Friday, July 27, 2012 9:54 AM
  • Hi Abartho,

    Can you please explain how you got commands to work, if you could show your view model and your command implementation it would be appreciated.

    Tuesday, October 2, 2018 9:01 AM