0

I'm trying to build a WPF user control called "Screen" which has two attached properties that behave as content slots for displaying a collection of controls and a collection of buttons. I'm basing the design for this on a solution found at WPF: template or UserControl with 2 (or more!) ContentPresenters to present content in 'slots'.

When I run the project, where I expect to see my "Screen" control I am instead seeing the string "(Collection)" printed on the screen. There are no binding errors in the output window. Here's my code:

Code behind:

public partial class Screen : UserControl
{
    public Screen()
    {
        this.InitializeComponent();

        this.ScreenGrid.DataContext = this;
        this.DataContextChanged += this.ScreenDataContextChanged;
        this.Content = new ObservableCollection<FrameworkElement>();
        this.Buttons = new ObservableCollection<Button>();
    }

    private void ScreenDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (this.GetValue(Screen.ScreenContentProperty) != null)
        {
            foreach (var control in ((ObservableCollection<FrameworkElement>)this.GetValue(Screen.ScreenContentProperty)))
            {
                control.DataContext = e.NewValue;
            }
        }

        if (this.GetValue(Screen.ButtonsProperty) != null)
        {
            foreach (var control in ((ObservableCollection<Button>)this.GetValue(Screen.ButtonsProperty)))
            {
                control.DataContext = e.NewValue;
            }
        }
    }

    public ObservableCollection<FrameworkElement> ScreenContent
    {
        get { return (ObservableCollection<FrameworkElement>)this.GetValue(Screen.ButtonsProperty); }
        set { this.SetValue(Screen.ScreenContentProperty, value); }
    }

    public ObservableCollection<Button> Buttons
    {
        get { return (ObservableCollection<Button>)this.GetValue(Screen.ButtonsProperty); }
        set { this.SetValue(Screen.ButtonsProperty, value); }
    }

    public static readonly DependencyProperty ScreenContentProperty = DependencyProperty.Register(
        "ScreenContent", typeof(ObservableCollection<FrameworkElement>), typeof(Screen),
        new PropertyMetadata(new ObservableCollection<FrameworkElement>()));

    public static readonly DependencyProperty ButtonsProperty = DependencyProperty.Register(
        "Buttons", typeof(ObservableCollection<Button>), typeof(Screen),
        new PropertyMetadata(new ObservableCollection<Button>()));
}

XAML:

<UserControl x:Class="Hca.Ims.Wpf.Views.Screen"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d">
    <StackPanel x:Name="ScreenGrid">
        <ItemsControl ItemsSource="{Binding ScreenContent}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ContentPresenter Content="{TemplateBinding Content}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <ScrollViewer>
                        <StackPanel />
                    </ScrollViewer>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <ItemsControl ItemsSource="{Binding Buttons}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ContentPresenter Content="{TemplateBinding Content}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </StackPanel>
</UserControl>

Usage:

<v:Screen>
    <v:Screen.ScreenContent>
        <TextBlock>Foo</TextBlock>
        <TextBlock>Bar</TextBlock>
    </v:Screen.ScreenContent>
    <v:Screen.Buttons>
        <Button>Submit</Button>
        <Button>Approve</Button>
    </v:Screen.Buttons>
</v:Screen>

Any ideas what I've done wrong?

Community
  • 1
  • 1
wwarby
  • 1,873
  • 1
  • 21
  • 37

1 Answers1

2

You seem to have way more code than you need to achieve this.

Boiling this down to it's simplest, you should be able to use a basic ItemsControl like so:

XAML

<UserControl x:Class="Hca.Ims.Wpf.Views.Screen"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d">
    <StackPanel x:Name="ScreenGrid">
        <ItemsControl ItemsSource="{Binding Items1}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Vertical"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>        
    </StackPanel>
</UserControl>

Codebehind:

public partial class Screen 
{
    public Screen()
    {
        InitializeComponent();

        ScreenGrid.DataContext = this;
        Items1 = new ObservableCollection<FrameworkElement>();
    }

    public static readonly DependencyProperty Items1Property = 
        DependencyProperty.Register("Items1", typeof(ObservableCollection<FrameworkElement>), typeof(Screen), new PropertyMetadata(null));

    public ObservableCollection<FrameworkElement> Items1
    {
        get { return (ObservableCollection<FrameworkElement>)GetValue(Items1Property); }
        set { SetValue(Items1Property, value); }
    }

}

Usage:

    <v:Screen>
        <v:Screen.Items1>
            <TextBlock>Foo</TextBlock>
            <TextBlock>Bar</TextBlock>
            <Button><TextBlock Text="Button Text"/></Button>
        </v:Screen.Items1>
    </v:Screen>
James Harcourt
  • 6,017
  • 4
  • 22
  • 42
  • 1
    I do sincerely apologise for not acknowledging this solution - I thought I had done already. – wwarby Apr 02 '15 at 12:18