开发者

Silverlight 4.0 Cannot create an instance of "CustomControl"

I'm currently working on a small project, and I am having some trouble with silverlight custom controls. I am trying to create a custom ScrollViewer (that will contain small images) for photo gallery. The problem I have, is that Visual Studio is throwing the following error:

Cannot create an instance of ScrollableImageViewer

Could someone provide some pointers as to what might be the cause of this error, and maybe some ways to fix it?

Below, is the XAML and C# code that I wrote.

<navigation:Page x:Class="PWS.Views.Biography" 
       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:local="clr-namespace:PWS.Controls"
       mc:Ignorable="d"
       xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
       d:DesignWidth="640" d:DesignHeight="480"
       Title="This is the biography page">
<Grid x:Name="LayoutRoot">
    <TextBlock x:Name ="tempText" FontSize="15" Foreground="Blue">this is the biography page</TextBlock>
    <local:ScrollableImageViewer x:Name= "scrollableViewer" />
</Grid>
</navigation:Page>

My custom control class is called ScrollableImageViewer and below you can see the code for it.

namespace PWS.Controls
{
    [ContentProperty("Items")]
    public class ScrollableImageViewer : Control
    {
        public static DependencyProperty ItemsProperty = DependencyProperty.RegisterAttached("Items", typeof(IList<UIElement>), typeof(ScrollableImageViewer), new PropertyMetadata(""));

        public ScrollableImageViewer()
        {
            this.DefaultStyleKey = typeof(ScrollableImageViewer);
            this.Items = new List<UIElement>();
        }

        public IList<UIElement> Items
        {
            get { return (IList<UIElement>)GetValue(ScrollableImageViewer.ItemsProperty); }
      开发者_运维问答      set { SetValue(ScrollableImageViewer.ItemsProperty, value); }
        }
    }
}

At the moment it is pretty simple and does not contain the custom logic for scrolling.

One more thing that I have to add is the generic.xaml file that holds the template for this control.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:PWS.Controls"
    >
  <Style TargetType="local:ScrollableImageViewer">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:ScrollableImageViewer">
          <Grid Background="{TemplateBinding Background}">
            <ScrollViewer x:Name="internalScrollViewer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                          Background="{TemplateBinding Background}"
                          VerticalScrollBarVisibility="Disabled"
                          HorizontalScrollBarVisibility="Hidden"
                          >
              <StackPanel Background="{TemplateBinding Background}" FlowDirection="LeftToRight" IsHitTestVisible="False" Children="{TemplateBinding Items}"></StackPanel>
            </ScrollViewer>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

As you can see from the template, I want my control to contain an internal ScrollViewer and a StackPanel whos Children property should be bound to the Items property of the custom control.

P.S I did some search on SO and found some questions regarding similar problems. Most of them were solved by adding code to the constructor to check if the control was displayed in the designer mode, but this did not work for me. It seems that I have a different problem.

P.P.S I'm just starting to learn Silverlight, so if you see some bad code please feel free to point it out to me.


when working with custom controls you have to deal with a lot of rules. For start lets assure this:

  1. Your project must have a "themes" folder and inside it a "Generic.xaml" resource dictionary that holds the style with your template.
  2. When defining a dependency property you cannot initialize it with values from different types. Your Items property is getting initialized with an empty string (""), this is the initial value for your property inside the "PropertyMetadata". A "null" or a "new ..." should do the trick.

Stated that, I offer you a work around to the Items property: give your StackPanel a name, access it from the "OnApplyTemplate" method. Use a temporal StackPanel to hold possible children that might be added before the real template is applied to your control and, when this really happens, pass the children from the temporal StackPanel to that in the template. For this to work use a NamedPart for your StackPanel.

Here is the code xaml:

<Style TargetType="local:ScrollableImageViewer">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:ScrollableImageViewer">
                <Grid Background="{TemplateBinding Background}">
                    <ScrollViewer x:Name="internalScrollViewer"
                                                HorizontalAlignment="Stretch"
                                                VerticalAlignment="Stretch"
                                                Background="{TemplateBinding Background}"
                                                VerticalScrollBarVisibility="Disabled"
                                                HorizontalScrollBarVisibility="Hidden">
                        <StackPanel x:Name="myStackPanel"
                                                Background="{TemplateBinding Background}"
                                                FlowDirection="LeftToRight"
                                                IsHitTestVisible="False"></StackPanel>
                    </ScrollViewer>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

This is code behind:

[TemplatePart(Name = "myStackPanel", Type = typeof(StackPanel))]
[ContentProperty("Items")]
public class ScrollableImageViewer : Control
{
    public ScrollableImageViewer()
    {
        this.DefaultStyleKey = typeof(ScrollableImageViewer);
    }

    StackPanel MyStackPanel = null;
    StackPanel TemporalSP = new StackPanel();

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        MyStackPanel = GetTemplateChild("myStackPanel") as StackPanel;

        // Pass created children before appliying the template.
        for (int i = TemporalSP.Children.Count - 1; i >= 0; i--)
        {
            UIElement OneControl = TemporalSP.Children[i];
            TemporalSP.Children.Remove(OneControl);
            MyStackPanel.Children.Add(OneControl);
        }
        TemporalSP = null;

    }

    public UIElementCollection Items
    {
        get
        {
            if (MyStackPanel != null)
                return MyStackPanel.Children;
            else
                return TemporalSP.Children;
        }
    }

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜