The other day I was working on a Silverlight app when I got this error:  Layout Cycle Detected.  The good news is that Silverlight actually gave me an error message that meant something.  That is unusual for Silverlight.  Of course the message didn’t tell me what caused it, or how to fix it!  Luckily, there is this thing called the Internet ;-).  I found some information on David Yack’s blog (I don’t know him) that helped.

It turns out that Silverlight doesn’t like it if you put too many TextBoxes in a ListBox (or possibly any other “repeater” kind of list control).  I’m not sure why it only applies to TextBoxes and not other controls, but here is my understanding of the problem…  Each time you add a TextBox to a ListBox it needs to figure out how much space to give the control, as well as all the other controls around it.  It figures that out, and then sizes all of the controls that don’t have specific sizes set, and then lays everything out for you.  But I guess in a list, it needs to do this over and over and over again and Silverlight thinks it has gone into an endless loop, so at a certain point it cuts it off and says “Layout Cycle Detected”.  Each time a control gets resized, the controls around it get resized, and that goes on and on until you get to the top level control.  I guess I can understand that it could be a problem, but I think the developers at Microsoft have got to come up with a better way to resolve this.  If I want to put a lot of TextBoxes in a list, I should be allowed to do so!  Also, the limit is supposedly 250 TextBoxes, but I am pretty sure I had less than that.

There are a few ways to get around the problem, as far as I know.  In my case there are a bunch of controls that made up the DataTemplate for my ListBox.  Since they are in a StackPanel, I put a fixed size on the StackPanel.  That prevents the “bubble up” of resizing going all the way to the page level.  That seemed to fix my problem most of the time, but not all of the time.  I could probably have kept playing with the layout and putting fixed sizes on more panels.  Instead, I gave in. 

I didn’t want to fight with Silverlight any more so I changed all of my TextBoxes to TextBlocks.  Then I set the the ListBox items up so that you can switch them into “Edit Mode”.  The problem here is that the ListBox doesn’t have built in support for that.  So here is how I did it.  By the way, this is not dependent upon the “SelectedItem” in the ListBox, so you can have multiple items in EditMode at the same time.

The purpose of my list is to display a list of Services to the user.  My ListBox is bound to a collection: ObservableCollection<ServiceListItem>.  ServiceListItem is just a class that represents my data.  Among it’s other properties, the ServiceListItem has a property for IsInEditMode.  It is nothing fancy:

            public bool IsInEditMode
            {
                get { return _isInEditMode; }
                set
                {
                    if (_isInEditMode == value)
                        return;
                    _isInEditMode = value;
                    NotifyPropertyChanged("IsInEditMode");
                }
            }
            private bool _isInEditMode;

In my DataTemplate for the ListBox I have two StackPanels, one for ReadOnly mode and one for Edit Mode.  The ReadOnly template has a button to switch to EditMode.  The EditMode template has two buttons.  The Cancel button reverts the data to it’s original state and switched to ReadOnly mode.  The Save button saves the data and switched to ReadOnly Mode.  Here is the one that puts the template into Edit Mode (nothing fancy here):

<Button x:Name="EditService" Click="EditService_Click">
    <Button.Template>
        <ControlTemplate>
            <Image Source="../images/pencil.png" 
                   Height="10" 
                   Width="10" 
                   Cursor="Hand" 
                   VerticalAlignment="Center" 
                   HorizontalAlignment="Center"/>
        </ControlTemplate>
    </Button.Template>
</Button>

The code for the button click is pretty simple too:

        private void EditService_Click(object sender, RoutedEventArgs e)
        {
            ServiceListItem item = (ServiceListItem)((Button)sender).DataContext;
            item.IsInEditMode = true;
        }

Actually, the only cool part of this is how the control figures out which StackPanel to display.  Each one has it’s Visibility Property bound to the IsInEditMode property!

                           <StackPanel x:Name=”EditPanel”
                                       Visibility=”{Binding IsInEditMode, Converter={StaticResource BoolVisibilityConverter}}” 
                                       Orientation=”Horizontal” Height=”20″ >

The trick is the Converter.  We’re using a Converter that converts a boolean value into a value from the Visibility Enumeration:

 

    public class BoolVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || string.IsNullOrEmpty(value.ToString()) || !(bool)value)
                return Visibility.Collapsed;

            return Visibility.Visible;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // we don't need it to convert back but we could implement that later.
            return value;
        }
    }
Don't forget to add the Converter to your user control's resources:
    <UserControl.Resources>
        <ClientUtilties:BoolVisibilityConverter x:Key="BoolVisibilityConverter" />
        <ClientUtilties:BoolInverseVisibilityConverter x:Key="BoolInverseVisibilityConverter" />
    </UserControl.Resources>

And it all just works!
From this, you should be able to figure out the other end of this, switching back to Read Only Mode. 
I hope this helps.  

One thought on “Silverlight – Error: Layout Cycle Detected and a ListBox that supports "Edit Mode"

Leave a reply

required

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>