SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and now iOS Charting & Android Chart Components

0
0

I am creating custom controls and when I inherit from any annotation class (not AnnotationBase), public override void OnApplyTemplate() is called but any this.Template.FindName always returns null. If I inherit directly from ContentControl everything works correctly. I have tried inheriting from CustomAnnotation, BoxAnnotation, AnchorPointAnnotation and several others, all give the same result. I have checked the annotation classes and they all eventually inherit from ContentControl.

Version
4.2.0.9193
  • Andrew
    Hi JasonWhen calling override OnApplytemplate(), do you call the base.OnApplyTemplate as well before Template.FindName?
  • Jason Neeley
    Yes, that is the first call in the override method.
  • Yuriy
    Hi Jason, how do you apply your custom template? If using default styles, maybe you need to set DefaultStyleKey for your custom controls? Also, you can try calling GetTemplateChild(..) instead of FindName(..).
  • Jason Neeley
    I follow the recommended best practice of setting the default style key in a static constructor. I will post some code snippets later today.
  • You must to post comments
0
0

Amplifying code to hopefully help diagnose:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:CustomControls.Chart"
                xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
                xmlns:customControls="clr-namespace:CustomControls">

<!--add this resource dictionary to the generic.xaml merged resource dictionary-->
<!--<ResourceDictionary Source="pack://application:,,,/CustomControls;component/Location/ControlName.xaml"/>-->

<DataTemplate x:Key="ChartBarDataTemplate">
    <!--add items to display data as desired-->
</DataTemplate>

<ControlTemplate x:Key="ChartBarControlTemplate" TargetType="local:ChartBar">
    <ControlTemplate.Resources>
       <!-- ... removed for brevity -->
    </ControlTemplate.Resources>
    <ControlTemplate.Triggers>
       <!-- ... removed for brevity -->
    </ControlTemplate.Triggers>
    <Grid x:Name="PART_Container" Height="85">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="15"/>
        </Grid.RowDefinitions>
        <Path x:Name="path" Data="M0,0 L400,0 400,44.999997 C400,47.761426 397.76141,50.000001 395,50.000001 L255,50.000001 C252.23859,50.000001 250,52.238576 250,55.000001 250,57.761426 247.76143,60.000001 245,60.000001 L155,60.000001 C152.23857,60.000001 150,57.761426 150,55.000001 150,52.238576 147.76143,50.000001 145,50.000001 L5.000001,50.000001 C2.2385762,50.000001 0,47.761426 0,44.999997 z"
          Stretch="Fill" Stroke="DodgerBlue" Grid.RowSpan="2" StrokeThickness="{Binding BorderThickness.Bottom, ElementName=ButtonAutoRange}" >
            <Path.Fill>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="DodgerBlue" Offset="0"/>
                    <GradientStop Color="#3F1E90FF" Offset="0.8"/>
                </LinearGradientBrush>
            </Path.Fill>
        </Path>
    <!-- a bunch of other stuff -->
    </Grid>
</ControlTemplate>

/// <summary>
/// Class ChartBar. This class cannot be inherited.
/// </summary>
/// <seealso cref="System.Windows.Controls.ContentControl" />
/// <seealso cref="System.IDisposable" />
[TemplatePart(Name = "PART_Container", Type = typeof(Grid))]
[TemplatePart(Name = "PART_Canvas", Type = typeof(Canvas))]
[TemplatePart(Name = "ButtonAutoRange", Type = typeof(Button))]
[TemplatePart(Name = "ComboBoxTheme", Type = typeof(ComboBox))]
[TemplatePart(Name = "TextBoxInstrument", Type = typeof(TextBox))]
[TemplatePart(Name = "TextBoxTimeframe", Type = typeof(TextBox))]
public sealed partial class ChartBar : ContentControl, IDisposable
{

    /// <summary>
    /// When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate" />.
    /// </summary>
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        this.container = (Grid)this.Template.FindName("PART_Container", this);
        this.canvas = (Canvas)this.Template.FindName("PART_Canvas", this);
        this.buttonAutoRange = (Button)this.Template.FindName("ButtonAutoRange", this);
        // omitted for brevity
    }

}

So as I mentioned, when I derive directly from ContentControl, the Template.FindName works flawlessly. However, as soon as that is changed to any of the annotation classes (not the AnnotationBase) Template.FindName always returns null. Ideally, I’d like to be able to inherit directly from something like CustomAnnotation so I don’t have to wrap anything and can directly add this custom control to the annotation collection.

  • Jason
  • You must to post comments
0
0

Forgot to add constructors:

    /// <summary>
    /// Initializes static members of the <see cref="ChartBar" /> class.
    /// </summary>
    static ChartBar()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ChartBar), new FrameworkPropertyMetadata(typeof(ChartBar)));
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ChartBar" /> class.
    /// </summary>
    public ChartBar()
    : base()
    {
        Instance = this;
        this.Dispatcher.UnhandledException += DispatcherOnUnhandledException;
        this.atomicDisposed = 0;
        this.container = null;

        this.internalDataContext = new ChartBarDataContext(this);
    }
  • You must to post comments
0
0

Hi Jason,

Thanks for sharing your code. As I can see, you do create a ControlTemplate for the ChartBar type, but it isn’t applied anywhere. So your
ChartBar just picks up a template from the default annotation style.

You have to set the Template property of ChartBar explicitly. Also, I would suggest providing a new default style for ChartBar. This will require setting the DefaultStyleKey property in ChartBar constructor.

What else you need to know, is that all annotation types have the AnnotationRoot member, which need to refer to the most top panel in your template.

Finally, most of the annotation types require a particular template parts, although they aren’t marked with the TemplatePart attribute (by mistake). So the safest way for you to do this is to inherit from CustomAnnotation – it doesn’t look for any template parts.

Hope this helps,

Best regards,
Yuriy

  • Jason Neeley
    Thanks Yuriy. I missed the style in my copy paste but am setting the template explicitly in that style. I set the default style key on the static constructor, do you advise differently? All of my testing showed the static constructor was always called first.I will definitely try getting and storing the AnnotationRoot.
  • Jason Neeley
    All attempts have been unsuccessful. I have created a support ticket and attached a small self contained solution which demonstrates the anomaly.
  • You must to post comments
Showing 3 results
Your Answer

Please first to submit.