AEvers
AEvers

Reputation: 85

Bound User-Controls in an ObservableCollection aren't showing up in uniformgrid

So, I'm fairly new to C#/XAML and have been trying to teach myself MVVM by reworking an old project. I ran into a problem where a user-control is supposed to be added to a uniformgrid. The user-control shows up fine if I implement it by itself, but if I add it to a ObservableCollection and then try to bind that to a uniformgrid, the path to the user-control gets displayed rather than the actual UI element. Unfortunately I'm new enough to C# and MVVM that I can't identify what specifically is the problem, which makes it hard to search online for.

<UserControl x:Class="CMS.Views.MonthView"
             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" 
             xmlns:local="clr-namespace:CMS.Views"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
    <ItemsControl ItemsSource="{Binding Dates}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid IsItemsHost="True" Columns="7"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
    </Grid>
</UserControl>

MonthView.cs

namespace CMS.Views
{
    public partial class MonthView : UserControl
    {
        public MonthView()
        {
            InitializeComponent();
            MonthViewModel monthViewModelObject = MonthViewModel.GetMonthViewModel();
            this.DataContext = monthViewModelObject;
        }
    }
}

MonthViewModel

namespace CMS.ViewModels
{
    class MonthViewModel
    {
        private readonly ObservableCollection<DayViewModel> _dates = new ObservableCollection<DayViewModel>();

        public IReadOnlyCollection<DayViewModel> Dates
        {
            get { return _dates; }
        }

        public static MonthViewModel GetMonthViewModel()
        {
            var month = new MonthViewModel();
            month.testdaymodel();
            return month;
        }

        public void testdaymodel()
        {
            DayViewModel DVM = DayViewModel.GetDayViewModel();
            DVM.LoadDate(DateTime.Now);
            _dates.Add(DVM);
        }
    }
}

DayView's XAML which has the DataTemplate

<UserControl x:Class="CMS.Views.DayView"
    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" 
    xmlns:local="clr-namespace:CMS.Views" 
    mc:Ignorable = "d"
    MinWidth="100" MinHeight="100" BorderBrush="LightSlateGray" BorderThickness="0.5,0.5,1.5,1.5">

    <UserControl.Resources>
        <ResourceDictionary>
        </ResourceDictionary>
    </UserControl.Resources>

    <DataTemplate x:Name ="DayBox">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="21"/>
                <RowDefinition  Height="*"/>
            </Grid.RowDefinitions>
            <Border x:Name="DayLabelRowBorder" CornerRadius="2" Grid.Row="0" BorderBrush="{x:Null}" Background="{DynamicResource BlueGradientBrush}">
                <Label x:Name="DayLabel" Content="{Binding Path = Info.Day, Mode = OneWay}" FontWeight="Bold" FontFamily="Arial"/>
            </Border>

            <!--This will be bound to the event schedule for a given day-->
            <StackPanel Grid.Row="1" x:Name="DayAppointmentsStack" HorizontalAlignment="Stretch" Background="White" VerticalAlignment="Stretch">
            </StackPanel>
        </Grid>
    </DataTemplate>
</UserControl>

Upvotes: 1

Views: 246

Answers (1)

janonimus
janonimus

Reputation: 2897

EDIT: The same rule applies whether you're using a simple control like a Label or your own control like DayView.

You need to set the ItemsControl.ItemTemplate that will be bound to each item from your IReadOnlyCollection<DayViewModel>.

Then, make a DataTemplate of your liking. Like this:

<ItemsControl ItemsSource="{Binding Dates}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid IsItemsHost="True" Columns="7"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <!-- This control is automatically bound to each DayViewModel instance -->
            <local:DayView />
            <!-- 
            <Label Content="{Binding PropertyToDisplay}" ></Label>
            -->
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

You didn't show the DayViewModel class, so you can just change the PropertyToDisplay to the actual property you want your view to display.

EDIT: Making DayView the ItemsControl.Template will automatically bind it to the type of the items in the ItemSource.

That means you can treat DayView like a UserControl with DayViewModel as its DataContext without explicitly setting it.

I am assuming that the actual View of your DayView is the Grid inside the DataTemplate, so I just modified the code as follows:

DayView.xaml

<UserControl x:Class="CMS.Views.DayView"
    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" 
    xmlns:local="clr-namespace:CMS.Views" 
    mc:Ignorable = "d"
    MinWidth="100" MinHeight="100" BorderBrush="LightSlateGray" BorderThickness="0.5,0.5,1.5,1.5">

    <UserControl.Resources>
        <ResourceDictionary>
        </ResourceDictionary>
    </UserControl.Resources>

    <!-- <DataTemplate x:Name ="DayBox"> -->

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="21"/>
                <RowDefinition  Height="*"/>
            </Grid.RowDefinitions>
            <Border x:Name="DayLabelRowBorder" CornerRadius="2" Grid.Row="0" BorderBrush="{x:Null}" Background="{DynamicResource BlueGradientBrush}">
                <Label x:Name="DayLabel" Content="{Binding Path = Info.Day, Mode = OneWay}" FontWeight="Bold" FontFamily="Arial"/>
            </Border>

            <!--This will be bound to the event schedule for a given day-->
            <StackPanel Grid.Row="1" x:Name="DayAppointmentsStack" HorizontalAlignment="Stretch" Background="White" VerticalAlignment="Stretch">
            </StackPanel>
        </Grid>

    <!-- </DataTemplate> -->

</UserControl>

Upvotes: 5

Related Questions