Nested binding with templates - c#

I am creating 'Lessons' tab in my application. The problem is in displaying data.
Timetable(List<DayInfo>) is binded to ItemsControl. Each DayInfo is an item in this ItemsControl. I tried to bind Exams collection to nested ItemsControl placed in ItemTemplate, but It's not working.
I'd like to know what I'm doing wrong. I guess my Exams binding is the problem.
Timetable:
private List<DayInfo> timetable;
public List<DayInfo> Timetable
{
get { return timetable; }
set
{
timetable = value;
NotifyOfPropertyChange(() => Timetable);
}
}
There is DayInfo.cs:
public class DayInfo : IValue
{
public string DayName { get; }
public List<ExamEntry> Exams { get; }
...
}
ExamEntry.cs:
public class ExamEntry : DayEntry, IValue
{
public string Description { get; }
...
}
XAML code:
<ItemsControl
ItemsSource="{Binding Timetable}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
...
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<StackPanel
Orientation="Horizontal"
Margin="0">
</StackPanel>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label
Content="{Binding DayName}" /> <!-- It still works -->
<ItemsControl
ItemsSource="{Binding Exams}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label
Content="{Binding Description}" /> <!-- It's not displayed -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Everything is fine with this piece of code. I've forgotten about another part of project, where Timetable was bugged. Sorry for confusion.

Related

Bind content presenter content to a visual element

I have an items control with items source, and i want it to display previews
<ItemsControl ItemsSource="{Binding Path=Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Preview}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Preview is a grid
public class Child
{
public Grid Preview { get; set; }
public Child()
{
Preview = new Grid();
Preview.Children.Add(new TextBlock() { Text = "Test"});
Preview.Background = new SolidColorBrush(Colors.Red);
}
}
But it doesn't seem to render anything, what am I missing?
try like this,
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Preview}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
or
<ItemsControl ItemsSource="{Binding Path=Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Preview}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
there is no datacontext, so it didnt display anything.

Add a TextBox as the last element of an ItemsControl with WrapPanel

I am trying to implement the following scanarios
APPROACH SO FAR
Tried to implement it with an ItemsControl (with WrapPanel) and a TextBox wrapped inside a WrapPanel, but it does not have a desired output as there are two WrapPanels wrapping separately
<toolkit:WrapPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding someThing}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Border>
<TextBlock Text="somesomething" />
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBox/>
</toolkit:WrapPanel>
I am thinking if I can add the TextBox at the END of the ItemsControl, but failed to do so. Please specify if there is any other workaround/ solution to any of my approaches
You need to use DataTemplateSelector for the ItemsControl and specify different templates for different list items.
public class BlockItem
{
// TODO
}
public class BoxItem
{
// TODO
}
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate BlockTemplate { get; set; }
public DataTemplate BoxTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
if (item is BlockItem) return BlockTemplate;
else if (item is BoxItem) return BoxTemplate;
return base.SelectTemplateCore(item);
}
}
XAML:
<ItemsControl ItemsSource="{Binding someObject}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplateSelector>
<local:MyTemplateSelector>
<local:MyTemplateSelector.BlockTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="something"/>
</Grid>
</DataTemplate>
</local:MyTemplateSelector.BlockTemplate>
<local:MyTemplateSelector.BoxTemplate>
<DataTemplate>
<Grid>
<TextBox Text="something"/>
</Grid>
</DataTemplate>
</local:MyTemplateSelector.BoxTemplate>
</local:MyTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
And you then add different types of objects to your items source:
someObject.Add(new BlockItem());
someObject.Add(new BlockItem());
someObject.Add(new BlockItem());
someObject.Add(new BlockItem());
someObject.Add(new BoxItem());
If you want the TextBox to be the last element, then you need it to be the last item in your ItemsSource list.

Stretch GridViewItem to fill screen

I'm making an app that is similar to windows8 calendar app and I have ran into a problem.
So how to stretch my groups in GridView to fill all available space (the same as in month view in windows app)? I've tried to set property HorizontalContentAlignment of GridViewItem but it doesn't take an effect.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<GridView Padding="116,37,40,46" ItemsSource="{Binding Source={StaticResource DayGroups}}">
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Day}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Key}" Style="{StaticResource SubtitleTextBlockStyle}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid />
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
</Grid>
Thanks
ItemSource is just collection of
public class GroupViewModel<TKey, TItem> : ViewModelBase
{
private ObservableCollection<TItem> _items;
private TKey _key;
public TKey Key
{
get { return _key; }
set { Set(ref _key, value); }
}
public ObservableCollection<TItem> Items
{
get { return _items; }
set { Set(ref _items, value); }
}
}
where TKey is string, TItem is DateTime

Grouping with strongly type group key in WPF

Every example and I article I found, is about grouping items by one property and displaying it. But what I have, is a strongly type group key, which I want to display. Here is models and grouping logic:
The item interface
public interface IItem {
string Title { get; }
string ToolTip { get; }
object Icon { get; }
Type GroupType { get; }
}
IItem has many implementations like this:
public class Item : IItem {
public string Title { get; private set; }
public string ToolTip { get; private set; }
public object Icon { get; private set; }
// I have many implementation of IGroup which I will use them in GroupType properties.
public Type GroupType { get { return SomeGroupTypeHere; } }
}
And here is the group interface:
public interface IGroup {
string Name { get; }
object Icon { get; }
}
and it has many implementations too.
I collect them in my view model (by getting help from Autofac):
public class MyViewModel {
private readonly IEnumerable<IGrouping<IGroup, IItem>> _items;
public MyViewModel(IEnumerable<IGroup> groups, IEnumerable<IItem> items){
_items = items.GroupBy(t => {
var g = groups.First(u => u.GetType() == t.GroupType);
return g;
});
}
public IEnumerable<IGrouping<IGroup, IItem>> Items {
get { return _items; }
}
}
Now, the problem is, how to display this grouped items, in a ItemsControl?
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate >
<!-- here is my template that uses IItem properties, example: -->
<Button Content="{Binding Title}"
ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
The code, just only displays the first item in each group, and I have no idea what to do to show group headers (that use IGroup properties) and also show all items in each group. Any suggestion please? Any article or blog-post will be very useful. Thanks in advance.
You want to use HierarchicalDataTemplate when you want to display Grouped data, Change your ItemsControl.ItemTemplate to a `HierarchialDataTemplate'.
Sample (and Untested) HierarchialDataTemplate:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding}">
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Refer to this blogpost for step-by-step approach to HierarchialDataTemplates.
UPDATE
I have tried the above HierarchialDataTemplate with your code and it doesnt seem to work. However, if I replace ItemsControl with TreeView, it does work as expected.
So, I guess, you might want to have nested ItemControl for your case, Tested sample code as below:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
<ItemsControl ItemsSource="{Binding}" Margin="15,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the screen shot with this nested ItemsControls.
UPDATE 2
I think you need to update the template to display Group, the current template doesnt do that.
Updated Template is below:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Key.Name}" />
<ItemsControl ItemsSource="{Binding}" Margin="15,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And the resulting Screenshot:

C# WPF managing nested structures using ItemsControl

So, I have already posted a question about the nested controls structure in WPF, it is here:
Nested controls structure - in XAML or C#?
And I have received a solution as follows:
<ItemsControl ItemsSource="{Binding SomeCollectionOfViewModel}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding SomeCollection}"> <!-- Nested Content -->
...
</DataTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>
I have used that solution, comming up with this:
<!-- The UniformGrids - boxes -->
<ItemsControl ItemsSource="{Binding UniformGridCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- The TextBoxes - Cells -->
<ItemsControl ItemsSource="{Binding TextBoxCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the C# side:
public partial class MainWindow : Window
{
private readonly ObservableCollection<UniformGrid> uniformGridCollection = new ObservableCollection<UniformGrid>();
public ObservableCollection<UniformGrid> UniformGridCollection { get { return uniformGridCollection; } }
private readonly ObservableCollection<UniformGrid> textBoxCollection = new ObservableCollection<UniformGrid>();
public ObservableCollection<UniformGrid> TextBoxCollection { get { return textBoxCollection; } }
public MainWindow()
{
InitializeComponent();
for (int i = 1; i <= 9; i++)
{
UniformGrid box = new UniformGrid();
UniformGridCollection.Add(box);
UniformGrid cell = new UniformGrid();
TextBoxCollection.Add(cell);
}
DataContext = this;
}
}
But somehow the "Cells" - textboxes, that are inside the uniformgrids, do not create. Instead, I get only 9 uniform grids contain in one big uniform Grid (specified in Paneltemplate).
I have checked the prgram with Snoop v 2.8.0:
http://i40.tinypic.com/htzo5l.jpg
The question is: Could somebody tell me, why the inside structure(textboxes) arent created?
You're not defining any TextBoxes anywhere, That's why you're not getting any TextBoxes in the Visual Tree:
<Window x:Class="MiscSamples.NestedItemsControls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NestedItemsControls" Height="300" Width="300">
<ItemsControl ItemsSource="{Binding Level1}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Level2}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}"/> <!-- You Are missing this! -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Also, as mentioned in the comment, your Collections should be of ViewModel or Model types, not UI types:
ViewModels:
public class NestedItemsViewModel
{
public List<Level1Item> Level1 { get; set; }
}
public class Level1Item
{
public List<Level2Item> Level2 { get; set; }
}
public class Level2Item
{
public string Value { get; set; }
}
Code Behind:
public partial class NestedItemsControls : Window
{
public NestedItemsControls()
{
InitializeComponent();
DataContext = new NestedItemsViewModel()
{
Level1 = Enumerable.Range(0, 10)
.Select(l1 => new Level1Item()
{
Level2 = Enumerable.Range(0, 10)
.Select(l2 => new Level2Item { Value = l1.ToString() + "-" + l2.ToString() })
.ToList()
})
.ToList()
};
}
}
Notice that you will have to change the Lists to ObservableCollections if you expect these collections to change dynamically during runtime.
Also notice you have to implement INotifyPropertyChanged properly.

Categories