Windows Phone Longlistselector ItemsSource Performance - c#

I'm working on an app for Windows Phone (Silverlight WP 8.0) with a custom UserControl containing three LongListSelectors. This UserControl sits inside of a Panorama. When an item is selected from the first list, the user control changes visual states and animates the second list in, and the same for the third. The problem I'm encountering is the long load time between changing the ItemsSource of the list (either explicitly or through binding) and the actual rendering of the items. With a list of about 20 items, the time is at least 500ms, sometimes over 1 second. This seems unreasonable. Here's the XAML I'm working with:
MainPage.xaml:
<phone:Panorama Template="{StaticResource TunrPanorama}">
<!--Panorama item one-->
<phone:PanoramaItem Background="White" Style="{StaticResource MusicPanoramaItemStyle}">
<tunr:LibraryControl DataContext="{Binding}" TrackSelected="LibraryControl_TrackSelected" />
</phone:PanoramaItem>
LibraryControl.xaml:
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel x:Name="stackPanel" Grid.Row="0" Orientation="Vertical" Height="108">
<!-- Some headers here -->
</StackPanel>
<Grid Grid.Row="1" Margin="12,0,0,0">
<!-- List number 1 -->
<phone:LongListSelector Grid.Row="0" Grid.Column="0" x:Name="music_artists" VerticalAlignment="Stretch" ItemsSource="{Binding ArtistList}" SelectionChanged="music_artists_SelectionChanged" RenderTransformOrigin="0.5,0.5">
<phone:LongListSelector.RenderTransform>
<CompositeTransform/>
</phone:LongListSelector.RenderTransform>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<ListBoxItem Margin="0,6,0,6">
<StackPanel>
<TextBlock Text="{Binding}" TextWrapping="NoWrap" Style="{StaticResource PhoneTextExtraLargeStyle}" FontSize="{StaticResource PhoneFontSizeLarge}" Foreground="Black"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
<!-- List number 2 -->
<phone:LongListSelector Grid.Row="0" Grid.Column="0" x:Name="music_albums" LayoutMode="Grid" GridCellSize="190,190" SelectionChanged="music_albums_SelectionChanged" RenderTransformOrigin="0.5,0.5">
<phone:LongListSelector.RenderTransform>
<CompositeTransform/>
</phone:LongListSelector.RenderTransform>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<ListBoxItem Margin="6,6,6,6">
<Grid>
<Border Width="178" Height="178" Background="#FF838383" />
</Grid>
</ListBoxItem>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
<!-- List number 3 -->
<phone:LongListSelector Grid.Row="0" Grid.Column="0" x:Name="music_tracks" SelectionChanged="music_tracks_SelectionChanged">
<phone:LongListSelector.RenderTransform>
<CompositeTransform/>
</phone:LongListSelector.RenderTransform>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<ListBoxItem Margin="0,6,0,6">
<StackPanel>
<TextBlock Text="{Binding Title}" TextWrapping="NoWrap" Style="{StaticResource PhoneTextExtraLargeStyle}" FontSize="{StaticResource PhoneFontSizeLarge}" Foreground="Black"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
Here's the code that populates the list:
(DataContext as LibraryViewModel).SelectAlbum(album).ContinueWith((songs) => {
Dispatcher.BeginInvoke(() =>
{
music_tracks.ItemsSource = songs.Result;
VisualStateManager.GoToState(this, "Tracks", true);
});
});
All the SelectAlbum method above does now is return a list of newly created Song instances - no other processing is done, so this should not be any sort of performance hog. But still there is a huge delay in rendering.
I've turned off transitions in my visual state changes to no avail; the list still takes too long to load. I've also experimented by creating a fresh new project with a LLS inside of a Panorama, populating it with a button press to see the delay. It's almost instant. The only difference between that and my project seems to be the UserControl I've nested these lists inside of - can that be the cause of this bad performance?
Any suggestions are very much appreciated!
For good measure, here's the class of the item I'm rendering:
public class SongModel
{
public Guid SongID { get; set; }
//public string SongFingerPrint { get; set; }
public Guid OwnerId { get; set; }
public string SongMD5 { get; set; }
public string Title { get; set; }
public string Artist { get; set; }
public string Album { get; set; }
public int TrackNumber { get; set; }
public int DiscNumber { get; set; }
public int Year { get; set; }
public string Genre { get; set; }
public double Length { get; set; }
}

Why do you use LongListSelector? Have you seen this ?
I mean, if an amount of the items is fixed and you do not use grouping, should be enough to use ListBox. Did you try that?

Related

Data binding Pivot control while data binding within each Pivot items data template

So I have an ``ObservableCollection which I'm binding to a Pivot control. This ObservableCollection has a child that is also an ObservableCollection and I'm trying to bind a GridView in each Pivotitem's body.
I've tested my code-behind to ensure the collections are populated i.e.
// Level 1 ObservableCollection
foreach (var dchallenge in ChallengesList)
{
// Level 2 ObservableCollection
Debug.WriteLine(dchallenge.Name);
foreach (var dactivity in dchallenge.Activities)
{
Debug.WriteLine(dactivity.Name);
}
}
And its output is as expected.
For reference, my model is as follows:
public class Challenge
{
public string Type { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Slug { get; set; }
public string Image { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ObservableCollection<Activity> Activities { get; set; }
public class Activity
{
public string Name { get; set; }
public string Description { get; set; }
public long Hash { get; set; }
}
}
And finally, my view:
<Pivot x:Name="Items" ItemsSource="{x:Bind ChallengesList, Mode=OneWay}" Margin="20,417,0,0">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="data:Challenge">
<TextBlock Text="{Binding Name, Mode=OneWay}" FontWeight="Normal" FontSize="16"/>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate x:DataType="data:Challenge">
<StackPanel Margin="0,20,0,0" HorizontalAlignment="Left">
<TextBlock Text="{Binding Description, Mode=OneWay}" MaxWidth="300" TextWrapping="WrapWholeWords"/>
<TextBlock Text="Activities" FontWeight="Medium" MaxWidth="300" Margin="0,20,0,0"/>
<!--<GridView DataContext="{Binding}" ItemsSource="{x:Bind Activities}" SelectionMode="None"
IsItemClickEnabled="False" IsSwipeEnabled="False" CanDragItems="False"
HorizontalAlignment="Left" Margin="0,70,0,0" VerticalAlignment="Top" Width="300">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Challenge">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="{x:Bind Name}" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>-->
</StackPanel>
</DataTemplate>
</Pivot.ItemTemplate>
</Pivot>
A GridView may not be the control that I finally settle on, but if I know how to bind it then I should be able to use that knowledge with another control.
Intellisense helps a lot in determining which properties are available, but Activities isn't one. I only get strings such as Name and Description which work correctly (first two TextBlock).
The only problem I see is that you are using the wrong DataType in the inner GridView. The type in Activities collection is Challenge.Activity, which is a nested class and nested classes are denoted by plus sign (+) in XAML, so data:Challenge+Activity:
<GridView DataContext="{Binding}" ItemsSource="{x:Bind Activities}" SelectionMode="None"
IsItemClickEnabled="False" IsSwipeEnabled="False" CanDragItems="False"
HorizontalAlignment="Left" Margin="0,70,0,0" VerticalAlignment="Top" Width="300">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Challenge+Activity">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="{x:Bind Name}" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
VS may still throw some warnings but those will disappear after successful compilation.

How to use a Binding of the ViewModel instead of the DataType of a ListView?

Let me begin with apologizing for the perhaps somewhat vague title, I'm having a hard time explaining my problem! Perhaps this is why I'm hardly getting any Google results, with this post being the closest to my problem (I think): How to bind to a property of the ViewModel from within a GridView
Anyways, I have a list of news articles that are being dynamically generated. The user has the option to press a "Star"-button in order to add an article to his/her favorites list. This "Star"-button should thus only be visible when the user is logged in.
I'm trying to achieve this by setting the Visibility of the Button to a property called IsLoggedIn inside my ViewModel. However, because this is happening inside my ListView it's trying to find the property IsLoggedIn inside of Article instead of the ViewModel.
So I guess my questions boils down to: How can I bind to a ViewModel property inside of a Databound ListView?
<ListView ItemsSource="{x:Bind VM.Articles, Mode=OneWay}" ItemClick="DebuggableListView_ItemClick" IsItemClickEnabled="True" SelectionMode="None" Grid.Row="1" VerticalAlignment="Top">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Article">
<Grid Margin="0,10,10,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Image}" Margin="0,0,10,0" Grid.Column="0" VerticalAlignment="Top" ImageFailed="Image_ImageFailed"/>
<Button Visibility="{x:Bind VM.IsLoggedIn, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}" FontFamily="Segoe MDL2 Assets" Content="" FontSize="30" Background="Transparent" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
<StackPanel Grid.Column="1">
<TextBlock TextWrapping="Wrap" FontWeight="Bold" Text="{Binding Title}"/>
<TextBlock TextWrapping="Wrap" Text="{Binding Summary}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Requested Article class:
public sealed class Article
{
public int Id { get; set; }
public int Feed { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public DateTime PublishDate { get; set; }
public string Image { get; set; }
public string Url { get; set; }
public string[] Related { get; set; }
public Category[] Categories { get; set; }
public bool IsLiked { get; set; }
}
Okay, so currently I got it working by having a property which gets the singleton of my VM, however I'm sure there has to be a cleaner way to get something simple like this working. I've added a sample rar (OneDrive link: https://1drv.ms/u/s!Ar4fOTiwmGYnyWbwRY6rM0eFsL9x) which has a list, a ViewModel, some dummy data and a Visibility property inside the VM. If you can get it working without my dirty method please feel free to commit as an answer.
This type problem can best be solved using relative binding. Rather than binding directly to the current DataContext, i.e. this case the individual item in the ListView's ItemsSource, you can bind to a property of any ancestor element.
Given a simple ViewModel class
public class ViewModel: ViewModelBase
{
private bool _isLoggedIn;
public bool IsLoggedIn
{
get { return _isLoggedIn; }
set
{
_isLoggedIn = value;
RaisePropertyChanged(() => IsLoggedIn);
}
}
public IEnumerable<string> Items
{ get { return new[] {"One", "Two", "Theee", "Four", "Five"}; } }
}
the View can then be defined as
<Window x:Class="ParentBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ParentBindingTest"
Title="MainWindow"
Width="300" Height="400">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CheckBox Margin="16,8" HorizontalAlignment="Left" Content="Logged In?" IsChecked="{Binding IsLoggedIn, Mode=TwoWay}" />
<ListView Grid.Row="1" Margin="16,8" ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Visibility="{Binding Path=DataContext.IsLoggedIn, RelativeSource={RelativeSource AncestorType={x:Type ListView}}, Converter={StaticResource BooleanToVisibilityConverter}}">
<Image Source="Images\remove.png" />
</Button>
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
Note how in the item DataTemplate, the TextBlock's Text property is bound directly to the item.
However the Button's Visibility is bound not to a propery of it's own DataContext, but to that of the ListView itself, using relative binding.

How to insert an image into a FlipView of a list?

I have a GridView with image, to tap on any of them, you open a FlipView with images of GridView can watch with the swipe. I wish I could do the tapping on an image of the grid and bring up the control FlipView already with that image previously selected, and does not begin to always display the first image of the list. how can I do?
This is XAML code:
<FlipView x:Name="FlipBig" Visibility="Collapsed" ItemsSource="{Binding Source={StaticResource CVSOpere}}" Grid.Column="0" Grid.Row="0" Width="295" Height="340">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="290"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="Gray">
<Image Source="{Binding URLOpera}" ImageFailed="Image_ImageFailed" Margin="3,3,3,3" Grid.Row="0" />
</Border>
<Border Grid.Row="1" Background="Gray" Margin="0,10,0,0" >
<TextBlock Text="{Binding NomeOpera}" Foreground="White" HorizontalAlignment="Center" FontSize="17" TextWrapping="Wrap"/>
</Border>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
this is my class in c#:
namespace App1
{
class MuseoOpera
{
public string NomeMuseoContenuta { get; set; }
public string NomeOpera { get; set; }
public string URLOpera { get; set; }
}
}
The usercontrols are considered derived types, and do not inherit styles implicitly.
One thing you can do is set the style on the UserControl initialization
Style = (Style)FindResource(typeof (UserControl));

Win 8 App Set text in data template

Im trying to learn how to make datatemplate in listviews in my win 8 app
I have the following code in my Xaml code
<!-- Vertical scrolling item list -->
<ListView x:Name="itemListView"
Margin="120,0,0,60"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
SelectionChanged="ItemListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Height="110" Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
<Image Source="{Binding Image}" Stretch="UniformToFill"/>
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Text="{Binding Title}" TextWrapping="NoWrap" FontFamily="Global User Interface"/>
<TextBlock Text="{Binding Subtitle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding Description}" MaxHeight="60" FontFamily="Global User Interface"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
What I cant work out is how to set the text for the three text blocks Title,Subtitle & Description as well as the Picture in Image
Usually When the page loads I use the following in my itemListView_Loaded(object sender, RoutedEventArgs e) method
itemListView.Items.Add(Convert.ToString(correct) + ". " + line.Split(',')[6]);
But how do I do it Im really stumped
Any help appreciated
Mark
You'll have to make a class which includes those properties.
public class MyItem
{
public string Title { get; set; }
public string Subtitle { get; set; }
public string Description { get; set; }
public string Source { get; set; }
}
Then when you add items:
var item = new MyItem();
item.Title = "Title";
item.Subtitle = "Subtitle";
item.Description = "Some example description.";
item.Source = "Assets/SomeFolder/SomeImage.png";
itemListView.Items.Add(item);
That worked in my app (AirPett Transit)

Dynamic Property Value Binding in the ListBox

I have a listbox "lbModernLineView" in which it is bound to "Lines" observable collection and each listbox item is bound with "Line" object. I have another listbox within this listbox named"InnerListBox".
I am going to show a pop up to the user once the user right click on the "InnerListBox" .In this pop up I am going to show all the properties in the "Line" class(for eg: Value1,Value2 etc).
if a user selects a "property" name from the popup, I need to show that property "value" into the InnerListBox. (for eg: if a user selects "value1" ,then the correspoding value in that "Line " objects need to display in the InnerListBox).
User can select upto 5 property names from the Poppup and that five property values and five property names for that "Line" object should should display in the "InnerListBox".
How can I bind these dynamic property values in a listbox item?
public class Line
{
public int Value1{ get; set; }
public int Value2{ get; set; }
public int Value3{ get; set; }
public int Value4{ get; set; }
public int Value5{ get; set; }
public int Value6{ get; set; }
public int Value7{ get; set; }
public int Value8{ get; set; }
}
Following are the part of my listBox sample
<ListBox x:Name="lbModernLineView"
Grid.Row="1"
Margin="2,2,2,2"
FontFamily="Courier New"
FontSize="13" ItemsSource="{Binding Lines,IsAsync=True,
Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2,1,2,1">
<Border.ToolTip>
<TextBlock Text="{Binding desription}" />
</Border.ToolTip>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="110" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="89" />
</Grid.RowDefinitions>
<Border Grid.Column="1"
BorderBrush="Black"
BorderThickness="0,0,2,0" >
<ListBox ItemsSource="{Binding ModernColumn,Mode=TwoWay}" Name="InnerListBox" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"> </ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Prop}" />
<TextBlock Grid.Column="1" Text="{Binding Value}" HorizontalAlignment="Right" Background="Transparent"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<
</ListBox>

Categories