I'm creating a custom control that has a collection of objects that are to be displayed. I would like this to be a wrapper with some stuff on it and an inner section that hosts other stuff that is determined by the consumer of the control. Basically I want it to be used similar in use to StackPanel where the user creates it and adds controls inside the content section. I have this basically working with one thing that I can't figure out. Here is the 'Pages' dependency property that needs to be set:
public static readonly DependencyProperty PagesProperty =
DependencyProperty.Register("Pages", typeof(IEnumerable<MyContentPage>), typeof(UC_ApplicationWindow),
new PropertyMetadata(new List<MyContentPage>()));
public IList<MyContentPage> Pages
{
get => (IList<MyContentPage>)GetValue(PagesProperty);
set => SetValue(PagesProperty, value);
}
This is the code from a consumer window of this control that is currently working:
<graphicElements:UC_ApplicationWindow.Pages>
<x:Array Type="graphicElements:MyContentPage">
<graphicElements:MyContentPage>
<graphicElements:MyContentPage.Content>
...the contents of the page
</graphicElements:MyContentPage.Content>
</graphicElements:MyContentPage>
<graphicElements:MyContentPage>
<graphicElements:MyContentPage.Content>
...the contents of the page
</graphicElements:MyContentPage.Content>
</graphicElements:MyContentPage>
</x:Array>
</graphicElements:UC_ApplicationWindow.Pages>
Ideally I would like to be able to just set multiple elements for the Pages property without having to give it an array, however if I try to do that directly I get the error 'The specified value could not be assigned. The following type was expected: "IList'1".' I also would like to be able to specify the page content directly like most other WPF controls do, but when I take out the direct content property it says 'The type 'MyContentPage' does not support direct content'. Here is what I would ultimately like it to be able to look like:
<graphicElements:UC_ApplicationWindow.Pages>
<graphicElements:MyContentPage>
...the contents of the page
</graphicElements:MyContentPage>
<graphicElements:MyContentPage>
...the contents of the page
</graphicElements:MyContentPage>
</graphicElements:UC_ApplicationWindow.Pages>
As I said, it currently works and if it's what i have to do I can but it seems a little clumsy and I have a hunch it could work better but can't figure out how. Anyone have the magic key(s) I'm missing?
Ok, thanks to this post, I figured this out. All you need to do is add the 'ContentProperty' attribute to the top and XAML will parse that property as the content when specified directly. So in this case I actually did this twice. I put [ContentProperty(nameof(Pages))] on the main application window class and [ContentProperty(nameof(Content))] on the MyContentPage class.
I also figured out thanks a comment on this post I figured out that if you set the Pages property as IList instead of IEnumerable you don't have to specify array directly, it just creates it. Now instead of this:
<UC_ApplicationWindow>
<graphicElements:UC_ApplicationWindow.Pages>
<x:Array Type="graphicElements:MyContentPage">
<graphicElements:MyContentPage>
<graphicElements:MyContentPage.Content>
...the contents of the page
</graphicElements:MyContentPage.Content>
</graphicElements:MyContentPage>
<graphicElements:MyContentPage>
<graphicElements:MyContentPage.Content>
...the contents of the page
</graphicElements:MyContentPage.Content>
</graphicElements:MyContentPage>
</x:Array>
</graphicElements:UC_ApplicationWindow.Pages>
</UC_ApplicationWindow>
I can just do this:
<UC_ApplicationWindow>
<graphicElements:MyContentPage>
...the contents of the page
</graphicElements:MyContentPage>
<graphicElements:MyContentPage>
...the contents of the page
</graphicElements:MyContentPage>
</UC_ApplicationWindow>
Ahhhh... much cleaner :)