Thursday, March 24, 2011

Intermittent InavlidCastException in a UserControl.InitializeComponent()

Thanks in advance for any help with this. It is driving me nuts. Here is the setup:

I have a Silverlight Control Library "Controls", in which I have a customer control defined for presenting dialogs:

public class Dialog : ContentControl
{
    public Dialog()
        : base()
    {
        DefaultStyleKey = typeof(Dialog);
    }

<...normal custom control stuff...>
}

also the default style is in generic.xaml:

  <Style TargetType="src_general:Dialog">
        <Setter Property="Padding" Value="25"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="src_general:Dialog">
                    <Grid x:Name="RootElement" >
                        <vsm:VisualStateManager.VisualStateGroups>
                            <vsm:VisualStateGroup x:Name="DiakogStyleStates">
                                <vsm:VisualState x:Name="OkCancel">
                                    <Storyboard>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="OkOnly">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="CancelButton" Storyboard.TargetProperty="Visibility" >
                                            <DiscreteObjectKeyFrame KeyTime="0:0:0" >
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="CancelOnly">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="OkButton" Storyboard.TargetProperty="Visibility" >
                                            <DiscreteObjectKeyFrame KeyTime="0:0:0" >
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="None">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="CancelButton" Storyboard.TargetProperty="Visibility" >
                                            <DiscreteObjectKeyFrame KeyTime="0:0:0" >
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>

                                        <ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="OkButton" Storyboard.TargetProperty="Visibility" >
                                            <DiscreteObjectKeyFrame KeyTime="0:0:0" >
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                            </vsm:VisualStateGroup>
                        </vsm:VisualStateManager.VisualStateGroups>

                        <Popup x:Name="DialogPopup">
                            <src_general:WindowFrame x:Name="Frame">
                                <Grid >
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*" />
                                        <RowDefinition Height="Auto" />
                                    </Grid.RowDefinitions>

                                    <ContentPresenter Grid.Row="0" x:Name="ContentPresenter" Margin="{TemplateBinding Padding}"/>

                                    <!--Action Buttons-->
                                    <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="15">
                                        <src_general:GlassButton x:Name="CancelButton" Content="Cancel" Margin="2"/>
                                        <src_general:GlassButton x:Name="OkButton" Content="Ok" Margin="2"/>
                                    </StackPanel>
                                </Grid>

                            </src_general:WindowFrame>
                        </Popup>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

I use this dialog in a lot of places with no problem. However, in one application, nested about 3-4 usercontrols from the RootVisual, I use it the following way:

<general:Dialog x:Name="AddUpdateDialog" DialogStyle="OkCancel" Title="Add/Update Connection" Closed="AddUpdateDialog_Closed" ValidationGroup="AddConnection">
            <Grid Width="300">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="10"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" Style="{StaticResource LabelText}"/>
                <TextBox Grid.Row="0" Grid.Column="2" Text="{Binding Name, Mode=TwoWay}" Style="{StaticResource TextBoxInput}" MaxLength="49">
                    <val:ValidationManager.Validator>
                        <val:RequiredValidator ManagerName="AddConnection" ErrorMessage="Name is required."/>
                    </val:ValidationManager.Validator>
                </TextBox>
            </Grid>
        </general:Dialog>

When I run this app I intermittently (about every 5-10 starts get the following exception: "Unable to cast object of type 'System.Windows.Controls.ContentControl' to type 'hookitupright.com.silverlight.controls.general.Dialog'." that occurs in the InitializeComponent() for the parent UserControl of the above XAML. To be sepcific, it occurs right here:

           this.AddUpdateDialog = ((hookitupright.com.silverlight.controls.general.Dialog)(this.FindName("AddUpdateDialog")));

When I put a breakpoint there, most of the time the FindName returns a Dialog typed object, but sometimes, it returns a ContentControl (the base for Dialog) and it fails. The XAML has not changed. It is static...Since the exception is intermittent and occurs within generated code I am at a loss.

I have tried: 1) Moved all of the content for the Dialog into a separate UserControl - only seemed to make problem worse 2) Comment out parts and see when it works...well, if I comment out the TextBox completely, it no longer fails. Everything else (including the custom validation attached property) seems to have no impact. 2a) Thinking it may have something to do with the TwoWay binding to the TextBox, I removed the binding. Still fail.

UPDATE: So given 2) above I left the Textbox commented out decided to move on to other things and come back to this with hopes that something will reveal itself to me. Unfortunately, it seems to also fail with the textbox out, just less frequently.

In addition, I have this control in the exact same configuration in another usercontrol in the same app (and at the same level in the VisualTree) and it does not fail at all. So I literally copied and pasted the failing XAML into the Main.xaml (my root visual) and of course, it does not fail there either. Assuming that the the XAML is loaded in sequence (top to bottom) the failing control is likely one of the last ones loaded. My only hypothesis now is that there is some timing thing that is happening whereby as Iam still loading the visual tree, I start to get *Completed events from the loading of the data via WCF service and that these trigger a layout before the visual tree is fully loaded which causes some ill side effects... I will test this.

The problem is that it does NOT fail every time. It blows up about 20% of the time. When it works, everything works even this dialog???

This problem is related if not the same problem: When I "fix" the invalidcast by commenting out needed functionality, I will far less frequently but intermittently get this invalid attribute (when the attribute/property is in fact there). http://stackoverflow.com/questions/229117/xamlparseexception-attribute-in-custom-control-missing-but-its-defined#473015

Help please.

From stackoverflow
  • After much, much research and frustration, I believe it to be related to this: http://blogs.msdn.com/silverlight_sdk/archive/2008/10/24/loaded-event-timing-in-silverlight.aspx

    The Loaded event in Silverlight does not match the definition of (a) what it is in WPF and consequently (b) the definition that they pasted into the Silverlight documentation from WPF

    WTF???

    I have modified some code based on the above and now it does not seem to fail. I wish I could tell you exactly why it did what did and why what I did fixed it (or at least masked it again??), but I can't. Maybe someone else will encounter this and can figure it out.

    Here is what I was doing and what I changed:

    The Textbox above makes use of an Attached Property for a Validator (like the ASP.NET validators and similar to the ones in the Silverlight Toolkit). When a Validator (base class for RequireValidator) is attached to a control, it links to the control to provide validation as an attached behavior. The trick is that it then tries to Validate() the current control. For a RequiredValidator on a TextBox control it calls string.IsNullOrEmpty() for the Text property on the linkedControl. All of this works fine as these are just properties on the TextBox Control. However, just after that, the validator needs to somehow tell the control to display the error indicator and any error message. I do this through providing a custom Style that includes two new VisualStates ("ValidInput" & "InvalidInput") in their own VisualStateGroup ("ValidationStates") and that one of the Style's Elements is a Control that supports and interface called IValidationNotificationControl.

    Ok, enough background. From the documentation, I knew that I could only access the elements of the TextBox Style (the new visualStates and the notification icon) AFTER the template had been applied so I hooked into the Loaded event for the linkelement for the validator and called Validate() there for the first time. Well, someone at MS saved about 15 minutes by copying an pasting the Loaded Event description from WPF and that cost me about 3-4 days of heartache. Turns out that there is no guarantee that the template is applied then in Loaded Event in silverlight. So, I simply added a call to ApplyTemplate() in the Loaded event handler for the linkedelement and the failure went away.

    My guess is that the error was intermittent because the Layout (and therefore the application of the Template) occurs asynchronously and sometimes it was there when I hit the Loaded Event and sometimes it was not. But I still think that it is a Silverlight Bug as to where the error manifested itself and may even point to a security hole (if, but some action, somewhere else in the code, I can cause the XAML Parser to return a type different than what teh actual XAMl indicates...). Oh well...it seems to be working now. Maybe this will help someone else.

  • Maybe this helps someone too: take a look at my answer in another thread:

0 comments:

Post a Comment