Silverlight嵌套自定义控件导致StackOverflowException
本文关键字:StackOverflowException 自定义控件 嵌套 Silverlight | 更新日期: 2023-09-27 17:54:33
我正在为我的Silverlight项目编写一个可重用的控制库。目前我得到一个StackOverflowException每次我启动我的测试应用程序,这是使用控制库。我能够在一个小样本项目中重现异常。
我有两个简单的自定义控件://Control1.cs
public class Control1:Control
{
public Control1()
{
this.DefaultStyleKey = typeof(Control1);
}
}
//Control2.cs
public class Control2:Control
{
public static DependencyProperty TestProperty =
DependencyProperty.Register("Test",typeof(Control1),typeof(Control2),null);
public Control1 Test
{
get {return (Control1)GetValue(TestProperty);}
set {SetValue(TestProperty,value);}
}
public Control2()
{
this.DefaultStyleKey = typeof(Control2);
}
}
Constrol2有一个Control1类型的DependencyProperty。在我的控制库中,Control1类似于按下Control2时传递给菜单的MenuItem。现在在我的主题/通用。xaml定义了控件的两个默认样式:
//generic.xaml
<Style TargetType="local:Control1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Control1">
<Rectangle Width="20" Height="20" Fill="Blue"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local:Control2">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Control2">
<Rectangle Width="20" Height="20" Fill="Red"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Test">
<Setter.Value>
<local:Control1/>
</Setter.Value>
</Setter>
</Style>
现在当我在测试应用程序中使用Control2时,我得到一个StackOverflowException。设置断点时,Control2的构造函数被调用一次,然后Control1的构造函数被连续调用,直到StackOverflowException。
当我改变control1的构造函数直接从一个resourceKey的ResourceDictionary加载它的样式时,一切都加载得很好。但这更像是一种变通而不是解决方案。
当我在泛型中删除TestProperty的Setter时。Xaml一切都很好。然后我就可以在测试应用程序的App.xaml中重写样式,并在那里为TestProperty定义Setter。但那也不是我想做的。
谁有解决这个问题的办法?或者也许有人能解释一下为什么它会这样。Thanks in advance
我自己偶然发现了这个绝妙的效果,结果是:
你不能实例化从UIElement
派生的对象(你的<local:Control1/>
行正是这样做的)在ResourceDictionary
(和泛型)。Xaml是一个),因为该字典中的所有对象必须是可共享的。
记录在这里。相关部门:
可共享类型和元素类型
资源字典是一种定义可共享类型的技术这些类型的值在XAML中。并非所有类型或值都适合以获取资源词典中的用法。想了解更多信息类型在Silverlight中被认为是可共享的,参见参考资料字典。
特别地,所有元素派生类型都是不可共享的,除非它们来自于模板和特定模板上的应用控制实例。除了模板之外,还需要一个UIElement在实例化后仅存在于对象树中的一个位置,并且让一个元素是可共享的可能会违反这一点原则。
将您的control1包装在DataTemplate
中,导致您的control1不在ResourceDictionary
中实例化,而是在实际实例化control2的时间点。
<Style TargetType="OuterControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="OuterControl">
<StackPanel>
<TextBlock Text="Outer Hello World"/>
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<InnerControl/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
和代码
public class OuterControl : Control
{
public OuterControl()
{
DefaultStyleKey = typeof( OuterControl );
}
public InnerControl Content
{
get { return (InnerControl) GetValue( ContentProperty ); }
set { SetValue( ContentProperty, value ); }
}
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register( "Content", typeof( InnerControl ), typeof( OuterControl ), new PropertyMetadata( null ) );
public DataTemplate ContentTemplate
{
get { return (DataTemplate) GetValue( ContentTemplateProperty ); }
set { SetValue( ContentTemplateProperty, value ); }
}
public static readonly DependencyProperty ContentTemplateProperty =
DependencyProperty.Register( "ContentTemplate", typeof( DataTemplate ), typeof( OuterControl ), new PropertyMetadata( null ) );
}