I've found a really strange quirk in WPF. If I specify a DataTemplate for an interface, it will work if defined inside an ItemsControl.ItemTemplate, but will not work if defined inside ItemsControl.Resrouces.
Concrete example:
I have a tree structure I want to represent. All items in the tree implement IHardware, but they do not necessarily have a common base type. If I define a HierarchicalDataTemplate for IHardware inside TreeView.ItemTemplate, everything works swimmingly. If I define the template inside TreeView.Resources, it never gets used/applied. The following shows the same data in 2 columns, the first column works as expected, the second column does not.
<Window x:Class="WPFInterfaceBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self ="clr-namespace:WPFInterfaceBinding"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- Works -->
<Border
Grid.Column="0"
Background="Gray">
<TreeView
ItemsSource="{Binding Hardware}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
DataType="{x:Type self:IHardware}"
ItemsSource="{Binding SubHardware}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
<!-- Doesn't work -->
<Border
Grid.Column="1"
Background="Gray">
<TreeView
ItemsSource="{Binding Hardware}">
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type self:IHardware}"
ItemsSource="{Binding SubHardware}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Border>
</Grid>
</Window>
Note that in the second column, nothing has changed except TreeView.ItemTemplate -> TreeView.Resources
Why is this the case? How can I get the template to work when inside Resources? I imagine I can work around this using a DataTemplateSelector, but first I'm curious if there's a way to actually get it working as expected.
Code behind, for completeness
using System.Windows;
namespace WPFInterfaceBinding
{
public partial class MainWindow : Window
{
public IHardware[] Hardware { get; private set; }
public MainWindow ()
{
Hardware = InitializeHardware();
InitializeComponent();
}
private IHardware[] InitializeHardware ()
{
return new Hardware[] {
new Hardware("Component 1", new Hardware[] {
new Hardware("Sub Component 1"),
new Hardware("Sub Component 2")
}),
new Hardware("Component 2", new Hardware[] {
new Hardware("Sub Component 3"),
new Hardware("Sub Component 4")
})
};
}
}
public class Hardware : IHardware
{
public string Name { get; set; }
public IHardware[] SubHardware { get; set; }
public Hardware ( string name, Hardware[] subHardware = null )
{
Name = name;
SubHardware = subHardware ?? new Hardware[0];
}
}
public interface IHardware
{
string Name { get; set; }
IHardware[] SubHardware { get; set; }
}
}
Additional information:
I can't simply use ItemTemplate because in my actual usage scenario there will be non-IHardware items mixed in using a CompositeCollection so I need multiple templates.
I can't change the types of the collections from IHardware to something concrete because I'm displaying data from code I don't control.
This is just example code, not representative of any design patterns actually in use.
Defining the template inside TreeView.Resources works just fine if the type is changed from IHardware to Hardware.
Turns out, WPF just doesn't like binding to interfaces. The only work around I could figure out was to use a DataTemplateSelector.
public class OHMTreeTemplateSelector : DataTemplateSelector
{
public HierarchicalDataTemplate HardwareTemplate { get; set; }
public DataTemplate SensorTemplate { get; set; }
public override DataTemplate SelectTemplate ( object item, DependencyObject container )
{
if ( item is IHardware ) return HardwareTemplate;
else if ( item is ISensor ) return SensorTemplate;
return base.SelectTemplate(item, container);
}
}
Though, for other reasons I ended up creating a separate ViewModel for the data that exposes it through concrete types, circumventing this issue.
Related
Struggling with showing nested relationship in my TreeView. Here is the scenario:
In the database I have Category and Account tables. Each category can have zero or more sub-categories so this table has a nested relationship with itself. Each category/sub-category can have zero or more Account in it, there is a one-to-many relation between Category and Account. Simple, isn't it!
On top of my DB, I have EDMX, with Categories and Accounts entities in it and their associations as I mentioned above. For ease of understanding, I have renamed navigation properties so that Categories now has ParentCategory, ChildCategories and Accounts properties in it.
On top of EDMX, I have my ViewModel, which defines a public property named AllCategories. My TreeView will bind to this property. I initialize this property at the startup like this:
using (MyEntities context = new MyEntities())
Categories = context.Categories.Include(x => x.Accounts).ToList();
Finally I use the following HierarchicalDataTemplate to show this stuff:
<HierarchicalDataTemplate DataType = "{x:Type local:Category}" ItemsSource = "{Binding Path=ChildCategories}">
<TreeViewItem Header="{Binding Name}" ItemsSource="{Binding Accounts}" />
</HierarchicalDataTemplate>
<DataTemplate DataType = "{x:Type local:Account}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
This runs fine and shows categories, sub-categories and accounts in the tree, but the problem is that sub-categories show up not only under their parent category, but also at the root-level. This happens for categories of all depths. What am I doing wrong here?
Note: If I add .Where(x=>!x.ParentID.HasValue) in the VM, it shows only the root category and its immediate children, nothing else.
Edit
Here's what it currently looks like. Everything goes fine up to the dotted white line (I added that line manually for illustration; has nothing to do with WPF). After that, the sub-categories start repeating with their child sub-categories. This process continues over and over till the leaf sub-categories. I believe I understand what's going on here, but don't have a solution for it. For reference, this guy presents a solution of the problem, but he is using DataSets, whereas I'm working with EF and can't translate his solution into my scenario.
The idea is to connect your business data by ObservableCollections and leave your Hierarchical templates simple, so that the treeview won't show duplicate entries.
The sample code shows nested viewmodel relationship and the corresponding hierarchical templates. For simplification, the Root is an ObservableCollection (otherwise you would need to add INotifyPropertyChanged here and selective ItemsSource Binding in the TreeView)
<Window x:Class="MyWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyWpf"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<HierarchicalDataTemplate DataType = "{x:Type local:RootItem}" ItemsSource = "{Binding Path=Categories}">
<TextBlock Text="{Binding Header}"></TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType = "{x:Type local:CatNode}" ItemsSource = "{Binding Path=Items}">
<TextBlock Text="{Binding Header}"></TextBlock>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding MyRoot}"/>
</Grid>
</Window>
namespace MyWpf
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
MyRoot = new ObservableCollection<RootItem>();
MyRoot.Add(new RootItem());
}
public ObservableCollection<RootItem> MyRoot { get; set; }
}
public class RootItem
{
public RootItem()
{
Categories = new ObservableCollection<CatNode>();
Categories.Add(new CatNode { Header = "Cat1" });
Categories[0].Items.Add("Item11");
Categories[0].Items.Add("Item12");
Categories.Add(new CatNode { Header = "Cat2" });
Categories[1].Items.Add("Item21");
Categories[1].Items.Add("Item22");
}
public string Header { get { return "Root"; }}
public ObservableCollection<CatNode> Categories { get; set; }
}
public class CatNode
{
public CatNode()
{
Items = new ObservableCollection<string>();
}
public string Header { get; set; }
public ObservableCollection<string> Items { get; set; }
}
}
Is it the intended behavior that a binding to a collection automatically uses the first item as source?
Example Xaml:
<Window x:Class="ListSelection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBlock Text="{Binding ColContent}" />
<TextBlock Text="{Binding ItemContent}" />
</StackPanel>
</Window>
and Code:
using System.Collections.Generic;
using System.Windows;
namespace ListSelection
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyCol("col 1")
{
new MyItem("item 1"),
new MyItem("item 2")
};
}
}
public class MyItem
{
public string ItemContent { get; set; }
public MyItem(string content)
{
ItemContent = content;
}
}
public class MyCol : List<MyItem>
{
public string ColContent { get; set; }
public MyCol(string content)
{
ColContent = content;
}
}
}
The UI shows up with:
col 1
item 1
The second binding took implicitly the first collection item as source! So bug, feature or intended?
EDIT: .net 4.5, VS2012, corrections
EDIT 2:
I further investigated the problem together with a mate and got closer to the solution:
<Window x:Class="ListSelection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<ListView ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemContent}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Text="{Binding ItemContent}" />
</StackPanel>
</Window>
The - lets call it - magic binding seems to exist for master detail views. By default any collection that is bound gets a CollectionView - which provides a selected item property (and other cool stuff like sorting). This selected item can be used shortcutted for the detailed view. If the IsSynchronizedWithCurrentItem is set to true the shortcutted binding reacts to changed selections. The problem in the whole thing: the selected item of the CollectionView is alway set to the first item which leads to the magic binding... I would call that a bug and it should only work explicitly, e.g. by binding the collection to a Selector with the IsSynchronizedWithCurrentItem set.
I wonder how I can show design time data in Expression Blend that is located inside a SampleData.xaml using a CollectionViewSource? Before changing my code to use the CVS, I used an ObservableCollection. I was in the need to filter and sort the items inside there, thus I changed the code to use the CVS. Now my designer complains about not being able to fill the SampleData's NextItems with a proper structure to show up in Expression Blend. Here is some code I use inside the app:
MainViewModel.cs
class MainViewModel
{
public MainViewModel()
{
AllItems = new ObservableCollection<ItemViewModel>();
NextItems = new CollectionViewSource();
NextItems.Source = AllItems;
}
public CollectionViewSource NextItems
{
get;
private set;
}
public ObservableCollection<ItemViewModel> AllItems
{
get;
private set;
}
some functions to fill, filter, sort etc...
}
MainView.xaml:
<phone:PhoneApplicationPage
... some other stuff ...
d:DesignWidth="480"
d:DesignHeight="728"
d:DataContext="{d:DesignData SampleData/SampleData.xaml}">
<Grid
x:Name="LayoutRoot"
Background="Transparent">
<controls:Panorama>
<controls:PanoramaItem>
<ListBox ItemsSource="{Binding NextItems.View}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Image}" />
<StackPanel>
<TextBlock Text="{Binding FullName}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PanoramaItem>
</controls:Panorama>
</Grid>
</phone:PhoneApplicationPage>
SampleData.xaml
<local:MainViewModel
xmlns:local="clr-namespace:MyAppNamespace"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:swd="clr-namespace:System.Windows.Data;assembly=System.Windows" >
<local:MainViewModel.AllItems>
<local:ItemModel
FullName="Dummy"
Image="/Images/dummy.png" />
</local:MainViewModel.AllItems>
<local:MainViewModel.NextItems>
How to fill the CollectionViewSource's Source?
</local:MainViewModel.NextItems>
</local:MainViewModel>
So the question I can't find an answer to is how to fill the Source for NextItems in SampleDate.xaml? Any help would be much appreciated.
if you want to show sample data in the designer I would recommend you to do it from code. There are two ways of generating sample data for the Blend Designer or the VStudio designer:
From an XML file as you do.
From a c# class -> Best option
best option.
In WPF, in windows 8 and in WP7.5 and highger, you can access a propertie called:Windows.ApplicationModel.DesignMode.DesignModeEnabled making use of it you can seed your ObservableCollection from your view model:
public class MainViewModel
{
public MainViewModel()
{
AllItems = new ObservableCollection<ItemViewModel>();
if (DesignMode.DesignModeEnabled)
{
AllItems = FakeDataProvider.FakeDataItems;
}
NextItems.Source = AllItems;
}
public CollectionViewSource NextItems
{
get;
private set;
}
public ObservableCollection<ItemViewModel> AllItems
{
get;
private set;
}
}
In this way, if you change the model, you dont' have to regenerate an XML file, it's a little bit cleaner from a C# file. The FakeDataProvider is an static class where all design-time fake data are stored. So in you XAML the only thing you have to do is to bind your Listbox to the collection of your ViewModel.
I am newbie to WPF, and needs help to bind data into the ComboBox. The xaml file contains the tag below.
<UserControl x:Class="SKAT.Postfordeler.Client.UI.View.ChooseInboxView"
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"
d:DesignHeight="42" d:DesignWidth="598">
<Grid>
<StackPanel Orientation="Horizontal">
<ComboBox Name="_currentInbox" Width="180" Margin="5" Height="22" DataContext="{Binding}" />
<Label Content="Et job kører allerede i denne indbakke (1500 ud af 1700 poster behandlet)" Name="_currentProcess" Margin="5" Height="25" />
</StackPanel>
</Grid>
//Inbox class , this class was implemented in seperate project
namespace SKAT.Postfordeler.Shared.DataTypes
{
[DataContract]
public class Inbox
{
[DataMember]
public String Id { get; set; }
[DataMember]
public String Folder { get; set; }
[DataMember]
public Rule Rules { get; set; }
}
}
//This code is located in the controller, the Activate method will fire when the MainWindow was executed
public void Activate()
{
var configuration = _configurationManager.GetConfiguration();// this method gets the xaml file settings
_chooseInboxView.FillInboxes(configuration.Inboxes); // Inboxes data binds to combobox
}
and in the View code behind, I created a method to bind the data which contains a type of list
public void FillInboxes(List<Inbox> inboxes)
{
DataContext = inboxes;
}
But it won't works,Any help please?
I assume your Inbox class consists of two properties (for simplicity), but there may be any number of them:
public class Inbox
{
public int ID { get; set; }
public string Text { get; set; }
}
You write a DataTemplate, for example:
<Grid.Resources>
<DataTemplate x:Key="InboxTemplate">
<WrapPanel>
<TextBlock Text="{Binding Path=ID}"/>
<TextBlock>:</TextBlock>
<TextBlock Text="{Binding Path=Text}"/>
</WrapPanel>
</DataTemplate>
</Grid.Resources>
Then correct your ComboBox declaration like:
<ComboBox Name="_currentInbox" Width="180" Margin="5" Height="22" ItemsSource="{Binding}" ItemTemplate="{StaticResource InboxTemplate}" />
Finally you set DataContext of your ComboBox to your List<Inbox>:
public void FillInboxes(List<Inbox> inboxes)
{
_currentInbox.DataContext = inboxes;
}
EDIT: As you've asked for a simpler solution, you can just override ToString() method of your Inbox class:
protected override string ToString()
{
return ID.ToString() + ":" + Text;
}
Instead of DataContext={Binding} you should have ItemsSource={Binding}.
The data context for any frameworkelement in the visual tree is by default {Binding}.
<ComboBox Name="_currentInbox"
SelectedItem="Hoved"
Width="180"
Margin="5"
Height="22"
DisplayMemberPath="Name"
ItemSource="{Binding}" />
Also for the combobox to display text of the items correctly I suppose you need DisplayMemberPath too. I assumed the property from Inbox class that you need to display is Name. Please replace with your relevant property name.
If your Inbox class is like,
public class Inbox
{
public int ID { get; set; }
public string Text { get; set; }
}
And if you do not want to change your xmal, the code behind method should be like this,
public void FillInboxes(List<Inbox> inboxes)
{
_currentInbox.DisplayMemberPath = "Text"; // To display the 'Text' property in the combobox dropdown
//_currentInbox.DisplayMemberPath = "ID"; // To display the 'ID' property in the combobox dropdown
_currentInbox.DataContext = inboxes;
}
Regard this image:
alt text http://img25.imageshack.us/img25/9743/timetablepo.png
The TimeTableViewModel of this user interface is this:
public string SchoolclassCodeMonday {get;set;}
public string SchoolclassCodeTuesday {get;set;}
public string SchoolclassCodeWednesday {get;set;}
public string SchoolclassCodeThursday {get;set;}
public string SchoolclassCodeFriday {get;set;}
public string SchoolclassCodeSaturday {get;set;}
public string SchoolclassCodeSunday {get;set;}
The above would work when I would display only the properties as string in a textbox.
But what I want is to bind each combox to a mutual ObservableCollection
SchoolclassCodes and the SelectedItem aka DisplayMember of the ComboBox must somehow be
MAPPED to one of the 7 above Properties AND the SelectedItem if retrieved must supply a row
of all 7 schoolclass selected in the combobox.
Or what I actually want just in other words ;-)
Display in the ComboBox the SchoolclassCodes list, set the Value of SelectedItem.SchoolclassCode"WeekdayName" to the Value of the selected ComboboxItem.SchoolclassCode
Well there are some ideas I have but all lack some experience to make them fully working.
I could add to the TimeTableViewModel for each Property a ObservableCollection
SchoolclassCodes but that seems very redundant to me. Why should I hold 7 lists for ONE row when each cell has the same list with the same items in it ?
Any suggestions concerning the ViewModels structure and the binding in Wpf are welcome :)
UPDATE: My SchoolclassCodes list is dynamically created, so I there is no possibility about static binding or hardcode string items in XAML...
UPDATE2:
OK I tried to make it working with MVVM:
I had to change the ObservableCollection ClassCodes
to ObservableCollection SchoolclassCodes as the Schoolclass object
has a reference to Pupil class with strings thats not possible.
Schoolclass.cs:
public string SchoolclassCode {get;set;}
...
TimeTableWeekViewModel.cs:
public ObservableCollection<Schoolclass> SchoolclassCodes
{
get { return _schoolclassCodes; }
set
{
_schoolclassCodes = value;
this.RaisePropertyChanged("SchoolclassCodes");
}
}
XAML:
How must the binding look like NOW because the SchoolclassCodes is not found by wpf ?
I recommend that you look into the MVVM (Model-View-ViewModel) pattern if you are doing anything remotely complicated. Also, it is useful to implement INotifyPropertyChanged in your model/viewmodel (e.g. the MyWeek class in my example below). That will notify any other bindings whenever one of your SchoolclassCode<Day> properties is changed.
Here is some simple sample code to get you started:
using System.Collections.ObjectModel;
using System.Linq;
namespace BindingSample
{
public partial class Window1
{
public Window1()
{
InitializeComponent();
SchoolclassCodes = new ObservableCollection<string>(
Enumerable.Range(1, 10).Select(i => "Code #" + i));
MyWeeks = new ObservableCollection<MyWeek>(
Enumerable.Range(1, 5).Select(i => new MyWeek() {SchoolclassCodeMonday = SchoolclassCodes.First()}));
DataContext = this;
}
public ObservableCollection<string> SchoolclassCodes { get; private set; }
public ObservableCollection<MyWeek> MyWeeks { get; private set; }
}
public class MyWeek
{
public string SchoolclassCodeMonday { get; set; }
public string SchoolclassCodeTuesday { get; set; }
public string SchoolclassCodeWednesday { get; set; }
public string SchoolclassCodeThursday { get; set; }
public string SchoolclassCodeFriday { get; set; }
public string SchoolclassCodeSaturday { get; set; }
public string SchoolclassCodeSunday { get; set; }
}
}
<Window x:Class="BindingSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<CollectionViewSource x:Key="ClassCodes" Source="{Binding SchoolclassCodes}" />
</Window.Resources>
<ListView ItemsSource="{Binding MyWeeks}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Monday">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding SchoolclassCodeMonday}"
ItemsSource="{Binding Source={StaticResource ClassCodes}}"
IsSynchronizedWithCurrentItem="False" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!-- Other columns here... -->
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Window>
Since the columns must be auto generated, putting this code in the appropriate place (Loaded event?) should do what you want :
ObservableCollection<String> Collection = GetCollection();
foreach (DataGridComboBoxColumn column in DataGrid1.Columns.OfType<DataGridComboBoxColumn>())
{
column.ItemsSource = Collection;
}
Of course, some modifications must be applied!
You don't need to mess around with the DataGrid, and you certainly don't need to create any class with seven different properties that you have to implement property-change notification for. The Grid control makes calendars easy.
Your view model should have three classes: Month, Week, and Day. The Week class contains a list of Day objects, and the Month class has a list of Week objects. Each class should have a reference to its parent, i.e. Week in Day and Month in Week.
The Week class's constructor should initialize its Days property to be a list of 7 Day objects. The Month class's constructor has to have more specific logic in it for setting up its Weeks property; I'll leave that to you.
The Day object should expose these properties:
public DayOfWeek DayNumber { get; private set; }
public ObservableCollection<SchoolclassCode> Codes { get { return Week.Codes; } }
as well as a read/write string Code property that does property-change notification.
The Week object should expose:
public ObservableCollection<SchoolclassCode> Codes { get { return Month.Codes; } }
public IEnumerable<Day> Days { get; private set; }
public IEnumerable<string> Codes
{
get { return Days.Select(x => x.Code); }
}
You can then define a data template for presenting days:
<DataTemplate DataType="{x:Type local:Day}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Sunday"/>
<ColumnDefinition SharedSizeGroup="Monday"/>
<ColumnDefinition SharedSizeGroup="Tuesday"/>
<ColumnDefinition SharedSizeGroup="Wednesday"/>
<ColumnDefinition SharedSizeGroup="Thursday"/>
<ColumnDefinition SharedSizeGroup="Friday"/>
<ColumnDefinition SharedSizeGroup="Saturday"/>
</Grid.ColumnDefinitions>
<ComboBox
Grid.Column="{Binding DayNumber}"
ItemsSource="{Binding Codes}"
SelectedValue="{Binding Code, Mode=TwoWay}"/>
</Grid>
</DataTemplate>
and weeks:
<DataTemplate DataType="{x:Type Week}">
<ItemsControl ItemsSource="{Binding Days}">
<ItemsControl.ItemTemplate>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
and months:
<DataTemplate DataType="{x:Type Month}">
<ItemsControl ItemsSource="{Binding Weeks}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Adding day names and week numbers to the above templates is straightforward.