WPF Listbox Grouping Header Not Binding - wpf

I have a CollectionViewSource, It is grouped by a Created proprty in an ObservableCollection. The Grouping works within the listbox with the exception to i cannot get the Header text to display the created date.
The CollectionViewSource is below:
<Window.Resources>
<CollectionViewSource x:Key="TaskListColSource" Source="{Binding Path=TaskItems}">
<CollectionViewSource.SortDescriptions>
<componentModel:SortDescription PropertyName="Created" />
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Created" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
My Listbox GroupStyle is below:
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Foreground" Value="White" />
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Foreground="White">
<Expander.HeaderTemplate>
<DataTemplate DataType="{x:Type models:TaskItem}">
<TextBlock x:Name="asdf" Text="{Binding Created}" Foreground="White"/>
</DataTemplate>
</Expander.HeaderTemplate>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
Could anyone possibly help me with displaying the created date within the relevant Textblock?
I am binding the listbox in the following way:
<ListBox x:Name="TaskListBox" ItemsSource="{Binding Source={StaticResource TaskListColSource}}">

You need to set Header for your Expander like this:
<Expander IsExpanded="True" Foreground="White"
Header="{Binding}">
</Expander>
The implicit DataContext in each GroupItem is each data item. So the Header should be set to each data item (via {Binding}). The HeaderTemplate has implicit DataContext as its Header but you has not assigned anything for it.
Edit:
If you want some StringFormat, why not set it inside TextBlock (let the Header unchanged):
<TextBlock x:Name="asdf" Text="{Binding Created, StringFormat=dd MMM yy, ConverterCulture=en-GB}" Foreground="White"/>

Related

WPF - Move ListView.GroupStyle to external XAML

After several hours I gave up.
I have the following ListView (in Grid) with GroupStyle defined inside of it.
I want to take it out somehow and put it in a Template or Style (I'm confused) and then add it to my main Style of the ListView (ListViewSimpleStyle).
This way it will be reusable in other places instead of copy-paste it every time.
How do I do it?
<ListView Name="LvDataBinding" Grid.Row="0"
Style="{StaticResource ListViewSimpleStyle}">
<!-- Define the grouping-->
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontSize="12" Text="{Binding Name}"
Foreground="{StaticResource GrayForgroundBrush}"></TextBlock>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
Thanks
Groupstyle is style of Groupbox so we need to edit style of Groupbox and I have changed GroupBox HeaderTemplate as you want to change HeaderTemplate of GroupStyle.
Visit this:http://msdn.microsoft.com/en-us/library/ms754027(v=vs.90).aspx
and Add GroupBox With Its style in your own listview template style i.e ListViewSimpleStyle
<Style x:Key="ListViewSimpleStyle" TargetType="ListView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListView">
<Grid Background="AliceBlue" >
<GroupBox>
<GroupBox.Style>
<Style TargetType="GroupBox">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock FontSize="12" Text="{Binding Name}" Foreground="{StaticResource GrayForgroundBrush}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupBox.Style>
</GroupBox>
<ItemsPresenter></ItemsPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Something Like For Loop in XAML

I have a Resource Dictionary in which I want to have a common DataTemplate for ComboBox.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate DataType="{x:Type ComboBox}">
<StackPanel Orientation="Horizontal">
<!--Here I need to use something like For Loop-->
<TextBlock Text=""></TextBlock>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
Now I have created a dependency property of type integer named NoOfColumns. While declaring the comboBox I need to set the NoOfColumns property to automatically generate that number of columns. I want them to databind.
Update as requested by Joe
<ComboBox x:Name="cbUnder" ItemsSource="{Binding GroupsAndCorrespondingEffects}"
IsEditable="True" SelectedItem="{Binding SelectedGroup, Mode=TwoWay}"
Text="{Binding InputValue, UpdateSourceTrigger=PropertyChanged}" TextSearch.TextPath="GroupName"
Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="3">
<ComboBox.Resources>
<DataTemplate DataType="{x:Type vm:GroupAndCorrespondingEffect}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" Width="250">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="{Binding CorrespondingEffect}" />
</StackPanel>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>
There's nothing like for in XAML, but ItemsControl is very much like foreach. Instead of setting an int property, make an ObservableCollection<T> and add that many objects to it, and then bind the ItemsControl to your collection property.
This has the added benefit that each collection item can expose properties to be bound, e.g. if you wanted to display different text in each TextBlock, you could put a property on your collection item and bind the TextBlock to that property.

Need multiple styles dependent on Listboxitem

i have a Listbox, which stores two different object types, based on the same baseclass. (e.g. BaseObject = baseclass and the children of it: CustomPath and CustomImage)
The Datasource:
ObservableCollection<BattlegroundBaseObject> _baseObjectCollection;
public ObservableCollection<BattlegroundBaseObject> BaseObjectCollection
{
get { return _baseObjectCollection?? (_baseObjectCollection= new ObservableCollection<BaseObject>()); }
}
The Listbox databinding: <ListBox ItemsSource="{Binding BaseObjectCollection}"
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" x:Name="ListBoxPathLineStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem" x:Name="BattlegroundObjectControlTemplate">
<Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}" Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}">
</Path>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect" TargetName="PathLine">
<Setter.Value>
<DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
I want to add to the ControlTemplate where the Path is, also a Image and to differ it by type or a property. doesnt matter.
anyone any ideas?
You can add to ListBox resources DataTemplate for each type.
In my example classes Car and Motorbike derived from Vehicle class.
<ListBox x:Name="listBox">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Car}">
<StackPanel Background="Red">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Motorbike}">
<StackPanel Background="Orange">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
EDIT:
You can add style for ListBoxItem to resources:
<ListBox x:Name="listBox">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type local:Car}">
<StackPanel Background="Red">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Motorbike}">
<StackPanel Background="Orange">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
You could define some DataTemplates for your different classes. They determine how the classes are displayed. I've been using them to display derived classes differently when working with a collection of the base class.
<DataTemplate DataType="{x:Type CustomPath}">
<TextBlock Text="This is a CustomPath"/>
</DataTemplate>
<DataTemplate DataType="{x:Type CustomImage}">
<TextBlock Text="This is a CustomImage"/>
</DataTemplate>
At the moment you are changing the style of the controls that WPF is using to render your bound data. A better way to do this is to provide WPF with a way of generating the correct controls. Ignore the ListBoxItem and use DataTemplates for your actual objects.
First you need to tell the Window or control how to find your types.
<Window or UserControl
...
xmlns:model="clr-namespace:yourNamespace"
>
Then you can provide WPF with a way to show your objects e.g.
<DataTemplate TargetType="{x:Type model:CustomPath}">
<Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}"
Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}"/>
<!-- maybe use a binding from the Path.Effect back to the IsSelected and ValueConverters
to re-apply the selection effect-->
</DataTemplate>
<DataTemplate TargetType="{x:Type model:CustomImage}">
<Image Src="{Binding SomeProperty}" />
</DataTemplate>
Now all you need to do is to make these available to the ListBox in some way. Almost every element in WPF can have .Resources added to it, so you could choose to do these across the entire window
<Window ...>
<Window.Resources>
<DataTemplate .../>
<DataTemplate .../>
</Window.Resources>
...
<ListBox .../>
</Window>
or you can apply it more locally
<Window ...>
...
<ListBox>
<ListBox.Resources>
<DataTemplate .../>
<DataTemplate .../>
</ListBox.Resources>
</ListBox>
</Window>
And this way your listbox definition can become much neater too, e.g. if you are using Window.Resources
<ListBox ItemsSource="{Binding BaseObjectCollection}"/>

How to make CollectionView grouping respect data template settings

I have ListBox with group style:
<GroupStyle HidesIfEmpty="True" x:Key="GroupStyle">
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel Background="LightSkyBlue"
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}},
Path=ActualWidth}">
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter/>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
Any change in bound collection causes expanders to reopen.
Is there any way to remember user preferences and leave expanders folded?
The question is what you are doing with the bound collection.
I agree with Nikolay that the best option is to have your viewmodel have a property that you will bind the expander to.
As for the question I've asked at the beginning: Are you somewhat refreshing the CollectionView?
Refresh causes the UI to be recreated. (Meaning, it is like Reset on an ObservableCollection. It tells the UI to create the user controls and load the XAML of your templates again which is bad for performance).
I can suggest a simple hack - just add IsExpanded property to your model class and bind Expander.IsExpanded to it.

WPF UserControl exposing inner listitems

I'm trying to write a UserControl to display a list of items where each of these items is a title and a group of checkboxes. This whole will represent a form of data where the person filling it in is answering a list of questions with a 1 to 4 value. This all works and binds nicely to the window's ViewModel.
But I've currently got the answers hardcoded in the UserControl as follows:
<ListBox
ItemsPanel="{StaticResource HorizontalScores}"
Style="{StaticResource styleOuterListBox}"
ItemContainerStyle="{StaticResource styleOuterListBoxItem}">
<ListBoxItem>Never</ListBoxItem>
<ListBoxItem>Sometimes</ListBoxItem>
<ListBoxItem>Often</ListBoxItem>
<ListBoxItem>Always</ListBoxItem>
</ListBox>
I would like to set these from the window's XAML or from the ViewModel as they will be different for other forms but can't see the correct incantation. How do I remove the ListBoxItems from the UserControl and use databinding instead?
BigEdit ...
Ok, this is the actual user control (it looks hideous but that's not the point):
<UserControl x:Class="BussPerry.Scorer" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:BussPerry.ViewModel" xmlns:local="clr-namespace:BussPerry">
<UserControl.Resources>
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="Gray" />
<SolidColorBrush x:Key="SelectedForegroundBrush" Color="Red" />
<ItemsPanelTemplate x:Key="HorizontalScores">
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<Style x:Key="styleListBox" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<ItemsPresenter Margin="2" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="styleListBoxItem" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<CheckBox Name="CheckBox" Padding="1" Width="60"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter HorizontalAlignment="Center"/>
</CheckBox>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="CheckBox" Property="Background" Value="{StaticResource SelectedBackgroundBrush}" />
<Setter TargetName="CheckBox" Property="Foreground" Value="{StaticResource SelectedForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ListBox ItemsPanel="{StaticResource HorizontalScores}" Style="{StaticResource styleListBox}"
ItemContainerStyle="{StaticResource styleListBoxItem}" SelectedIndex="{Binding Path=Score}">
<ListBoxItem>Never</ListBoxItem>
<ListBoxItem>Sometimes</ListBoxItem>
<ListBoxItem>Often</ListBoxItem>
<ListBoxItem>Always</ListBoxItem>
</ListBox>
</UserControl>
And it's being called as follows:
<ListView
Name="listviewScores"
ItemsSource="{Binding Path=Scores}"
Margin="5"
BorderThickness="0"
Background="Transparent"
Focusable="False"
Grid.Row="3">
<ListView.View>
<GridView
ColumnHeaderContainerStyle="{StaticResource styleHiddenHeader}">
<GridView.Columns>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Path=Index}"
HorizontalAlignment="Right" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn
DisplayMemberBinding="{Binding Path=Title}" />
<GridViewColumn >
<GridViewColumn.CellTemplate>
<DataTemplate>
<local:Scorer >
</local:Scorer>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
What I want to do is to move the Never/Sometimes/Often/Always listboxitems from being hard coded in the user control to be databound.
(Suggestions of "you don't want to do it like that" are also welcome!)
(one year later...)
I think your question is similar to mine. I have come up with a technique to expose the ItemsSource of an inner control on a UserControl. The link to my question is here:
Exposing inner Control properties for binding in WPF
I know that my solution works. What I don't know is if it violates some sacred 'best practices' out there in WPF. It 'feels' right though.
Do you want to bind a collection to a listbox?
It's pretty simple...
<ListBox ItemsSource="{Binding Answers}" />
where Answers is your collection exposed in your ViewModel.
If you're having trouble creating a custom control that exposes an ItemsSource, then you just need to inherit from ItemsControl instead of just UserControl.
EDIT:
Some assumptions:
the DataContext of the ListBox, custom control, or higher parent element is set to your
ViewModel.
the ViewModel has a
property called "Answers".
the Answers property implements
IEnumerable<>.

Resources