0

I am making own graph control, it has list of figures

public class Figure
{
    public virtual void Render(Graph graph, GDI.Graphics graphics) { }
}

// was nested before, that's why here is @jnovo answer
public class Line : Figure { ... }
public class Plot : Figure { ... }
... // more figures

[ContentProperty("Figures")] // this doesn't help
public class Graph : FrameworkElement
{
    public IList<Figure> Figures { get; set; }
}

And I have problems to define it in xaml:

<local:Graph>
    <local:Graph.Figures>
        <local:Line/> <!-- Property 'Figures' does not support values of type 'Line' -->
    </local:Graph.Figures>
</local:Graph>

How to solve it?

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • Change your `IList
    ` to `List
    ` and it will work now.
    – Mat J Sep 24 '14 at 12:55
  • Although you actually had the problem with `IList` before updating your question, it was not the main issue - indeed it hadn't occurred yet. Imho, your edit was too radical and doesn't reflect the original problem. Thus in the future, please either put more effort in the edit - at least explaining the original problem - or make a new question. Anyways, I'm glad you got it sorted out :) – jnovo Sep 24 '14 at 13:53

2 Answers2

2

From XAML and Custom Classes for WPF in MSDN:

Your custom class must not be a nested class. Nested classes and the "dot" in their general CLR usage syntax interfere with other WPF and/or XAML features such as attached properties.

So, define your classes in a namespace outside the Graph class.

jnovo
  • 5,659
  • 2
  • 38
  • 56
  • There are dozens of advantage of nested classes: no namespace conflicts, access to non `public` methods. I don't know if I am ready to sacrifice all that. I see my question title is not correct and this problem is common ([1](http://stackoverflow.com/q/14546347/1997232), [2](http://stackoverflow.com/q/4269896/1997232), etc.). Do you know if there is a way to overcome this issue? To still have nested classes. – Sinatr Sep 24 '14 at 10:35
  • For namespace conflicts you can actually nest namespaces or use namespace aliases. Arguably, if you require access to non public methods, your design is flawed, unless you use `protected` and access them from sub-classes. You may also use `internal` for assembly level access, but I wouldn't encourage so. – jnovo Sep 24 '14 at 10:42
  • @Sinatr by the way, I don't really agree with your statement regarding nested classes usefulness. Check this [SO question and answers](http://stackoverflow.com/a/48879/3042204) for some examples of their use. Bottom line: if you need to use them outside the enclosing class, the shouldn't be there. – jnovo Sep 24 '14 at 10:51
  • I don't think there are design problems, because each `Figure` needs something from `Graph` and this something I don't want to be `public` for `Graph` users. To example, calculation of bounds, to redraw figures or to automatically zoom graph to content. The `Graph` users shouldn't see any `Graph.ZoomChange` or `Graph.GDIPen` (which to example takes `RenderSize` of graph to create wpf gradient brush from gdi one). Ability to use `private` members in `Figure` based classes is really a nut. But I might have to sacrifice all that if I want to use it in xaml. – Sinatr Sep 24 '14 at 11:45
  • See last edit please. It's not nested anymore, but the problem still (and it looks like my question tittle was correct, that's why I am not making new question .. yet). – Sinatr Sep 24 '14 at 12:32
2

Changing the abstract IList to a type like List or ObservableCollection should be part of the solution, as I don't think the xaml engine will just decide which type should implement the IList. Instantiation of the list should happen beforehand as well, I suppose adding an element in xaml might just call the Add method on the list.

Try if this works for you, compiles and runs for me.

// without DependencyProperty
public class Graph : FrameworkElement
{
    // Figures
    public List<Figure> Figures { get; set; }

    public Graph()
    {
        Figures = new List<Figure>();
    }
}

// with DependencyProperty
public class Graph : FrameworkElement
{
    // Figures
    private static readonly DependencyPropertyKey FiguresPropertyKey = DependencyProperty.RegisterReadOnly("Figures", typeof(ObservableCollection<Figure>), typeof(Graph), new FrameworkPropertyMetadata(new ObservableCollection<Figure>()));
    public static readonly DependencyProperty FiguresProperty = FiguresPropertyKey.DependencyProperty;
    public ObservableCollection<Figure> Figures { get { return (ObservableCollection<Figure>)GetValue(FiguresProperty); } }

    public Graph()
    {
        // explanation for this, see http://msdn.microsoft.com/en-us/library/aa970563%28v=vs.110%29.aspx
        SetValue(FiguresPropertyKey, new ObservableCollection<Figure>());
    }
}
Mike Fuchs
  • 12,081
  • 6
  • 58
  • 71