WPF bind to property of the ItemsPanel of an ItemsControl - c#

I've written a code example below:
<StackPanel>
<TextBlock>Appointments today</TextBlock>
<ItemsControl ItemsSource="{Binding Appointments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:CalendarMonthDayEventsItemsPanel OutsideViewportCount="..." />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border>
<TextBlock Text="{Binding Title}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal">
<TextBlock>+</TextBlock>
<TextBlock Text="{Binding ......}" /> <!-- Should show number of items in ItemsControl that are outside view, using CalendarMonthDayEventsItemsPanel.OutsideViewportCount -->
<TextBlock>more items</TextBlock>
</StackPanel>
</StackPanel>
I Created a custom Panel, the CalendarMonthDayEventsItemsPanel, and I use that panel as the itemspanel of the ItemsControl. In the panel's layout code, I determine how many items (panel childs) are outside the bounds of the panel. The property OutsideViewportCount contains the number of items that are outside of the visible bounds of the CalendarMonthDayEventsItemsPanel.
Now I want to show the value of OutsideViewportCount in a TextBlock that's below the ItemsControl.
This means I have to create some kind of binding, but everything I tried doesn't work.
Does someone have a solution or a better approach to achieve the same result?

Maybe you can make use of Tag property of ItemsPanel to relay the value of ItemsPanel as a workaround.
<ItemsControl x:Name="A"
ItemsSource="{Binding Appointments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:CalendarMonthDayEventsItemsPanel OutsideViewportCount="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=Tag, Mode=OneWayToSource}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
...
</ItemsControl>
<StackPanel Orientation="Horizontal">
<TextBlock>+</TextBlock>
<TextBlock Text="{Binding ElementName=A, Path=Tag, Mode=OneWay}"/>
<TextBlock>more items</TextBlock>
</StackPanel>

Related

Can I use a WrapPanel to organize ListView group headers?

I would like to be able to use a WrapPanel to wrap by group header instead of by item.
I have implemented the following ICollectionView view for a ListView bound to an ObservableCollection studentlist:
view = CollectionViewSource.GetDefaultView(studentlist);
view.GroupDescriptions.Add(new PropertyGroupDescription("sectionoutput"));
view.SortDescriptions.Add(new SortDescription("sectionoutput", ListSortDirection.Ascending));
view.SortDescriptions.Add(new SortDescription("displayname", ListSortDirection.Ascending));
The XAML includes a style resource to set the template for the group header:
<Style x:Key="ContainerStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander Header="{Binding Name}" IsExpanded="{Binding Name, Mode=OneWay, Converter={StaticResource expanderconverter}}">
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This Style is used as the GroupStyle for the ListView:
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ContainerStyle}"/>
</ListView.GroupStyle>
...
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding displayname}">
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As is, the code presents a vertical list of student names grouped by a header listing their section number. But I would like to list the groups sequentially in a horizontal direction.
I know that I am able to organize the items in the ListView with a WrapPanel:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" ItemWidth="150">
</WrapPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
But this code merely displays the students in a group side by side rather than displaying the groups themselves side by side. What I would like to do is have the groups themselves wrapped horizontally. I've tried to implement a wrap panel in the style resource, but I haven't been successful and have been wondering if there is a way to wrap by group.
I figure it is possible to do this by switching to a TreeView and turning the groups into Items. But I would prefer to use a ListView if at all possible because my program implements a class that takes the ListView as an argument.
The GroupStyle has a Panel property that you can set to an ItemsPanelTemplate with a WrapPanel:
<ListView.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ContainerStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
Suppose to have a ViewModel that is the DataContext of the XAML posted below that contains an ObservableCollection<GroupViewModel> named GroupsViewModel.
GroupViewModel merely has a property named GroupName that is the name of the Group to show and it has a property of ObservableCollection<StudentViewModel>.
StudentViewModel has the property displayName to display the name of the Student.
Said so, by using an Expander and 2 nested ListBox you can get what you asked, like the following code:
<ListBox ItemsSource="{Binding GroupsViewModel}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Expander HorizontalAlignment="Left" IsExpanded="True" Header="{Binding GroupName}">
<ListBox ItemsSource="{Binding StudentViewModel}" HorizontalAlignment="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding displayname}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Expander>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

How to display listbox items in column view in WPF c#

Am using listbox control in WPF its display listbox item in row wise but I want to display in column wise ( Something like bootstrap grid)
XMAL
<ListBox x:Name="lb_items">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10 0 0 0">
<Image Source="{Binding ImageSource}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Am binding Listbox from code behind
lb_items.ItemsSource = modules.ToList();
Try this one.
<ListBox x:Name="lb_items">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10 0 0 0">
<Image Source="{Binding ImageSource}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ListBox has property called ItemsPanel, which determines how items are rendered.
Somewhere in application resource create different ItemsPanelTemplate, so you can easily reuse it:
<ItemsPanelTemplate x:Key="WrapPanelTemplate">
<WrapPanel />
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
then you can easily use it:
<ListBox ItemsPanel="{StaticResource HorizontalStackPanelTemplate}">...
<ItemControl ItemsPanel="{StaticResource WrapPanelTemplate}">...
always put such assets into application resources in order to make the code for your views clean and readable.
Extra Tip:
Here is animated WrapPanel, that animates items, when you resize window or an item is inserted to the list:
<ItemsPanelTemplate x:Key="FluidWrapPanel">
<WrapPanel>
<i:Interaction.Behaviors>
<ei:FluidMoveBehavior AppliesTo="Children" Duration="0:0:0.5">
<ei:FluidMoveBehavior.EaseY>
<SineEase EasingMode="EaseInOut" />
</ei:FluidMoveBehavior.EaseY>
<ei:FluidMoveBehavior.EaseX>
<CubicEase EasingMode="EaseInOut" />
</ei:FluidMoveBehavior.EaseX>
</ei:FluidMoveBehavior>
</i:Interaction.Behaviors>
</WrapPanel>
</ItemsPanelTemplate>
dont forget to include these xml namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

Wrap panels and item collections

I have a collection defined as
public class BraiderList : ObservableCollection <Braider>
with the objects defines as
public class Braider : INotifyPropertyChanged
The XAML code for displaying the data is shown below. As far as it goes, everything works correctly but I'd like to change how the items in my collection are displayed. I'd like to have each item in my collection be a separate item in a wrap panel instead of them all being part of one control. Is there any way to do this in XAML or do I need to write the code in C# instead?
<Border Grid.Row="1" BorderBrush="Black" BorderThickness="5" CornerRadius="8" Margin="2,2" ClipToBounds="True" >
<WrapPanel Name="WPanel1">
<Border Margin="5" Padding="5">
<ItemsControl Name="MyList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Name="SPanel1">
<Image Source="{StaticResource BraiderImage}" Height ="75" Width="150" Visibility ="{Binding ShowIcon}"/>
<Label Content= "{Binding Name}" Visibility ="{Binding ShowName}" HorizontalAlignment="Center"/>
<Label Content= "{Binding ProductionCounter}" ContentStringFormat="Production Counter: {0:0.0}" Visibility ="{Binding ShowProductionCounter}" />
<Label Content= "{Binding LeadFront}" ContentStringFormat="Lead, Front Deck: {0:0.0}" Visibility ="{Binding ShowLeadFront}"/>
<Label Content= "{Binding LeadBack}" ContentStringFormat="Lead, Back Deck: {0:0.0}" Visibility ="{Binding ShowLeadBack}"/>
<Label Content= "{Binding Address}" ContentStringFormat="IP Address: {0}" Visibility ="{Binding ShowIP}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</WrapPanel>
</Border>
Your XAML seems wrong.
What you're looking for is to set the WrapPanel as the ItemsPanel of the ItemsControl, in such a way that the ItemsControl uses the WrapPanel to layout it's items rather than being inside the WrapPanel itself, like this:
<!-- No Need for a WrapPanel outside the ItemsControl, remove it -->
<Border>
<ItemsControl Name="MyList">
<!-- use a WrapPanel as the ItemsPanel for this ItemsControl -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Rest of your XAML here -->
</ItemsControl>
</Border>

Xaml code explanation

I am working on c# xaml using silverlight and i am bit confused about the hierarchy of this xaml code:
<UserControl.Resources>
<this:MyValueConverter x:Key="TabConverter"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="Green">
<ItemsControl ItemsSource="{Binding Path=TabList, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas></Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="150" Background="red" Height="100" Canvas.Left="{Binding TabList, Mode=TwoWay, Converter={StaticResource TabConverter}}" Canvas.Top="100" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
When i run it gives a big green color window(sorry if technical names are not correct), whereas it should also show red color somewhere in same window as it has Border Width="150" Background="red" .Could some one please explain me what this code is doing ?
ItemsControl really just binds to a list and applies a datatemplate (border with background red) for each item in the list. As for the reason you're only seeing green, well there's probably nothing in the TabList property on your viewmodel. That way, nothing in the items template renders, and all you see is green.
You'll need to make sure that TabList is bound correctly (it exists on your datacontext, whether that's a view model or not) and that it has items in it.
Here's a simpler version of what you may want to accomplish:
<Grid x:Name="LayoutRoot" Background="Green">
<ItemsControl ItemsSource="{Binding Path=TabList, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="150" Background="red" Height="100" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>

Binding a list to a flow of WrapPanels

I am trying to do an interface in WPF where a list of item is displayed, taking as little vertical space as possible:
My instinct was to use an ItemsControl to bind my list, and to put the UI for each item into a WrapPanel. Unfortunately by default, each item starts at a new line regardless of the window's size. Adding a StackPanel with Orientation="Horizontal" makes all items in a single line, regardless of the size...
<!--<ScrollViewer Grid.Row="0" Grid.Column="0"
VerticalScrollBarVisibility="Auto">-->
<ItemsControl Margin="0,4" ItemsSource="{Binding Path=Watchers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type Core:Watcher}">
<WrapPanel Orientation="Horizontal">
<TextBlock Margin="0,2" Text="{Binding Path=Name}"
Width="250px" />
<TextBox Text="{Binding Path=Value, Mode=OneWay}"
Width="300px">
</TextBox>
</WrapPanel>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
<!--</ScrollViewer>-->
Any pointers?
Bonus point: Where should I add a ScrollViewer in order to have the behavior shown in my mockup?
Thanks a lot!
The WrapPanel has, as well as the StackPanel, an Orientation property, which defaults to Vertical. So your list should wrap (assuming you have enough space at hand) if your ItemsPanelTemplate looks like this:
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
About the ScrollViewer:
The desired behavior should be achieved by defining it like follows (not tested though, and I omitted all for this example unnecessary stuff):
<ScrollViewer HorizontalAlignment="Stretch">
<ItemsControl MinWidth="550" Width="{Binding ActualWidth, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}" />
</ScrollViewer>
I think you want the WrapPanel in the ItemsPanel and the StackPanel in the ItemTemplate.
why not use the WrapPanel as ItemsPanelTemplate?
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
as long as the items have space to the left they would be arranged horizontal.

Categories