Performance issue - DataTemplate and DataBinding - 1st load - wpf

Here a drawing of my UI:
I have a list of elements at the top (clickme1 and clickme2). Each of this element will have several information contained inside MyList that I want to display inside the customElement of the listbox.
Clickme1 is clicked by default. When I click on the Clickme2, the listbox is taking 2-3 seconds before updating the CustomElements by the information of the Clickme2. When I switch again, it's updating right away so the problem is visible only for the first click. CustomElement has a lot of different bindings to the Clickme element (images and texts).
Here my code:
public ObservableCollection<CustomClass> MyList { get; set; }
<ListBox x:Name="MyListBox"
ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:CustomElement x:Name="MyCustomElement"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Border" BorderBrush="Transparent" Background="Black">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I tried to use the VirtualizingStackPanel instead of the WrapPanel but it did not improve anything.
Any help ?

When you repopulate the collection, are you assigning a new ObservableCollection? Or working on the existing one?
You should populate your collection "off-line" and then bind it. Or Google for an ObservableCollection derivative that supports "bulk insert". The standard ObservableCollection will send a UI update for every insert.
WRONG:
MyList.Clear();
MyList.Add(...);
RIGHT:
ObservableCollection<CustomClass> coll = new ...
coll.Add(...);
MyList = coll;

Related

Livecharts change default legend

Here is my problem.
I am using LiveCharts to display some data.
Everything fine until showing the legend which represents all the data displayed.
chart with legend
Is it possibile to show a legend based on for example color(Stroke) or DefaultGeometries?
Thanks in advance for your help,
Diego
I know this is a little late but I was looking to do something similar, so here is what I was able to come up with.
You would need to create a new collection and follow the example for Live Charts Energy Predictions.
First you need to set the LegendLocation="None" for the Chart
<wpf:CartesianChart Hoverable="False" DataTooltip="{x:Null}" DisableAnimations="True" LegendLocation="None" Series="{Binding AllSeries, ElementName=FastGraphRoot}">
New Legend Code (part that matters in .xaml):
<ListBox Name="ListBox" MinHeight="250" ItemsSource="{Binding AllSeriesGroupByName, ElementName=FastGraphRoot}" Panel.ZIndex="1" BorderThickness="0" Background="Transparent">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type wpf:LineSeries}">
<TextBlock Text="{Binding Title}"
Foreground="{Binding Stroke}"
FontSize="24"/>
</DataTemplate>
</ListBox.ItemTemplate>
<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>
The Binded list would come from the original series but grouped, I have used a property for this:
private SeriesCollection _allSeriesGroupByName = new SeriesCollection();
public SeriesCollection AllSeriesGroupByName { get { return _allSeriesGroupByName; } set { _allSeriesGroupByName = value; OnPropertyChanged(); } }
you could fill it simply with this code(or anything else you think is faster):
var col = collection.GroupBy(g => ((LineSeries)g).Stroke).Select(p => p.First());
AllSeriesGroupByName = new SeriesCollection();
foreach (var c in col)
{
AllSeriesGroupByName.Add(c);
}

Displaying images in a grid

I have an ObservableCollection of BitmapSources and I want to display them all in a grid and override the selected and not selected styles. I have been looking at a lot of different ways to do this but have not managed to get it working to my satisfaction. My latest attempt looks something like this:
<ListBox ItemsSource={Binding Images}>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel ItemsHost="True">
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="3">
<Image Source={Binding}>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This does display the images, but the WrapPanel is always just one row... I don't want to have to scroll horizontally, so I want it to make row breaks by itself or be able to tell it that it should only have 3 items per row or something like that. Without the WrapPanel the images take one row each. Also, I don't really understand how to override the style for selected items and such since the DataTemplate's DataType is BitmapSource now, not ListBoxItem...
I have also tried a DataGrid (which seems more appropriate) with similar results.
How do I do this?
Define UniformGrid as your ItemsPanel and set the Columns value to the number of items you want to have per row.
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
And your style to override default selection background and border brush setting:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
</Style.Resources>
<Setter Property="BorderThickness" Value="0.5"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="blue"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
You could simply disable horizontal scrolling. This will make the WrapPanel wrap its children.
<ListBox ItemsSource="{Binding Images}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="3">
<Image Stretch="None" Source="{Binding}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
-Edit-
The part about listbox width in this answer is not correct, see clemens answer instead
-Original post-
You need to constrict the width of the list box, either by setting the width or with some other parent layout. Otherwise the wrap panel will claim all the available width to put everything on one row. This happens to me alot when using scrollpanes for example
One tip is to setting different background colors to elements to see who is actually grabbing the space
You can change the selected styles by setting the ItemsContainerStyle

WPF Style: Generalizing

Following being WPF style, is there a way to generalize the hard-coded column names (Name and Code), so that I could specify them when actually applying this style on a ComboBox? Even better, if I could even modify the number of columns?
<Style TargetType="ComboBox" x:Key="MultiColumnComboBoxStyle">
<Style.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border>
<Grid HorizontalAlignment="Stretch" TextElement.FontWeight="Normal">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Code" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}" />
<Rectangle Grid.Column="1" Width="1" Fill="Black" />
<TextBlock Grid.Column="2" Text="{Binding Path=Code}" Margin="5,0,5,0" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Grid.IsSharedSizeScope="True" IsItemsHost="True" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
instead of using a style you could consider craeting a custom control with a dependency property for your columns.
a little bit of setup involved but It will better meet your needs, especialy if you want to reuse it.
an example would be something like the following. Some of this is psuedo code you should be able to fill out.
<!-- In your generic.xaml file -->
<Style TargetType="MyCustomComboBox" >
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="MyCustomComboBox" >
<!-- your template code goes here -->
<Grid x:Name="_myCustomGrid />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
//then in a cs file inherit from the combo box
public class MyCustomColumnComboBox : ComboBox //get all the combobox functionality
{
public IList ComboColumns
{
get { return (IList)GetValue(ComboColumnsProperty);}
set { SetValue(ComboColumnsProperty,value);}
}
public static readonly DependencyProperty ComboColumnsProperty = DependencyProperty.RegisterProperty(...);
private Grid _grid;
public override OnApplyTemplate()
{
//pull your template grid info here, then use that when setting the columns.
_grid = GetTemplateChild("_myCustomGrid") as Grid;
//from here you can check to see if you have your list yet,
//if you don't then you maintain the grid for when you do have your list.
// This can behave different depending on if you are in wpf or silverlight,
// and depending on how you were to add the items to the dependency property.
}
}
In Summary, for you, add the custom control with the custom dependency property, then in your theme/generic.xaml drop in your template and name the grid to what you want to pull into your template in the on apply template function. from there you are either ready to set up or can set up your columns that you specified in the dependency property.
NOTE : The dependency property isn't actually necessary but it can help to buy you a little bit more flexibility later on using things like the dependency properties on change callback to update if necessary.

XamDataGrid Collection Column

Is there a way to bind to a collection column in a XamDataGrid DataSource?
What i am trying to do is to show all the items of the specific column collection in a single grid field. (using the appropriate templates).
Hope it makes sense to you all. Let me know if you need me to clarify things a little bit more.
I finally found the answer.
I just used a Wrapper class to host the collection and bind to the column to the Wrapper class property instead of the collection property.
After that, making the appropriate template is very easy.
Here is an example:
<Style x:Key="ValidationsStyle" TargetType="{x:Type igDP:CellValuePresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type igDP:CellValuePresenter}">
<ContentControl DataContext="{TemplateBinding Value}">
<ItemsControl ItemsSource="{Binding Validations}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ValidationName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ContentControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

How to use Canvas as the ItemsPanel for an ItemsControl in Silverlight 3

I am trying to set the Canvas properties in an ItemsControl DataTemplate with Silverlight 3. According to this post, the only way of doing that is to set it using the ItemsContainerStyle for the ContentPresenter type, since the Canvas properties only take effect on direct children of the Canvas. This doesn't seem to work in SL3, since the ItemsControl doesn't have an ItemsContainerStyle property, so I tried a ListBox as advised by this article, but it still doesn't work. From the XAML below, I would expect to see a green square, with the numbers 10, 30, 50, 70 cascading from "NW" to "SE" direction. Can anyone tell me why they are all stacked on top of eachother in the NW corner?
<UserControl x:Class="TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib" >
<StackPanel>
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" Width="100" Height="100" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding}" />
<Setter Property="Canvas.Top" Value="{Binding}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ListBox.Items>
</ListBox>
</StackPanel>
</UserControl>
I'm not sure if it will work in your scenario, but I've accomplished this in the past using the RenderTransform.
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}">
<TextBox.RenderTransform>
<TranslateTransform X="100" Y="100" />
</TextBox.RenderTransform>
</TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ItemsControl.Items>
</ItemsControl>
Or in the case of binding you will need to use a converter
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" RenderTransform="{Binding Converter={StaticResource NumberToTransformGroupConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ItemsControl.Items>
</ItemsControl>
Converter
public void ConvertTo(object value, ...)
{
int intValue = int.Parse(value.ToString());
return new TransformGroup()
{
Children = new TransformCollection()
{
new TranslateTransform { X = intValue, Y = intValue }
}
};
}
Silverlight4 does not bind to attached properties in style. I suggest using David Anson's approach described here.
<UserControl.Resources>
<Style x:Key="ScreenBindStyle" TargetType="ListBoxItem">
<Setter Property="Helpers:SetterValueBindingHelper.PropertyBinding">
<Setter.Value>
<Helpers:SetterValueBindingHelper>
<Helpers:SetterValueBindingHelper Type="Canvas" Property="Left" Binding="{Binding LocationField.Value.X}" />
<Helpers:SetterValueBindingHelper Type="Canvas" Property="Top" Binding="{Binding LocationField.Value.Y}" />
<Helpers:SetterValueBindingHelper Type="Canvas" Property="ZIndex" Binding="{Binding ZIndex.Value}" />
</Helpers:SetterValueBindingHelper>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And in listbox:
ItemContainerStyle="{StaticResource ScreenBindStyle}"
Old post but I went into the same problem but now with SL5 that now allows Binding in style setters. I was trying to avoid to use the ListBox (because it handles selection and so on) and the ItemsControl still doesn't have an ItemContainerStyle. So I tried a few things.
I haven't found many subject discussing this problem so let me share my solution (sorry if it duplicates)
In fact, I found a very convenient way to solve the problem by adding an unnamed Style in the ItemsControl resources :
<ItemsControl ItemsSource="{Binding Path=MyData}">
<ItemsControl.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding Path=Bounds.Top}"/>
<Setter Property="Canvas.Left" Value="{Binding Path=Bounds.Left}"/>
<Setter Property="Width" Value="{Binding Path=Bounds.Width}"/>
<Setter Property="Height" Value="{Binding Path=Bounds.Height}"/>
</Style>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="my:DataType">
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Works like a charm in SL5 :)
I can't explain what you are seeing either. Your Xaml is broken in at least a couple of ways.
First the Xaml itself fails because this:-
<Style TargetType="ContentPresenter">
should be
<Style TargetType="ContentControl">
the Item Containers in the case of ListBox are of type ListBoxItem which derive from ContentControl.
Still without that the placing {Binding} in style setters still doesn't work. I guess you were imagining that the style would be applied to each item in turn and get its Value from the current item. However even if binding worked in the style, there would only be one style and it would get its data binding from the ListBox DataContext. This is a different DataContext that applies to each ListBox item (which in this case is each Item in the Items collection).
Still I think Ben has a reasonable solution in place which eliminates this approach.

Resources