locked
Multiple instances of a control in the same page RRS feed

  • Question

  • So, I am glad that Expression allows you to create user controls. If you are designing a control and want to make sure that you don't have to make corrections in every instance of it over your project, it's perfect.

    Now, I can't seem to understand why I can't have more than 1 of the same control in a specific page.

    My case is simple: I want to design a UI element that is like a mail inbox. In this inbox, I want to display several "mails". So I design 1 mail as a component, and create different states for that mail, for hover for instance. I want then to display a list of them in a stackpanel, to simulate the behavior of a mail.

    I got my main page:

    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	xmlns:local="clr-namespace:Mail_InboxScreens"
    	x:Class="Mail_InboxScreens.Screen_1"
    	Width="1200" Height="900">
    
    	<Grid x:Name="LayoutRoot" Background="White">
    		<local:Mail Margin="138,278,462,0" VerticalAlignment="Top" d:LayoutOverrides="Height"/>
    	</Grid>
    </UserControl>

    And my mail:
    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	x:Name="userControl" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
    	x:Class="Mail_InboxScreens.Mail"
    	d:DesignWidth="640" Height="32" Width="600">
    
    	<Grid x:Name="LayoutRoot">
    		<i:Interaction.Triggers>
    			<i:EventTrigger EventName="MouseEnter">
    				<ic:GoToStateAction StateName="Expanded"/>
    			</i:EventTrigger>
    			<i:EventTrigger EventName="MouseLeave">
    				<ic:GoToStateAction StateName="Line"/>
    			</i:EventTrigger>
    		</i:Interaction.Triggers>
    		<VisualStateManager.VisualStateGroups>
    			<VisualStateGroup x:Name="VisualStateGroup">
    				<VisualState x:Name="Line"/>
    				<VisualState x:Name="Expanded">
    					<Storyboard>
    						<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="userControl" Storyboard.TargetProperty="(FrameworkElement.Height)">
    							<EasingDoubleKeyFrame KeyTime="00:00:00" Value="300"/>
    						</DoubleAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    			</VisualStateGroup>
    		</VisualStateManager.VisualStateGroups>
    		<Rectangle Stroke="Black" RadiusX="4" RadiusY="4" Margin="0">
    			<Rectangle.Fill>
    				<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
    					<GradientStop Color="Black" Offset="1"/>
    					<GradientStop Color="#FF656565"/>
    				</LinearGradientBrush>
    			</Rectangle.Fill>
    		</Rectangle>
    	</Grid>
    </UserControl>
    This works as intended as long as I only have 1 mail on the page: the mail displays correctly, and on hover, expands.


    However, there is already the message "Using more than one instance of a named UserControl in the same XAML file may cause errors", which points to the "x:Name="userControl" line of the mail XAML.


    Now, if I want to insert a second instance of that mail on the same page, the main page becomes:

    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	xmlns:local="clr-namespace:Mail_InboxScreens"
    	x:Class="Mail_InboxScreens.Screen_1"
    	Width="1200" Height="900">
    
    	<Grid x:Name="LayoutRoot" Background="White">
    		<local:Mail Margin="138,278,462,0" VerticalAlignment="Top" d:LayoutOverrides="Height"/>
    		<local:Mail Margin="138,359,462,0" VerticalAlignment="Top" d:LayoutOverrides="Height"/>
    	</Grid>
    </UserControl>
    And the page doesn't work anymore. To be precise, the build completes, doesn't retrieve any error outside of the above mentioned error, but when I press F5, the browser does display the Sketchflow interface, but not my page.

    I do understand that somewhere, somehow, there is a naming conflict. I need to identify uniquely each instance of the user control that I put on the page. However, even if I rename each instance individually:

    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	xmlns:local="clr-namespace:Mail_InboxScreens"
    	x:Class="Mail_InboxScreens.Screen_1"
    	Width="1200" Height="900">
    
    	<Grid x:Name="LayoutRoot" Background="White">
    		<local:Mail x:Name="Mail_1" Margin="138,278,462,0" VerticalAlignment="Top" d:LayoutOverrides="Height"/>
    		<local:Mail x:Name="Mail_2" Margin="138,359,462,0" VerticalAlignment="Top" d:LayoutOverrides="Height"/>
    	</Grid>
    </UserControl>
    The page loads and displays both instances, but they have lost their interactivity: if I hover on them, there is no change of state anymore.

    So, the question is:

    What do I need to do to be able to create multiple instances of the same user control on the same page, so that they retain their interactivity? I want to have active buttons that have actions on the different states.
    Monday, November 30, 2009 11:28 AM

All replies

  • One solution as I envision it might be to create a template for this mail userControl... but for whatever reason I cannot create templates from existing user controls it seems.

    If I select a user control like my mail above, the "edit template" functionality is disabled. Maybe if I understand why the edit template feature is disabled that might help...
    Monday, November 30, 2009 11:35 AM
  • Also, if I copy paste back the first XAML file in my home page, it works again correctly. If I try to rename the control in the page, then it stops working... and if I remove the name, it works again...

    Why is that? Why renaming a control prevents it to work?
    Monday, November 30, 2009 11:54 AM
  • Bumping this up as it really is blocking me for further progress. Thanks a lot if anyone can help!!
    Wednesday, December 2, 2009 9:06 AM
  • Hello Eric,

    The restriction on naming does not make sense to me since this is suppose to be a variable holding a unique reference to the instance. Maybe this is just a restriction imposed by Blend.

    However you can avoid naming the user control in this case by having the storyboard refer to the LayoutRoot and using MinHeight and MinWidth values at this level. Also set the top level Width and Height on the UserControl to Auto.

    UserControl code:
    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    	xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
        mc:Ignorable="d"
    	x:Class="SilverlightPrototype1Screens.MyUc"
    	Width="Auto" Height="Auto" 
    	
    	>
    
    	<Grid MinWidth="100" MinHeight="30" x:Name="LayoutRoot">
           <i:Interaction.Triggers>
    			<i:EventTrigger EventName="MouseEnter">
    				<ic:GoToStateAction StateName="Expanded"/>
    			</i:EventTrigger>
    			<i:EventTrigger EventName="MouseLeave">
    				<ic:GoToStateAction StateName="Line"/>
    			</i:EventTrigger>
    		</i:Interaction.Triggers>
    		<VisualStateManager.VisualStateGroups>
    			<VisualStateGroup x:Name="VisualStateGroup">
    				<VisualState x:Name="Line"/>
    				<VisualState x:Name="Expanded">
    					<Storyboard>
    						<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(FrameworkElement.Height)">
    							<EasingDoubleKeyFrame KeyTime="00:00:00" Value="300"/>
    						</DoubleAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    			</VisualStateGroup>
    		</VisualStateManager.VisualStateGroups>
    		<Rectangle Stroke="Black" RadiusX="4" RadiusY="4" Margin="0">
    			<Rectangle.Fill>
    				<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
    					<GradientStop Color="Black" Offset="1"/>
    					<GradientStop Color="#FF656565"/>
    				</LinearGradientBrush>
    			</Rectangle.Fill>
    		</Rectangle>
        </Grid>
    </UserControl>
    Then to use this in a SketchFlow page:
    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	xmlns:local="clr-namespace:SilverlightPrototype1Screens"
    	x:Class="SilverlightPrototype1Screens.Screen_1_1"
    	Width="640" Height="480">
    
    	<Grid x:Name="LayoutRoot" Background="White">
    		<Grid Margin="48,113,112,122">
    			<StackPanel Margin="162,8,142,40">
    				<local:MyUc Margin="0" VerticalAlignment="Top"/>
    				<local:MyUc Margin="0" VerticalAlignment="Top"/>
    				<local:MyUc Margin="0" VerticalAlignment="Top"/>
    				<local:MyUc Margin="0" VerticalAlignment="Top"/>
    			</StackPanel>
    		</Grid>
    	</Grid>
    </UserControl>

    The controls expand to a height of 300 as desired.


    As far as using a Control Template is concerned a Control is really designed around the lookless model. Once you define the template within a Control you can edit it freely.

    Hope this helps

    regards

    Andrew

    Ps now if I could only work out why only sometimes the code I paste here is in colour... oh well
    Wednesday, December 2, 2009 7:11 PM
  • Eric, did you get this sorted out with Andrew's help?
    Friday, December 4, 2009 6:48 AM
  • Sorry for lag, I was really busy lately.

    I just tried the above solution, but I can't build the project.

    However, that probably wouldn't solve my problem: I definitely would have to name each of the instances of the control, as I would like to make sure I don't confuse them
    Monday, December 14, 2009 1:41 PM
  • I haven't done a ton of interactivity before, but have had multiple instances of a usercontrol on a page with different names.  But the thing is that you have to do it in code-behind.  Where are the usercontrols added?  Let's say you wanted them on a canvas.  Name the canvas "mycanvas".  Then in code-behind, it could look like this:

    for(int i=0; i<10; i++) {
      string uc_name = String.Format("myusercontrol{0}", i);
      UserControl temp_usercontrol = new UserControl(); // this is bogus, you have to use the class name of your UC
      temp_usercontrol.Name = uc_name;
      mycanvas.Children.Add( temp_usercontrol);
    }
    Would this work for you?  If not, can you tell me why it wouldn't work in your situation? (just curious)
    Tuesday, December 15, 2009 7:13 AM
  • Well the point is -and that's something I pointed before-, that I am not a coder. I haven't the slightest idea where and how I should input the code that you provide.

    Actually my question is more and more: are we supposed to be able to create a template out of any object? Because that might answer my question very much. In the help files, there is a chapter about creating a template, but in my Expression Blend, if I follow their instructions, the option to create a template out of an object is greyed out.

    From the help files, I have the following, but if I create a basic rectangle and try to make a template out of it, the "Create Empty" line from the Template submenu is greyed out. Even if I make a user control out of the rectangle, it is still greyed out. If I make a control out of it, there, I can edit or create a template, but then I must deal with the states that come with that kind of control (if I chose to edit a button control, then I only have 6 states, and cannot add or remove any of them). 

  • In the Objects and Timeline panel or on the artboard, select the object from which you want to create a control template and do one of the following:

    • In the Object menu, point to Edit Template.

    • Right-click the object and then point to Edit Template.

    • In the breadcrumb bar at the top of the artboard, click the name of the object.

  • In the drop-down menu that appears, do one of the following:

    • To create a new empty template, click Create Empty.

    • To create a new template based upon the template currently in use by the selected object (whether it is the default template for the object, or a custom template previously created), click Edit a Copy.

       

      rom the hel

       

Tuesday, December 15, 2009 10:55 AM
  • Your problem is the x:Name in the mail user control:-

    change:-

    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	x:Name="userControl" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
    	x:Class="Mail_InboxScreens.Mail"
    	d:DesignWidth="640" Height="32" Width="600">

    To:

    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
    	x:Class="Mail_InboxScreens.Mail"
    	d:DesignWidth="640" Height="32" Width="600">

    This should work.. If you need to reference part of the template above you {Binding RelativeSource={RelativeSource FindAncestor....}} instead. MUCH safer.
    Wednesday, December 16, 2009 11:41 PM