3

First off, I am wondering if this is possible. I read slight grumblings around the internet about this, but I was not entirely sure.

My scenario: I have a base chart class which has some methods which all charts should have.

public partial class BaseChart : System.Web.UI.UserControl
{
    public BaseChart()
    {
    }

    public void ToggleLegend()
    {
        Chart1.Legends[0].Enabled = !Chart1.Legends[0].Enabled;
    }
}

There is also some mark-up for this BaseChart -- setting background colors, etc. All charts which inherit BaseChart should use this starting mark-up and be able to build upon it.

I would then like to do this:

public partial class HistoricalLineChart : BaseChart
{
    public HistoricalLineChart()
        : base()
    {
    }

    public HistoricalLineChart(int reportID)
        : base()
    {
        Chart1.Titles[0].Text = "Hello World";
    }
 }

where HistoricalLineChart is a web user control with no mark-up e.g. "HistoricalLineChart.ascx"

The problem is that Chart1 is undefined when in HistoricalLineChart's scope. Is there something that I am missing here?

Thanks.

Joel Beckham
  • 18,254
  • 3
  • 35
  • 58
Sean Anderson
  • 35
  • 1
  • 4

3 Answers3

3

Though usually one ends up just making a custom control in this situation (as the other answer suggest), and this is good for many reasons, there are a couple other approaches that may be useful in situations where complicated markup makes a server control infeasible.

1) Create a base class that has all the functionality common to your implementations and inherits UserControl. If you really want to include markup as part of the base class, you could put it in a separate usercontrol with no code, and load it from the abstract class, though this seems a little ugly. If the markup that is shared is simple, though, just render it from code instead.

public abstract class MyUserControl: UserControl
{
     public Chart Chart1;
     public void ToggleLegend()
    {
        Chart1.Legends[0].Enabled = !Chart1.Legends[0].Enabled;
    }
    public override void CreateChildControls()
    {
        Controls.Add(Page.LoadControl("path/to/mymarkup/control"));
        // or add them in code
        BuildBaseControls();
    }
}

2) Create UserControl implementations that inherit MyUserControl instead of UserControl, and add the markup

public partial class HistoricalLineChart : MyUserControl
{
    public HistoricalLineChart(int reportID)
        : base()
    {
        Chart1.Titles[0].Text = "Hello World";
    }
}

You could also create an interface that describes any controls that should appear in the markup and implement that. This is nice because it gives you a construct that is applicable to either a UserControl (where the controls are defined in markup) or a WebControl (where the controls are created in code), leaving the actual details of the markup to each implementation, but letting you share the functionality.

Jamie Treworgy
  • 23,934
  • 8
  • 76
  • 119
1

Unfortunately the markup portion of BaseChart is not actually part of the BaseChart class. The markup is part of a class that gets created when you compile and it inherits from BaseChart. So HistoricalLineChart only contains what you've explicitly set in BaseChart and none of the markup. The only way I know to work around this is to use a Composite Control or Custom Server Control (vs a UserControl).It's a bit more of a pain since you have to add your child controls programmatically, but should do what you want.

Here is an example: http://msdn.microsoft.com/en-us/library/3257x3ea(v=VS.100).aspx

Basically:

  1. Inherit from CompositeControl.
  2. Override CreateChildControls. In this method, you can add all of your child controls (like your chart).
  3. Optional: Override Render. Override this if you need custom markup in addition to the child controls. You can output your custom markup plus call RenderControl on all of your child controls to tell them where to render their output. If you don't override this method at all, then the composite control will render out the child controls in the order that they are in the controls collection.

Here are a couple more tutorials:

Joel Beckham
  • 18,254
  • 3
  • 35
  • 58
  • I think creating a Custom Server Control is the way to go for you. – TheGeekYouNeed Feb 28 '11 at 22:05
  • Hey, so just glancing at this (I have never worked with a custom server control). I noticed that, in the walkthrough, they're creating a file called "WelcomeLabel.cs". When I create my UserControl I am using Page.LoadControl("foo.ascx"). I know that LoadControl cannot be called on a class -- that would just not make sense. Will I be able to call LoadControl on a custom server control? It seems like all the 'custom' is appended in a class which is of no use to LoadControl. – Sean Anderson Feb 28 '11 at 22:13
  • 1
    You would have no need to use `Page.LoadControl` for a custom server control. That is only needed for partial classes (it loads the ascx part). Just say `Controls.Add(new MyControl())` -- or instantiate it like any class and add it to the controls collection wherever you want. – Jamie Treworgy Feb 28 '11 at 22:40
  • @jamietre -- Good point. I have no idea what I was thinking. I deleted my comment. – Joel Beckham Feb 28 '11 at 22:41
  • Thanks for all the help so far. I am looking into composite controls and they seem to be what I will need to use. I have another concern, however, but perhaps you will not be able to answer it. When I first started creating a Microsoft Chart Control I set its properties at run-time. One of these properties was the font size of a label. I found that if I set this font at run-time then some auto-placement features provided by Microsoft seemed to stop working. Yet, if I set the same font at design-time then these features worked. Any idea how creating a composite control will affect this? – Sean Anderson Feb 28 '11 at 23:01
  • @Sean Anderson - I'm not sure what's causing the behavior you are seeing, so I don't know if the composite control will have an affect on this. After you set the font size, can you set any of the auto-placement properties to make sure that they are set correctly? – Joel Beckham Feb 28 '11 at 23:41
  • @Sean Anderson - Also, you may want to look into creating your chart control in the Composite Control's `OnInit`, set the properties there, then add the Chart control to the Composite's control collection in `CreateChildControls` like you were before. So basically, you're creating the Chart earlier in the page lifecycle than you were before. By doing so it may cause it to act more like it did when you were setting the properties at design time. Not sure though. – Joel Beckham Feb 28 '11 at 23:45
  • @Joel No big deal honestly. If it arises I'll look into it, but its non-related to this issue. Thanks for your help. – Sean Anderson Feb 28 '11 at 23:47
0

You can make a protected property in BaseChart that exposes the chart.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • That would be true if Chart1 were a property of BaseChart, but I think in this case Chart1 is created declaratively in the BaseChart.ascx which isn't a part of the actual BaseChart class. – Joel Beckham Feb 28 '11 at 22:02
  • But then what is it? And how is it possible that code in BaseChart can access Chart1, but can't return it as the return value for a protected property? – James Gaunt Feb 28 '11 at 23:04
  • @Joel: If you can use it in the `BaseChart` codebehind, you can return it from a property. – SLaks Feb 28 '11 at 23:08
  • @SLaks - What you say is true, but since Chart1 is declared in the ascx file, the chart instance is created there and not in the `BaseChart` class, so a class inheriting from `BaseChart` would need to instantiate it's own Chart object which I don't think is what Sean wanted. – Joel Beckham Feb 28 '11 at 23:52
  • @James - I realized my wording was incorrect in my first comment. Chart1 *is* a property of `BaseChart`, but the actual instance of the chart object is created declaratively in the ascx file. So in Sean's `HistoricalLineChart`, it does have the Chart1 property but it never gets set. – Joel Beckham Feb 28 '11 at 23:57