locked
Customer Activity Designer Problem - "Could not generate view for VisualBasicValue`1" RRS feed

  • Question

  • I'm reading Bruce Bukovics Pro WF4 book and in chapter 16 - Advanced Customer Activites, he details emulating a Sequence activity along with a custom designer. When I drop the custom activity into a new xaml activity, I'm getting all sorts of wierdness. The custom sequence activity has a condition property that determines whether to schedule each child activity contained within. Once I set a value in the ExpressionTextBox for the activity, Visual Studio starts showing in red "Could not generate view for VisualBasicValue`1" along with this error showing up in my error window:

    The activity 'VisualBasicValue<Boolean>' cannot be referenced by activity 'MySequence' because the latter is not in another activity's implementation.  An activity can only be referenced by the implementation of an activity which specifies that activity as a child or import.  Activity 'VisualBasicValue<Boolean>' is declared by activity 'MySequence'.

    Here is the source for the activity itself:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Activities;
    using System.ComponentModel;
    using System.Collections.ObjectModel;
    
    namespace ActivityLibrary
    {
    	[Designer(typeof(ActivityLibrary.Design.MySequenceDesigner))]
    	public sealed class MySequence : NativeActivity
    	{
    		[Browsable(false)]
    		public Collection<Activity> Activities { get; set; }
    
    		[RequiredArgument]
    		public Activity<bool> Condition { get; set; }
    
    		Variable<int> activityIndex = new Variable<int>("ActivityIndex", 0);
    
    		public MySequence()
    		{
    			Activities = new Collection<Activity>();
    		}
    
    		protected override void CacheMetadata(NativeActivityMetadata metadata)
    		{
    			Console.WriteLine("CacheMetadata");
    			metadata.SetChildrenCollection(Activities);
    			metadata.AddChild(Condition);
    			metadata.AddImplementationVariable(activityIndex);
    		}
    
    		protected override void Execute(NativeActivityContext context)
    		{
    			if (Condition != null)
    			{
    				Console.WriteLine("Executed Scheduled Condition");
    				context.ScheduleActivity<bool>(Condition, OnConditionComplete);
    			}
    		}
    
    		void OnConditionComplete(NativeActivityContext context, ActivityInstance completedInstance, bool result)
    		{
    			Console.WriteLine("OnConditionComplete: State:{0}, IsCompleted:{1}: Result:{2}", completedInstance.State, completedInstance.IsCompleted, result);
    			if (!context.IsCancellationRequested && result)
    			{
    				int index = activityIndex.Get(context);
    				if (index < Activities.Count)
    				{
    					Console.WriteLine("OnConditionComplete Scheduled Activity: {0}", Activities[index].DisplayName);
    					context.ScheduleActivity(Activities[index], OnComplete, OnFaulted);
    					index++;
    					activityIndex.Set(context, index);
    				}
    			}
    		}
    
    		void OnComplete(NativeActivityContext context, ActivityInstance completedInstance)
    		{
    			Console.WriteLine("OnComplete: State:{0}, IsCompleted:{1}", completedInstance.State, completedInstance.IsCompleted);
    
    			if (!context.IsCancellationRequested)
    			{
    				if (Condition != null)
    				{
    					Console.WriteLine("OnComplete Scheduled Condition");
    					context.ScheduleActivity<bool>(Condition, OnConditionComplete, OnFaulted);
    				}
    			}
    		}
    
    		void OnFaulted(NativeActivityFaultContext context, Exception propagatedException, ActivityInstance propagatedFrom)
    		{
    			Console.WriteLine("OnFaulted: {0}", propagatedException.Message);
    		}
    
    		protected override void Cancel(NativeActivityContext context)
    		{
    			Console.WriteLine("Cancel");
    			if (context.IsCancellationRequested)
    			{
    				Console.WriteLine("IsCancellationRequested");
    				context.CancelChildren();
    			}
    		}
    
    		protected override void Abort(NativeActivityAbortContext context)
    		{
    			base.Abort(context);
    			Console.WriteLine("Abort Reason: {0}", context.Reason.Message);
    		}
    	}
    }
    
    

    And here is the xaml for the designer:

     

    <sap:ActivityDesigner x:Class="ActivityLibrary.Design.MySequenceDesigner"
    		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    		xmlns:s="clr-namespace:System;assembly=mscorlib"
    		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    		xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
    		xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
    		xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation"
    		xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities"
    	 Collapsible="True">
    	<sap:ActivityDesigner.Resources>
    		<DataTemplate x:Key="ShowAsCollapsed">
    			<TextBlock Foreground="Gray">
    				<TextBlock.Text>
    					<MultiBinding StringFormat="Expand for {0} Activities">
    						<Binding Path="ModelItem.Activities.Count" />
    					</MultiBinding>
    				</TextBlock.Text>
    			</TextBlock>
    		</DataTemplate>
    		<DataTemplate x:Key="ShowAsExpanded">
    			<Grid>
    				<Grid.ColumnDefinitions>
    					<ColumnDefinition />
    					<ColumnDefinition />
    				</Grid.ColumnDefinitions>
    				<Grid.RowDefinitions>
    					<RowDefinition />
    					<RowDefinition />
    				</Grid.RowDefinitions>
    				<TextBlock Text="Condition" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" />
    				<sapv:ExpressionTextBox HintText="Enter a condition" Grid.Row="0" Grid.Column="1" MaxWidth="150" MinWidth="150" Margin="5"
    																OwnerActivity="{Binding Path=ModelItem}" ExpressionType="{x:Type TypeName=s:Boolean}"
    																Expression="{Binding Path=ModelItem.Condition, Mode=TwoWay}" />
    				<sap:WorkflowItemsPresenter Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" HintText="Drop activities here" Margin="5" MinHeight="100"
    																	 Items="{Binding Path=ModelItem.Activities, Mode=TwoWay}">
    					<sap:WorkflowItemsPresenter.SpacerTemplate>
    						<DataTemplate>
    							<Rectangle Width="140" Height="3" Fill="LightGray" Margin="7" />
    						</DataTemplate>
    					</sap:WorkflowItemsPresenter.SpacerTemplate>
    					<sap:WorkflowItemsPresenter.ItemsPanel>
    						<ItemsPanelTemplate>
    							<StackPanel Orientation="Vertical" />
    						</ItemsPanelTemplate>
    					</sap:WorkflowItemsPresenter.ItemsPanel>
    				</sap:WorkflowItemsPresenter>
    			</Grid>
    		</DataTemplate>
    		<Style x:Key="StyleWithCollapse" TargetType="{x:Type ContentPresenter}">
    			<Setter Property="ContentTemplate" Value="{DynamicResource ShowAsExpanded}" />
    			<Style.Triggers>
    				<DataTrigger Binding="{Binding Path=ShowExpanded}" Value="False">
    					<Setter Property="ContentTemplate" Value="{DynamicResource ShowAsCollapsed}" />
    				</DataTrigger>
    			</Style.Triggers>
    		</Style>
    	</sap:ActivityDesigner.Resources>
    	<Grid>
    		<ContentPresenter Style="{DynamicResource StyleWithCollapse}" Content="{Binding}" />
    	</Grid>
    </sap:ActivityDesigner>
    
    Any advice or assistance on why this is happening would be greatly appreciated!
    Blades Don't Need Reloading...
    Thursday, August 26, 2010 6:29 PM

Answers

  • Ah. apologies for the confusing previous post.

    First a code sample of what (hopefully) is the right cache metadata code:

            protected override void CacheMetadata(NativeActivityMetadata metadata)
            {
                Console.WriteLine("CacheMetadata");
                this.Activities.ToList().ForEach(a => metadata.AddChild(a));
                metadata.AddChild(Condition);
                metadata.AddImplementationVariable(activityIndex);
            }

    Under the covers, SetChildrenCollection takes the collection you pass in and sets the collection of to what you pass in by reference.  Afterwards, calling AddChild adds the collection to the the ChildrenCollection (and thus to your Activities collection as well).  As cache metadata is called multiple times, this results in multiple entries for your condition activity, which in turn generates the error message you saw. 

    The above code should work for your scenario.

    Also, I'd like to apologize again for the confusion in my previous posts.  Hopefully this will solve your problem.

    -Eric

    Friday, August 27, 2010 1:07 AM

All replies

  • Hello James,

    I think your problem comes from the following lines:

      protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
    Console.WriteLine("CacheMetadata");
    metadata.SetChildrenCollection(Activities);
    metadata.AddChild(Condition);
    metadata.AddImplementationVariable(activityIndex);
    }

    AddChild should be AddImplementationChild.

    Thanks,

    Eric

    Thursday, August 26, 2010 9:05 PM
  • It's not an implementation child though since it can be set at design time right?
    Blades Don't Need Reloading...
    Thursday, August 26, 2010 9:22 PM
  • My mistake.  You are entirely correct.  I looked at the error message and for some reason assumed that condition was internal to your activity. Apologies.

    What you need to do is simply add the Condition activity to your ChildrenCollection (In this case, it's your Activities collection) and you should be good to go.

    -Eric

    Thursday, August 26, 2010 10:32 PM
  • That doesn't work though because foreach each activity in the Activities collection, before I schedule each of them to execute (via ScheduleActivity), I want to execute the Condition property (returning a bool) to determine whether or not I should execute the next Activity in the Activities collection.


    Blades Don't Need Reloading...
    Friday, August 27, 2010 12:18 AM
  • Ah. apologies for the confusing previous post.

    First a code sample of what (hopefully) is the right cache metadata code:

            protected override void CacheMetadata(NativeActivityMetadata metadata)
            {
                Console.WriteLine("CacheMetadata");
                this.Activities.ToList().ForEach(a => metadata.AddChild(a));
                metadata.AddChild(Condition);
                metadata.AddImplementationVariable(activityIndex);
            }

    Under the covers, SetChildrenCollection takes the collection you pass in and sets the collection of to what you pass in by reference.  Afterwards, calling AddChild adds the collection to the the ChildrenCollection (and thus to your Activities collection as well).  As cache metadata is called multiple times, this results in multiple entries for your condition activity, which in turn generates the error message you saw. 

    The above code should work for your scenario.

    Also, I'd like to apologize again for the confusion in my previous posts.  Hopefully this will solve your problem.

    -Eric

    Friday, August 27, 2010 1:07 AM
  • Eric,

    Thanks, I think I follow. If this is the case though, why would anyone ever want to call SetChildrenCollection?


    Blades Don't Need Reloading...
    Friday, August 27, 2010 1:41 PM
  • SetChildrenCollection works well in the a case like Sequence activity, where the set of all children is really contained in an existing collection, and it makes more sense to reuse that collection than create a new one (via AddChild implicitly, or explicitly). Otherwise it's simpler just to use AddChild.

    Tim

    Sunday, August 29, 2010 8:00 PM