locked
custom control dependency property throws "object reference not set to an instance of an object" after build RRS feed

  • Question

  • I have a custom control that contains 4 visual "icon" states which can be set through a dependency property called "visibleIcon".  During design time, I am able to add the control and set the initial property to say visibleIcon="peach"; however, once the project is built, I get the error "object reference not set to an instance of an object" and the custom control disappears in the designer.

    the custom control is setup similar to the following in themes\generic.xaml :

        <Style TargetType="local:fruitButton">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:fruitButton">
    
                        <Canvas x:Name="PeachIcon" Visibility="Collapsed">
       <Path  Data="..."/>
                                        </Canvas>
    
                        <Canvas x:Name="GrapeIcon" Visibility="Collapsed">
       <Path  Data="..."/>
                                        </Canvas>
    
                        <Canvas x:Name="CherryIcon" Visibility="Collapsed">
       <Path  Data="..."/>
                                        </Canvas>

    in the fruitButton.xaml.cs file, I setup the dependency similar to the following:

        public sealed class fruitButton: Control
        {
            public fruitButton()
            {
                this.DefaultStyleKey = typeof(fruitButton);
            }
    
            public enum visibleIcon
            {
                peach,
                grape,
                cherry,
                None
            };
    
            public static readonly DependencyProperty VisibleIconProperty =
                DependencyProperty.Register("VisibleIcon", typeof(visibleIcon), typeof(fruitButton), new PropertyMetadata(visibleIcon.peach, new PropertyChangedCallback(OnVisibleIconChange)));
    
            private static void OnVisibleIconChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var instance = d as fruitButton;
                if (instance != null)
                {
                    switch (instance.VisibleIcon)
                    {
                        case visibleIcon.peach:
                            instance.GetTemplateChild("PeachIcon").SetValue(VisibilityProperty, Visibility.Visible);
                            instance.GetTemplateChild("GrapeIcon").SetValue(VisibilityProperty, Visibility.Collapsed);
                            instance.GetTemplateChild("CherryIcon").SetValue(VisibilityProperty, Visibility.Collapsed);
                            break;
    // change remaining visibility based on icon set
    // ...
    
                    }
                }
            }
    
            public visibleIcon VisibleIcon
            {
                get
                {
                    return (visibleIcon)GetValue(VisibleIconProperty);
                }
                set
                {
                    SetValue(VisibleIconProperty, value);
                }
            }

    in the mainpage.xaml, I add the custom control as follows:

    <local:fruitButton VisibleIcon="grape" />

    When I initial add the dependency property "VisibleIcon" and set the value to grape.. the grape icon is made visible; however, after I build the project the control disappears and the error "Object reference not set to an instance of an object"

    If I leave the dependency property out of the custom control in mainpage.xaml, not problem. I can even set the property in mainpage.xaml.cs code-behind during execution as:

    fruitButton.VisibleIcon = fruitButton.visibleIcon.peach;

    with no issues.. i only get the problem during design time.  What am I missing from the dependency property definition in fruitButton.xaml.cs to make this work during design time for the designer to be able to see an use the properties after build?

    I'm missing something, but I just can't figure out what.

    Monday, September 16, 2013 1:04 AM

Answers

  • Hi tsw,

    I think the problem is, that you're calling GetTemplatedChild in the PropertyChangeCallback, and that one is called before the template is instantiated.

    You should only GetTemplatedChild in the overriden OnApplyTemplate-Method, and nowhere else.

    Here an example what your code should/could look like. Please try if this solves the designer-issue:

    public sealed class fruitButton : Control
        {
            public fruitButton()
            {
                this.DefaultStyleKey = typeof(fruitButton);
            }
    
            public enum visibleIcon
            {
                peach,
                grape,
                cherry,
                None
            };
    
            public static readonly DependencyProperty VisibleIconProperty =
                DependencyProperty.Register("VisibleIcon", typeof(visibleIcon), typeof(fruitButton), new PropertyMetadata(visibleIcon.peach, new PropertyChangedCallback(OnVisibleIconChange)));
    
            private UIElement _peachIcon;
            private UIElement _cherryIcon;
            private UIElement _grapeIcon;
            protected override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                _peachIcon = GetTemplateChild("PeachIcon") as UIElement;
                _grapeIcon = GetTemplateChild("GrapeIcon") as UIElement;
                _cherryIcon = GetTemplateChild("CherryIcon") as UIElement;
            }
            private static void OnVisibleIconChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var instance = d as fruitButton;
                if (instance != null)
                {
                    if (instance._peachIcon != null)
                    {
                        instance._peachIcon.Visibility =
                            instance.VisibleIcon == visibleIcon.peach ? Visibility.Visible : Visibility.Collapsed;
                    }
                    if (instance._cherryIcon != null)
                    {
                        instance._cherryIcon.Visibility =
                             instance.VisibleIcon == visibleIcon.cherry ? Visibility.Visible : Visibility.Collapsed;
                    }
                    if (instance._grapeIcon != null)
                    {
                        instance._grapeIcon.Visibility =
                              instance.VisibleIcon == visibleIcon.grape ? Visibility.Visible : Visibility.Collapsed;
                    }
                }
            }
    
            public visibleIcon VisibleIcon
            {
                get
                {
                    return (visibleIcon)GetValue(VisibleIconProperty);
                }
                set
                {
                    SetValue(VisibleIconProperty, value);
                }
            }
        }

    Thomas Claudius Huber

    "If you can´t make your app run faster, make it at least look & feel extremly fast"

    twitter: @thomasclaudiush
    homepage: www.thomasclaudiushuber.com
    author of: ultimate Windows Store Apps handbook | ultimate WPF handbook | ultimate Silverlight handbook


    Monday, September 16, 2013 3:00 PM

All replies

  • Hi tsw,

    I think the problem is, that you're calling GetTemplatedChild in the PropertyChangeCallback, and that one is called before the template is instantiated.

    You should only GetTemplatedChild in the overriden OnApplyTemplate-Method, and nowhere else.

    Here an example what your code should/could look like. Please try if this solves the designer-issue:

    public sealed class fruitButton : Control
        {
            public fruitButton()
            {
                this.DefaultStyleKey = typeof(fruitButton);
            }
    
            public enum visibleIcon
            {
                peach,
                grape,
                cherry,
                None
            };
    
            public static readonly DependencyProperty VisibleIconProperty =
                DependencyProperty.Register("VisibleIcon", typeof(visibleIcon), typeof(fruitButton), new PropertyMetadata(visibleIcon.peach, new PropertyChangedCallback(OnVisibleIconChange)));
    
            private UIElement _peachIcon;
            private UIElement _cherryIcon;
            private UIElement _grapeIcon;
            protected override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                _peachIcon = GetTemplateChild("PeachIcon") as UIElement;
                _grapeIcon = GetTemplateChild("GrapeIcon") as UIElement;
                _cherryIcon = GetTemplateChild("CherryIcon") as UIElement;
            }
            private static void OnVisibleIconChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var instance = d as fruitButton;
                if (instance != null)
                {
                    if (instance._peachIcon != null)
                    {
                        instance._peachIcon.Visibility =
                            instance.VisibleIcon == visibleIcon.peach ? Visibility.Visible : Visibility.Collapsed;
                    }
                    if (instance._cherryIcon != null)
                    {
                        instance._cherryIcon.Visibility =
                             instance.VisibleIcon == visibleIcon.cherry ? Visibility.Visible : Visibility.Collapsed;
                    }
                    if (instance._grapeIcon != null)
                    {
                        instance._grapeIcon.Visibility =
                              instance.VisibleIcon == visibleIcon.grape ? Visibility.Visible : Visibility.Collapsed;
                    }
                }
            }
    
            public visibleIcon VisibleIcon
            {
                get
                {
                    return (visibleIcon)GetValue(VisibleIconProperty);
                }
                set
                {
                    SetValue(VisibleIconProperty, value);
                }
            }
        }

    Thomas Claudius Huber

    "If you can´t make your app run faster, make it at least look & feel extremly fast"

    twitter: @thomasclaudiush
    homepage: www.thomasclaudiushuber.com
    author of: ultimate Windows Store Apps handbook | ultimate WPF handbook | ultimate Silverlight handbook


    Monday, September 16, 2013 3:00 PM
  • thanks Thomas, you got me 99% of the way there :)

    It got rid of the object reference error.. but still did not resolve the 'design time' issue of the icon disappearing after build.. however; with your suggestion, I was able to correct that by doing the following in OnApplyTemplate()

    switch(this.VisibleIcon)
    {
    case visibleIcon.grape:
     this._grapeIcon.Visibility = Visibility.Visible;
    //add other fruits in switch :) ...
    }

    I really liked the shorthand if/else replacement.. certianly looks so much cleaner in code too!

    again, thanks!


    • Edited by tsw Tuesday, September 17, 2013 2:37 AM
    Tuesday, September 17, 2013 2:36 AM