Bind an ObservableDictionary in WPF xaml file - c#

I had an ObservableCollection<MyDataType>object named myCollection that I was presenting in a GUI using a <DataTemplate> in my .XAMLfile (See code below).
But now the type of myCollection has changed to a Dictionary<UInt64,MyDataType>.
How would I present it now? Here I found an implementation of an ObservableDictionary<>. But I don't know how I would bind this to my GUI?
Thank you for any assistance!
<Page x:Class="Project.ThisPage"
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-ThisPage.ProjectSpace"
mc:Ignorable="d"
d:DesignHeight="480" d:DesignWidth="640"
Title="ThisPage"
Name="thisPage">
<Page.Resources>
<DataTemplate x:Key="MyDataTemplate" DataType="{x:Type local:MyDataType}">
<Label Content="{Binding Name}"/>
<Label Content="{Binding Id}"/>
</DataTemplate>
</Page.Resources>
<Grid>
<ScrollViewer>
<StackPanel>
<ItemsControl ItemsSource="{Binding ElementName=thisPage,Path=myCollection}" ItemTemplate="{StaticResource MyDataTemplate}" />
</StackPanel>
</ScrollViewer>
</Grid></Page>

myCollection.Values will give you MyDataType collection and myCollection.Keys will give you UINT64 collection. So bind to MyDataType collection just update binding like:
ItemsControl ItemsSource="{Binding ElementName=thisPage,Path=myCollection.Values}" ItemTemplate="{StaticResource MyDataTemplate}" />

Related

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>
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>

Why does my WPF ListView/GridView not virtualize?

I have performance problem with my listview/gridview.
I traced it down to the view not being virtualized. I removed all business critical code and was left with the following XAML.
<UserControl x:Class="Weingartner.Controls.PointListEditorView"
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="300" d:DesignWidth="300"
x:Name="Root">
<Grid>
<ListView ItemsSource="{Binding ElementName=Root, Path=Points, Mode=OneWay}">
<ListView.View>
<GridView >
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label>Foo</Label>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</UserControl >
When I look in the visual debugger I see this
The ListViewItem instances continue for the full list of the data, ie about 800 points.
When I open up any of the ListViewItem objects I see that they are fully populated as below
As far as I understand the docs say that virtualization is on by default for ListBox and ListView. Is this correct?
Any suggestions?
Edit: This is a screenshot of the full visual tree:
We are using MahApps.Metro and after setting the default ListView style to
VirtualisedMetroListView virtualising works:
<Style BasedOn="{StaticResource VirtualisedMetroListView}" TargetType="ListView" />

How to avoid repeated xaml code

I have a small UI in XAML where I need to display twice the same thing in the same window. I created a Resources with the code, but can't figure out how to display it.
the resources :
<max:MaxUserControl.Resources>
<DataTemplate x:Key="tInfo">
<max:MaxGrid>
<max:MaxGrid.ColumnDefinitions>
...
</max:MaxGrid.ColumnDefinitions>
<max:MaxGrid.RowDefinitions>
...
</max:MaxGrid.RowDefinitions>
...
</max:MaxGrid>
</DataTemplate>
</max:MaxUserControl.Resources>
The only difference between both UI is the Datacontext, so I wanted to do something like :
<max:MaxStackPanel Grid.Row="1" Grid.Column="0" Template="{StaticResource ResourceKey=tInfo}" DataContext="{Binding ElementName=dtgEmployeeOccupation, Path=SelectedItem, Mode=OneWay}"/>
<max:MaxStackPanel Grid.Row="0" Grid.Column="1" Template="{StaticResource ResourceKey=tInfo}" DataContext="{Binding Path=ANOTHERBINDING"/>
What control I should use to achieve this?
Create UserControl and reuse where you want. At first you should create
UserControl, then add some necessary controls inside your UserControl. For example, we are creating UserControl and it would be called FooUserControl:
<UserControl x:Class="OpenExcelFileAndConvertToArray.FooUserControl"
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:OpenExcelFileAndConvertToArray"
mc:Ignorable="d">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="SomeText"/>
<Button Content="Delete"/>
</StackPanel>
</Grid>
</UserControl>
Then just in any other controls you can reuse this FooUserControl. For example:
<Window x:Class="OpenExcelFileAndConvertToArray.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OpenExcelFileAndConvertToArray"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ComboBox Text="qq" Name="comboBox">
<ComboBoxItem Content="1"/>
<ComboBoxItem Content="2"/>
<ComboBoxItem Content="3"/>
</ComboBox>
<!--reusable control-->
<local:FooUserControl/>
</StackPanel>
</Grid>

How do I add items dynamically to a WrapPanel?

I have the following XAML:
<Window x:Class="ImageComparing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="350" Width="525" xmlns:my="clr-namespace:ImageComparing" Title="Image comparing">
<DockPanel>
<ToolBar Name="toolbar1" DockPanel.Dock="Top" Height="41" Background="#FFA5D95A">
/*other content*/
</ToolBar>
<WrapPanel Name="wrapPanel1" >
/*other content*/
<Label Content="Label" Height="28" Name="label1" />
</WrapPanel>
</DockPanel>
</Window>
I want to add content to wrapPanel1 - I tried the following code:
if (info.Attributes == FileAttributes.Directory)
wrapPanel1.Children.Add(new FolderEntry(info.Name));
else
wrapPanel1.Children.Add(new FileEntry(info.Name));
For some reason, the items don't show up. How can I fix that problem?
You should to use some ItemsControl, then add/remove the items from the item source property. You can use the ItemsPanel property or changing the template. For instance, using a ListBox and setting the Template property:
<ListBox Grid.Row="2" ItemsSource="{Binding Items}">
<ListBox.Template>
<ControlTemplate>
<WrapPanel IsItemsHost="True"/>
</ControlTemplate>
</ListBox.Template>
</ListBox>
Now when you add or remove items from the Items property in your ViewModel/DataContext the item will be showed using the WrapPanel, and not the StackPanel that is the ListBox's default.
Hope this helps...

UserControl CollectionViewSource not working

I have a user-control containing this XAML
<UserControl x:Class="QA.JobListControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:QA" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<CollectionViewSource x:Name="itemsSource" IsSourceGrouped="True" />
</UserControl.Resources>
<ListView x:Name="JobListView" Margin="-10,-10,0,0" Padding="120,0,0,60" IsSwipeEnabled="False" ItemsSource="{Binding Source=itemsSource}" SelectionChanged="JobListView_SelectionChanged" SelectionMode="Single">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border HorizontalAlignment="Stretch">
<TextBlock Text='{Binding Status}' Margin="10" />
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<TextBlock Text='{Binding TaskName}' />
<TextBlock Text='{Binding DueDate}' />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
And to set the content I am using this C#-code
itemsSource.Source = Tasks.OrderBy(Tsk => Tsk.DueDate).GroupBy(Tsk => Tsk.Status);
It is showing some of the elements (but they are shown as empty elements), and not all are shown
What could be wrong?
If I am using this C#-code it is working (but it is not grouped)
JobListView.ItemsSource = Tasks.OrderBy(Tsk => Tsk.DueDate);
UPDATE
After adding the StaticResource like below, it now shows multiple groups without items
ItemsSource="{Binding Source={StaticResource itemsSource}}"
So I think you are misunderstanding the basics behind the GroupBy method. GroupBy, as opposed to most other Linq extensions, will not return a simple list of objects, instead it will return a list of IGrouping. IGrouping interface exposes a Key property which will hold the value of the grouping discriminator you passed in the GroupBy lambda.
Therefore, to get the list to display the group name, you have to bind the group header template to Key instead of Status.
<TextBlock Text='{Binding Key}' Margin="10" />
Also if reference your CollectionViewSource as a resource, you need to define a resource key to reference it later in your XAML as a StaticResource.
<CollectionViewSource x:Name="itemsSource" x:Key="groupedTasks" IsSourceGrouped="True" />
And in the list view.
<ListView x:Name="JobListView" ItemsSource="{Binding Source={StaticResource groupedTasks}}">
This way I got your example to work as expected.
As an additional read I dearly recommend you to read this article by Sergei Barskiy which demonstrates how to use grouping in XAML lists and also provides a GroupedData class that in my opinion is much better than the default IGrouping object to expose your data and consume it in the UI.

Categories