I have using the itemscontrol in WPF, I have given the dictionary collection as itemsource for itemscontrol. In this dictionary collection, will be used key and observablecollection. Different items will be in observablecollection of each dictionary items. so, when i'm given an itemsource it will be taken same height.
see the code:
<ItemsControl
Grid.Row="1"
Height="Auto"
ItemsSource="{Binding Values}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
IsItemsHost="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<GroupBox
MinWidth="303"
Margin="5,0,0,0">
<ItemsControl Margin="20,5,0,5">
<ItemsControl.Resources>
<CollectionViewSource x:Key="Collection" Source="{Binding Value}" />
<DataTemplate DataType="{x:Type Model:Sensor}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="1"
Content="{Binding Name}"
FontFamily="SegoeUI-Semibold"
FontSize="12"
FontWeight="SemiBold" />
<Label
Grid.Column="2"
HorizontalContentAlignment="Center"
Content="{Binding Value}"
FontFamily="SegoeUI"
FontSize="12" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type Model:DigitalInput}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="1"
Content="{Binding Name}"
FontFamily="SegoeUI-Semibold"
FontSize="12"
FontWeight="SemiBold" />
<Label
Grid.Column="2"
HorizontalContentAlignment="Center"
Content="{Binding InputState}"
FontFamily="SegoeUI"
FontSize="12" />
</Grid>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource Collection}}" />
</CompositeCollection>
</ItemsControl.ItemsSource>
</ItemsControl>
</GroupBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
see the class code:
private Dictionary<string, ObservableCollection<IValue>> values;
public Dictionary<string, ObservableCollection<IValue>> Values
{
get { return values; }
set { values = value; }
}
Current output:
Expected output:
I need to group the items as an expected output, so could you please provide any solution to achieve that?
This is how WrapPanel works. If you set Horizontal all items in row will have same height and it wraps elements to the next row.
You can try specifying Orientation="Vertical" for your WrapPanel, but not quite sure if it suits you. In this case all elements in column will have same width.
Otherwise you don't need either WrapPanel or UniformGrid, you need different panel which is called StaggeredPanel. Source code for uwp can be easily used in WPF, I just checked it.
Only had to rewrite one line which is not a big deal with the following answer: RegisterPropertyChangedCallback(Panel.HorizontalAlignmentProperty, OnHorizontalAlignmentChanged);
An explanation for similar control can be found on codeproject (Called VariableSizedWrapGrid). But I checked it and it has errors somewhere.
On ios it's called mosaic view or StaggeredLayoutManager for RecyclerView on Android.
Instead of WrapPanel, try a UniformGrid:
<UniformGrid Columns="1" IsItemsHost="True" />
Also, I'm not sure about the Height="Auto" setting. Remove it. The setting belongs to the RowDefinition of the grid.
Related
This question already has answers here:
WPF Sorting an ObservableCollection de-selects a ComboBox
(2 answers)
Closed 5 years ago.
I am currently trying to sort a ItemsControl by Name. Currently I have the list printing out but can't seem to order it on the Views side. I have the ability to order it on the Control or model side but want it to work from the view.
My ItemsControl has a binding to AllJobTypes (a list of class JobTypes). JobTypes has a property called Name that I would like to sort in the view.
I have some debug code in the XAML that Prints out the count of each object. The first 2 print out 'fail' and the last one works correctly. What can i do to order AllJobTypes on the view side?
<UserControl.Resources>
<converters:JobTypeCreditUnionCountConverter x:Key="JobTypeCreditUnionCountConverter" />
<CollectionViewSource x:Key="cvs" Source="{Binding RelativeSource={RelativeSource
AncestorType=UserControl}, Path=AllJobTypes}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</UserControl.Resources>
<StackPanel>
<Label Foreground="SteelBlue" FontSize="20" FontWeight="Bold">Job Types</Label>
<Label Content="{Binding AllJobTypes.Count, FallbackValue='fail'}" /> //Fail
<Label Content="{Binding cvs.Count, FallbackValue='fail'}" /> //Fail
<Label Content="{Binding RelativeSource={RelativeSource
AncestorType=UserControl}, Path=AllJobTypes}" /> //(Collection
<ItemsControl ItemsSource="{Binding cvs}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="6*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="" />
<TextBox Grid.Column="1" Text="{Binding Name}" IsEnabled="False" />
Try:
<ItemsControl ItemsSource="{Binding Source={StaticResource cvs}}" >
I have a TabControl in my app. I'd like to have as many TabItems as many entries are in my dictionary.
Here's my dictionary:
public Dictionary<string , ObservableCollection<PerformanceCounter>> Counters
{
get { return _Counters; }
}
Dictionary<string, ObservableCollection<PerformanceCounter>> _Counters = new Dictionary<string , ObservableCollection<PerformanceCounter>>();
Every entry has a string key and ObservableCollection of PerformanceCounter objects. Important thing is the fact that every PerformanceCounter object has properties: CounterName and InstanceName - I'll need these two to display them.
Now, to my XAML:
<TabItem Header="Memory">
<Grid Name="RAMGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Name="RAMListBox" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" ItemsSource="{Binding Memory, Mode=OneWay}" SelectionMode="Multiple" BorderThickness="1" BorderBrush="#FF8B8B8B" SelectionChanged="RAMListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding CounterName, Mode=OneWay}" />
<Run Text="{Binding InstanceName, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="RAMSelectAllButton" Margin="0,10,0,0" Grid.Column="0" Grid.Row="1" Click="RAMSelectAllButton_Click" >
<TextBlock Text="SELECT ALL"/>
</Button>
<Button Name="RAMUnSelectAllButton" Margin="0,10,0,0" Grid.Column="1" Grid.Row="1" Click="RAMUnSelectAllButton_Click" >
<TextBlock Text="UNSELECT ALL"/>
</Button>
</Grid>
</TabItem>
That's what I did and, as you might already know, it does not work. The above code is only for one entry of my dictionary, where the key is "Memory".
In my code I set DataContext:
this.DataContext = appData.Counters;
appData.Counters is that dictionary I presented at the beginning.
Here's what I'd like to achieve:
No matter how many entries there are in my dictionary, my TabControl would display TabItem for each of them.
Each TabItem has a ListBox and 2 buttons. I'll need too be able to access those (in order to clear the list and to have click event for each button).
I really don't know how to do it, I hope you can help me out.
Binding TabControl to items in Dictionary:
<Window.Resources>
<DataTemplate x:Key="templateForTheContent" >
<StackPanel>
<ListBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0"
ItemsSource="{Binding Value, Mode=OneWay}"
SelectionMode="Multiple"
BorderThickness="1" BorderBrush="#FF8B8B8B">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding CounterName, Mode=OneWay}" />
<Run Text="{Binding InstanceName, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="templateForTheHeader" >
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl TabStripPlacement="Left" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Counters}"
ContentTemplate="{StaticResource templateForTheContent}"
ItemTemplate="{StaticResource templateForTheHeader}">
</TabControl>
</Grid>
Now, Dictionary is not observable so if items will be added/removed during runtime, you may consider using something like ObservableDictionary instead
Create a ViewModel-class containing:
your Dictionary
two ICommand-Implementations for your Buttons
then
set the ViewModel-class as DataContext of the TabControl
set Counters as the ItemSource of the TabControl
reuse your XAML-Code defined within the TabItem and use it as
the Tabcontrol.ContentTemplate
Bind .Command of your Buttons to the ICommands in your ViewModel using RelativeSource
see for samples:
ContentTemplate: https://wpf.2000things.com/tag/tabcontrol/
ICommand https://stackoverflow.com/a/1468830/4919708
RelativeSource: https://stackoverflow.com/a/84317/4919708
As i said in one of the comments above I changed my Dictionary to this:
//list of all counters
public ObservableCollection<ObservableCollection<PerformanceCounter>> Counters
{
get { return _Counters; }
}
ObservableCollection<ObservableCollection<PerformanceCounter>> _Counters = new ObservableCollection<ObservableCollection<PerformanceCounter>>();
i used #Arie's solution to write this XAML:
<DataTemplate x:Key="templateForTheContent" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0"
ItemsSource="{Binding}"
SelectionMode="Multiple"
BorderThickness="1" BorderBrush="#FF8B8B8B">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding CounterName, Mode=OneWay}" />
<Run Text="{Binding InstanceName, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="RAMSelectAllButton" Margin="0,10,0,0" Grid.Column="0" Grid.Row="1" >
<TextBlock Text="SELECT ALL"/>
</Button>
<Button Name="RAMUnSelectAllButton" Margin="0,10,0,0" Grid.Column="1" Grid.Row="1" >
<TextBlock Text="UNSELECT ALL"/>
</Button>
</Grid>
</DataTemplate>
<DataTemplate x:Key="templateForTheHeader" >
<TextBlock Text="{Binding CategoryName}"/>
</DataTemplate>
</Window.Resources>
It displays correctly as many Tabs as I add entries to my Class ObservableCollection in the code behind.
Now i have a new problem: I don't know how to access each listBox from each Tab. i need to be able to read the list of selected objects.
Here's the problem. I have a grid with some data written in xaml:
<ItemsControl x:Name="ItemsControl" ItemsSource="{Binding Path=MyObjectCollection, UpdateSourceTrigger = PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="27"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MaxHeight="75" MinHeight="30"></RowDefinition>
</Grid.RowDefinitions>
<Label Name="LabelNumber" Content="{Binding ObjectID}" Grid.Column="0" Style="{StaticResource MyStyleLabel}" />
<control:FocusMeterControl x:Name="HorizontalFocusMeterControl" Value="{Binding ObjectProperty}" Height="Auto" Grid.Column="1" />
<Button Name="RemoveObject" Content="-" Grid.Column="2" Margin="5,0,0,0" Click="ButtonBase_OnClick" Tag="{Binding Point}" Style="{StaticResource MyStyleButton}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As you can see I'm creating a new grid for each member in my "MyObjectCollection". Ideally I think I should just create one row instead since this would make my next problem - the real problem easier.
However, I have not found any good way to do this in xaml if even possible. Is this possible without populating the collection manually from c# and setting a row-property in my objects manually in order to do something like this
<Label Name="LabelNumber" Grid.Row="{Binding ManuallyCalculatedRowID}" ... />
My primary problem is that I would like the grid/rows to all be equally high and if possible fill out the parent window. If the parent window is way too large MaxHeight should apply and I would just like some empty space below.
The parent control is a Windows Form ElementHost if that makes any difference.
Please let me know if you need any additional info.
To make all rows of equal hight we can use ItemsPanelTemplate and set it to UniformGrid that will take care of equal sizing problem:
<ItemsControl x:Name="ItemsControl" ItemsSource="{Binding Path=MyObjectCollection, UpdateSourceTrigger = PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="27"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Label Name="LabelNumber" Content="{Binding ObjectID}" Grid.Column="0" Style="{StaticResource MyStyleLabel}" />
<control:FocusMeterControl x:Name="HorizontalFocusMeterControl" Value="{Binding ObjectProperty}" Height="Auto" Grid.Column="1" />
<Button Name="RemoveObject" Content="-" Grid.Column="2" Margin="5,0,0,0" Click="ButtonBase_OnClick" Tag="{Binding Point}" Style="{StaticResource MyStyleButton}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that I removed row definition in ItemTemplate to lift vertical size restrictions for items. Not sure if that answers your question but that can be good starting point on the road to solution.
I'm currently trying to figure out how to show different types of objects in a GridView, look at this Pic for example:
the last element on the right side is different than the other elements, so if i bind an observablecollection to the GridView, how can i say that the last element is shown up in anohter layout.
currently I'm using this XAML-Code
<GridView x:Name="startView" ItemsSource="{Binding}" Grid.Column="1" Grid.Row="2" SelectionMode="None" Width="Auto">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="DetailTitle" Height="74" Text="{Binding Title}" />
<Image x:Name="Image" Height="Auto" Width="Auto" Margin="0" Stretch="None" Source="{Binding LocalCoverUrl}" Visibility="Collapsed" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal" MaximumRowsOrColumns="2" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
and this Code in the Back:
ObservableCollection<Movie> recentlyStarted = await Api.RecentlyStarted(3);
startView.DataContext = recentlyStarted;
but I have currently no clue how to let the last element show up in a different style
The easy way would be to have the two types of object as different classes (e.g. MoviePicStyle + MoviePlainStyle. Then move your DataTemplate out of the GridView, so that each object is picked up by type,
e.g.
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:MoviePicStyle}">
<StackPanel>
<TextBlock x:Name="DetailTitle" Height="74" Text="{Binding Title}" />
<Image x:Name="Image" Height="Auto" Width="Auto" Margin="0" Stretch="None" Source="{Binding LocalCoverUrl}" Visibility="Collapsed" />
</StackPanel>
<DataTemplate DataType="{x:Type ViewModel:MoviePlainStyle}">
...Different View...
</DataTemplate>
</Window.Resources>
<GridView...
Use template selector property of gridview and depending upon the type of object select the template. I did the same in my project. you need to write your own DataTemplateSelector.
I referred below link
http://babaandthepigman.wordpress.com/2012/02/08/datatemplateselector-winrt/
I have a silverlight application with an ItemsControl which shows a list of items with values and units assigned to them...
Some DataType 1.8 XY
Datatype2 15.6 Units
Other Datatype 1.8 XTZ
The issue I have is that the units are custom and hence I cannot know in advance how long they will be and I need them to line up as shown. So, on the fly, I want to address each of the unit textblocks, find the one with the largest width and set the rest to the same (or set the column to that width).
How can I address each of the textblocks generated in the items control individually in C#?
Here is the xaml so far
<ItemsControl Name="DataTypesGrid" ItemsSource="{Binding}" Margin="0" BorderBrush="{x:Null}" Foreground="White" Background="{x:Null}" IsEnabled="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Margin="0,2,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Left" Grid.Column="0" FontSize="15" />
<TextBlock Text="{Binding Value}" HorizontalAlignment="Right" Margin="0,0,4,0" FontSize="15" Grid.Column="1" />
<TextBlock Text="{Binding Unit}" HorizontalAlignment="Left" FontSize="15" Grid.Column="2" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Thanks for the help
Cap
First get hold of the code for the VisualTreeEnumeration extension methods from here.
Now add a Name property to your unit TextBlock in the data template Name="unitText".
Given the presence of the VisualTreeEnumeration extension methods you can now create a "query" for the boxes:-
IEnumerable<TextBlock> unitBlocks = DataTypesGrid.Descendents()
.OfType<TextBlock>()
.Where(t => t.Name == "unitText");
You can hang on to unitBlocks for as long as DataTypesGrid exists. Using For Each on it will return the latest contents of the ItemsControl. You can use .ToList() on it if you need to temporarily create a List<TextBlock>.