Performance degredation with custom ListBox ControlTemplate - wpf

Using a template for a custom control deriving from ListBox causes filtering of ItemSource to become slow. The filtering is done in the get of the ItemSource that the control is bound to. This problem is not present when a normal ListBox is used, so why should it be any different for a custom ListBox?
Filtering:
public IEnumerable<LibraryViewModel> Libraries {
get {
if (!string.IsNullOrEmpty(this.LibrarySearchString))
return _libraries.Where(lib => IsLibraryMatch(lib, this.LibrarySearchString));
else
return _libraries.OrderBy(lib => !lib.IsFavourite);
}
}
Using the control:
<con:FilterListBox Grid.Row="1"
ItemsSource="{Binding Libraries}"
SelectedItem="{Binding SelectedLibrary}"
ItemTemplate="{StaticResource
LibraryItemTemplate}"
SearchString="{Binding LibrarySearchString, Mode=TwoWay}"
IsSearching="False"
Margin="4"/>
The control template:
<Style x:Key="{x:Type con:FilterListBox}" TargetType="{x:Type con:FilterListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type con:FilterListBox}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<TextBlock Text="Search"
VerticalAlignment="Center"/>
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=SearchString,
UpdateSourceTrigger=PropertyChanged}"
Margin="4,0,0,0"/>
</DockPanel>
<ScrollViewer Grid.Row="1" CanContentScroll="True">
<StackPanel IsItemsHost="True"
HorizontalAlignment="Stretch"/>
</ScrollViewer>
<TextBlock Grid.Row="1"
Text="Searching..."
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=IsSearching,
Converter={StaticResource CollapsedIfFalseConverter}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanks for any help.

The slow behavior of your FilterListBox may come with an Virtualization issue. You replaced the ItemsHost of the ListBox with a simple StackPanel. By default, the ListBox uses a VirtualizingStackPanel, which virtualizes the Items whenever possible. See the default ListBox Template as a reference. If you have a simple StackPanel as ItemsPresenter, the ListBox has to re-render every item when your filter changes. Depending on the number of items, this can cause your slow behavior. Try to use the default itemshost instead. You should also know, that virtualization is only possible with 'simple' items (same Height for every item basically).

Related

XAML ViewBox forces a bad rescale on ListBoxItem MouseOver

I have the following XAML layout (ListBox where the Items are RadioButtons styled as ToggleButtons). The ListBox and the ToggleButton items scales and fills the ToggleButton items nicely to adapt to the window size. This is just as I want, but when I have the mouse over a ToggleButton in the ListBox, the style sets the Text to be bold. This is also what I want, but the problem is that it makes the button slightly bigger. When having the mouse over the ToggleButton item with the longest text the ViewBox refreshes and rescales all the ToggleButton controls. It looks like all the ToggleButtons makes a jump. How can I prevent such rescaling of the ToggleButtons still filling out the complete Grid Column and Row with the ListBox and its ToggleButton items?
<Viewbox Stretch="UniformToFill" Grid.Row="1" Grid.Column="0" VerticalAlignment="Stretch" Margin="5,0,0,15">
<Grid x:Name="LeftSide" Grid.Row="1" Grid.Column="0" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<ListBox Grid.Column="0" Grid.Row="0" Grid.RowSpan="10" ItemsSource="{Binding LeftPaneViewModelInfoItems, UpdateSourceTrigger=PropertyChanged}" Background="Transparent" SelectedItem="{Binding SelectedViewModelInfoItem}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton
Content="{Binding Text}"
GroupName="DisplayPage"
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"
Style="{StaticResource RadioButtonToggleButtonStyle}"
>
</RadioButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Viewbox>
Did not find a good solution. I ended up removing the bold setter property from the Mouse Over style.

How to achieve the complex UI using ItemControl in silverlight

I am working on creating a complex report which almost looks like shown in here image
For this I have create a collection where I will store all the descriptions and its corresponding ratings.
This collection is then I am binding to a ItemControl. The collection will be fetched from database depending on criteria's.
Now my problem is how to fragment or separate single ItemControl to look like shown in image. Should I use multiple collections which will be then bind to different ItemControl ? Can I use multiple datagrids?
I am out of ideas... Any suggestions / examples much appreciated.
Definitely do-able. Treat each block (such as Mathmatics, Arts Education etc) as an item, and you're then just dealing with an ItemsCollection. Create a style for how to present each item in that collection, and another style for how to present each property in your block (which will also feature a collection of something.
An example I have of something similar, was blocks which consisted of a heading, and a varied number of checkboxes each with a description. There could be a varying number of these blocks too.
In my view, that displayed these blocks, my xaml looked something like this:
<ScrollViewer VerticalScrollBarVisibility="Visible" MaxHeight="100">
<ItemsControl ItemsSource="{Binding FeatureFlags, Mode=TwoWay}" Style="{StaticResource FlagGroupsAndFlagsItemsControlStyle}" />
</ScrollViewer>
I then had a style for that ItemsControl defined in a resource dictionary somewhere ...
<Style x:Key="FlagGroupsAndFlagsItemsControlStyle" TargetType="ItemsControl">
<Setter Property="Width" Value="Auto" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid x:Name="FlagListGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Width="{Binding Width, ElementName=FlagListGrid, Mode=OneWay}" IsReadOnly="True" Text="{Binding Name}" />
<ListBox Grid.Row="1" Width="{Binding Width, ElementName=FlagListGrid, Mode=OneWay}" ItemsSource="{Binding Flags}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource FlagsListBoxItemsStyle}" Background="{Binding IsOptional, Converter={StaticResource YNToOptionalBackgroundColour}}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
And then a style for the items within that templates ListBox ...
<Style x:Key="FlagsListBoxItemsStyle" TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<toolkit:WrapPanel Width="{Binding Width, ElementName=FlagListGrid, Mode=OneWay}" Orientation="Horizontal" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Width="20" Height="18" VerticalAlignment="Top" IsChecked="{Binding IsSelected, Mode=TwoWay}" />
<TextBlock Width="180" MinHeight="18" VerticalAlignment="Center" Text="{Binding Description}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Unfortunately I can't show you an image of what the finished result looks like, but I hope these pointers could set you on track for your very similar problem

How do I make the combobox close when the dropdown button is pressed a second time?

I have a wpf combobox with some custom items. When the dropdown button is clicked the second time I would like the dropdown to close but instead it is reopened. I've seen similar issues in other applications and I've looked for a solution online but without succes. Here's the code so far:
<ComboBox Name="ComboBoxColorLut"
IsEditable="False"
SelectionChanged="ComboBoxColorLut_SelectionChanged"
ToolTip="Color lookup table"
HorizontalContentAlignment="Stretch">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition ></RowDefinition>
<RowDefinition Height="3"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=Name}"></TextBlock>
<Rectangle Grid.Row="1" Fill="{Binding LinearGradientBrush}"></Rectangle>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Using your control (having removed the SelectionChanged event) works correctly for me with PresentationFramework v4.0.30319. See if temporarily removing the SelectionChanged event fixes the problem.

Silverlight 3: ListBox DataTemplate HorizontalAlignment

I have a ListBox with it's ItemTemplate bound to a DataTemplate. My problem is I cannot get the elements in the template to stretch to the full width of the ListBox.
<ListBox x:Name="listPeople" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="0,0,0,0" Background="{x:Null}" SelectionMode="Extended" Grid.Row="1"
ItemTemplate="{StaticResource PersonViewModel.BrowserDataTemplate}"
ItemsSource="{Binding Mode=OneWay, Path=SearchResults}" >
</ListBox>
<DataTemplate x:Key="PersonViewModel.BrowserDataTemplate">
<ListBoxItem HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,5,5,5">
<Border Opacity=".1" x:Name="itemBorder" Background="#FF000000"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
CornerRadius="5,5,5,5" MinWidth="100" Height="50"/>
</Grid>
</ListBoxItem>
</DataTemplate>
As you can see, I have added a border within the grid to indicate the width of the template. My goal is to see this border expand to the full width of the listbox. Currently its width is determined by its contents or MinWidth, which is the only thing at the moment keeping it visible at all.
I spent an hour trying to resolve this one. Very very frustrasting. You don't have to override the entire default style for the ListBoxItem. I couldn't get this to work. In the end I resolved the issue by simply overriding just the HorizontalContentAlignment property in my ListBox.ItemContainerStyle section e.g:
<ListBox x:Name="ClassList" ItemsSource="{Binding LineClasses}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
SelectionMode="Extended"
ScrollViewer.HorizontalScrollBarVisibility="Hidden" HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch" Loaded="ClassList_Loaded"
VerticalAlignment="Stretch" Grid.Row="0">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" CornerRadius="3" Background="#FFE88D34"
BorderThickness="1" HorizontalAlignment="Stretch" >
<Grid Background="Transparent" HorizontalAlignment="Stretch" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0" HorizontalAlignment="Stretch"
Margin="2"
FontSize="10"
Text="{Binding DisplayClassNm}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
This worked a treat for me.
Myles
When creating Data Templates for ListBox, you should not incldue <ListBoxItem>. The contents of the DataTemplate will be placed inside of a generated container. You can control how that container is constructed using ItemContainerStyle.
The default control style for ListBoxItem is used to define the ItemContainerStyle by default. This style sets the ListBoxItem.HorizontalContentAlignment property to 'Left'. Notice how the ContentPresenter binds its HorizontalAlignment to this property.
You need to override the style of the ListBoxItem container that is being generated when you bind to your ListBox. This can be done by setting the ItemContainerStyle. Set the HorizontalContentAlignment property to be "Stretch".
Below is the default ListBoxItem Style. Included for reference.
<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
<Setter Property="Padding" Value="3"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="TabNavigation" Value="Local"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="{TemplateBinding Background}">
<!-- VSM excluded for readability -->
<Rectangle x:Name="fillColor" Fill="#FFBADDE9" RadiusX="1" RadiusY="1" IsHitTestVisible="False" Opacity="0"/>
<Rectangle x:Name="fillColor2" Fill="#FFBADDE9" RadiusX="1" RadiusY="1" IsHitTestVisible="False" Opacity="0"/>
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
<Rectangle x:Name="FocusVisualElement" Stroke="#FF6DBDD1" StrokeThickness="1" RadiusX="1" RadiusY="1" Visibility="Collapsed"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is an example of using the control templates and data templates for the listbox control. Refer to the XAML markup which streaches the border for the listbox items.
My ListBoxItem contained a CheckBox and the above solutions did not work for me (most likely due to the nature of a CheckBox, not those solutions) I was able to coerce this functionality by not binding to the "Content" property of the checkbox, but explicitely defining the XAML inline:
<CheckBox HorizontalAlignment="Stretch" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
<TextBlock
MinWidth="130"
Margin="0,-2,0,0"
HorizontalAlignment="Stretch"
Text="{Binding Path=DisplayText}" />
</CheckBox>
The margin is needed because the TextBox text did not align with the CheckBox's checkmark. The MinWidth was also necessary.
I am working with Silverlight 5 with VS 2012. I have the same issue. I have horizontal listbox with my own custom objects as ItemsSource. I want listbox items to expand. But they are not expanding. I donot want to give any hard coded width for every listbox item. I tried all answers here but nothing works. I just gave ItemsSource ={Binding Persons} DisplayMemberPath="First Name". Thats all
Two things I noticed here, because I had the same issue and wasn't able to solve it the way you're trying.
First, you don't need to explicitly put a ListBoxItem in your DataTemplate. This is created for you automatically, so you actually have your ListBoxItem inside of the one that was created for you. I checked this out in Snoop to confirm.
Second, and I don't know exactly why, but I wasn't able to get the stretching behavior out of the alignment attributes either. I changed it to use RelativeSource binding on the Width attribute to the ActualWidth property of the containing ListBoxItem. This worked for me.
Width="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ListBoxItem}}, Path=ActualWidth}"
If you need to set style properties on the ListBoxItem that is implicitly created for you, use a Style element inside of the ListBox.ItemContainerStyle element.
Hope this helps...

wpf border control to span the width of listboxItem

Im trying to define a dataTemplate for a business object in my wpf application a collection of which is being bound to a ListBox.
<UserControl.Resources>
<DataTemplate x:Key="ResizedItemsDataTemplate" DataType="{x:Type resizer:ResizeMonitorItem}">
<Border x:Name="bdr" BorderBrush="Blue"
BorderThickness="1"
CornerRadius="2"
Width="auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="14"></RowDefinition>
<RowDefinition Height="14"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding SaveAsFileName}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding ResizedImageFilePath}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0">
<Border BorderThickness="0,0,0,5" BorderBrush="DarkGray" >
<ListBox x:Name="ListBoxResizeItems" ItemsSource="{Binding Path=ResizeItems}" BorderThickness="0" ItemTemplate="{DynamicResource ResizedItemsDataTemplate}">
</ListBox>
</Border>
</Grid>
How can I get the border defined with x:Name=bdr to span the full width of each listbox item? At the moment it only spans the with of the textblocks inside it which dont neccessary fill the full width of the listboxitem and also vary for each listboxitem.
This is probably more to do with the ListBoxItems themselves not taking up the full width of the ListBox. Add the HorizontalContentAlignment="Stretch" attribute to your ListBox and see if it stretches the individual items to fill the width.
Worked it out. The trick is to set the HorizontalContentAlignment="Stretch" on your listbox to make its contents stretch the full width rather than fit the contents only.
<ListBox x:Name="ListBoxResizeItems"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Path=ResizeItems}"
BorderThickness="0"
ItemTemplate="{DynamicResource ResizedItemsDataTemplate}" >
</ListBox>
Sorry Matt, just got your answer thorugh as I was typing this post.
HorizontalContentAlignment is a nice, clean solution compared to what I was trying. Thanks!
Here's what ALMOST worked, but sometimes made a dialog box animated itself wider and wider forever:
Width="{Binding ActualWidth,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}"

Resources