Windows phone proper way to display collection of items - c#

I have XML file with my items and I'm deserializing them into ObservableCollection<UserControl> and I want to put it to my ItemsControl with my own layout.
Now I've created my own UserControl to handle deserialized data to layout and ObservableCollection is binded to my ItemsControl but if I want to display more than 5-10 items my app if getting unresponsive for a while.
How can I avoid freezes? Should I use DataTemplate within ItemsControl or any other ideas? I'm wondering how its done in apps like twitter or reddit which have many entries and everything is working quite nice. Have already searched for reddit/twitter app source to look how they have implemented it, but without success.
EDIT:
xaml
<ScrollViewer>
<ItemsControl x:Name="items" ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
c#
public MainPage()
{
this.InitializeComponent();
items.DataContext = _items;
}
public ObservableCollection<my_item> _items = new ObservableCollection<my_item>();
adding items
for (int i = 0; i < 40; i++)
{
_items.Add(new my_item());
}
my_item is my own UserControl with 2 small images, few buttons and a single TextBlock.

You should use ListView which has a virtualizing panel for displaying items by default:
<ListView ItemsSource={Binding Items} >
<ListView.ItemTemplate>
<DataTemplate>
<!-- here goes your item layout -->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Maybe one reason for your app "hanging" is if you are deserializing/parsing your XML file on the UI thread, especially if the XML file is large, so the solution to this problem is that you need to do the XML parsing on a background thread:
Task.Run(() =>
{
var items = ParseXML();
});
After edit:
1)
Don't wrap an ItemsControl inside a ScrollViewer, because you will break the virtualization. The ItemsControl itself has its own ScrollViewer so there is no need of an additional one.
If you are on Windows Phone 8.1 Runtime, use ItemsStackPanel instead of VirtualizingStackPanel.
2)
It is recommended for lists with many items to use DataTemplates instead of filling it with UI elements. So rather create a data template from your user control:
<DataTemplate>
<MyUserControl/>
</DataTemplate>

Related

WPF: How to dynamically create a grid with x rows and y columns with consecutive numbers

I am completely new to wpf and c#, so excuse if this is super trivial question. I am trying to create a fairly simple control.
This grid will always have consecutive numbers, with a color rectangle in front of it. Clicking on the gray rectangle will change its color, and set the text to bold (I will deal with these triggers later).
For now, I just need to figure out how to create this control dynamically. When the program starts, it needs to one time create this control, and then the size won't change. I need to tell it the number of columns and rows (each column will probably always have 8 elements), and have it populate with consecutive numbers with specific font style/rectangle color.
I was experimented with creating a stackpanel UserControl for rectangle/label combo, passing the style to it, and then adding 32 of these UserControls in specific row/column in a grid. But I would need the size of that grid to be dynamic, so I need some for loop in the code I think.
Thanks!
I would start with an ItemsControl
You can give it a collection of items, and it will render each item however you want, displayed in any panel you want.
For example, you might have something like this
<ItemsControl ItemsSource="{Binding MyCollection}">
<!-- This panel will be used to hold the items -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="8" Columns="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Each item will be drawn using this template -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Text="{Binding }" Style="{StaticResource MyButtonStyle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Rows and Columns property of the UniformGrid are DependencyProperties, so you could bind them to properties on the DataContext to make them dynamic.
The only problem with a UniformGrid is it only arranges items Horizontally. If you want to display them Vertically, you can either create a custom UniformGrid, or switch to a different panel such as a WrapPanel. If you are new to WPF Panels, I would recommend reading through WPF Layouts - A Quick Visual Start.
The ItemTemplate can be anything. Personally I would use a Button so you have the Click or Command behavior to handle that event, and just overwrite the Button's Template to look however you want. It is an easy task to include your Triggers in there too.
And if you wanted selection behavior, I would recommend switching from an ItemsControl to a ListBox, and overwriting that Template the same way, however it doesn't sound like you need it though, so I think an ItemsControl is better :)
I would try using a listview and change the template to the style you want to use for your elements.
To limit the number of items in a row you can use
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
That way you would always get 3 elements in a row, like
123
456
To make the 3 dynamic you can databind it to some value in your codebehind / viewmodel
to dynamically create the elements within the listview you can add objects to a list/observable collection and then add those to the listview via
listviewname.ItemSource=ListName;
Or however you like. They will get arranged according to how many columns you tell the grid to have. Adding 32 items (with uniform grid of 4) leads to
1 2 3 4
5 6 7 8
9 10 11 12
...
On your page you must create a "main" element, for example a Grid.
Give it a name, so that we can access it by code. Here I gave it the name of root
So you will have something like
<Page
... >
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
x:Name="root">
</Grid>
</Page>
Then, on the .cs file of this page you must create a function with the code below. You can call this function on the MainPage() function.
This loop will create one Grid column with dynamic Grid rows
// Create a Grid and add it to a component of your page
Grid mainGrid = new Grid();
root.Children.Add(mainGrid);
for (int i = 0; i < 8; i++)
{
// I am creating a Grid with a TextBlock inside,
// it will have the same apperance as a Rectangle,
// but this way you can have a Text inside
Grid g = new Grid();
TextBlock tb = new TextBlock();
tb.Text = i.ToString();
g.Children.Add(tb);
// Here you set the Grid properties, such as border and alignment
// You can add other properties and events you need
g.BorderThickness = new Thickness(1);
g.BorderBrush = new SolidColorBrush(Colors.Black);
g.HorizontalAlignment = HorizontalAlignment.Stretch;
g.VerticalAlignment = VerticalAlignment.Stretch;
// Add the newly created Grid to the outer Grid
mainGrid.RowDefinitions.Add(new RowDefinition());
mainGrid.Children.Add(g);
// Set the row of the Grid.
Grid.SetRow(g, i);
}
I used a Grid instead of a Rectangle since Rectangles can't have Children.
It must be easy to create the other columns as well, using the same logic that I used to create the Rows.
The solution to me was pretty like the ones above, but I had to bind the ItemsSource in the back code.
<!-- This panel will be used to hold the items -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Each item will be drawn using this template -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Here is the big deal I think. Desing your template according to what you need. In this case, a grid with 1 default row and column will work-->
<Grid>
<!--Reading object property called "Exchange" and setting its value into a text block-->
<TextBlock
Margin="10, 10, 10, 40" FontSize="16"
HorizontalAlignment="Center"
VerticalAlignment="Center" Foreground="GreenYellow" Text="{Binding Exchange}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
Then in the page.cs I did the binding right in the constructor. But you can bind anytime suits you
List<Bot> Bots = new()
{
new Bot
{
Exchange = "Binance"
},
new Bot
{
Exchange = "Kukoin"
}
};
ItemsControlName.ItemsSource = Bots; // I HAD to bind here. Could not make it work another way.
In my example, I have used a list of a simple class called Bot, with one single property called Exchange. Like this
public class Bot
{
public string Exchange { get; set; }
}
Then I got it:

WPF ListBox and large amound of data

I need to display a large amount of data (thousands of items) in a ListBox however it takes some times and the UI is not responsive until the whole items are displayed in the ListBox.
The ItemsSource of the ListBox is bound the an CollectionView.
I know the benefit of using the VirtualizingStackPanel but I insist to use a WrapPanel.
I've searched over the internet and found some VirtualizedWrapPanels but they have the same issue which is they don't allow the VirtualizingWrapPanel to grow to whatever size it likes and I have to set both Width and Height for them.
Now I need to know what other options are out there for me to do the job? What can I do so that the ListBox loads and displays this large amount quickly.
Please let me know if I haven't explained m
y issue clearly.
Any help is appreciated. Thanks in advance.
Edit
This is the relevant code
public ObservableCollection<T> Items
{
get { return _items; }
set
{
if (_items == value)
return;
_items = value;
OnPropertyChanged(() => Items);
}
}
I instantiate the ObservableColelction in the LoadData method of the ViewModel
_items = _service.Select();
and the CollectionView is instantiated in this way
ICollectionView cv = new CollectionViewSource() { Source = Items }.View;
and the xaml code of the ListBox
<ListBox
x:Name="Items"
ItemsSource="{Binding CollectionView}"
Padding="10,10,10,10"
SelectionMode="Single"
VerticalAlignment="Top"
IsSynchronizedWithCurrentItem="True">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" Height="480" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
I am not sure if this is applicable or you are looking for this, however virtualizing the data is one more solution you can look into. there are some very good posts describing this. these 2 blog/post seems to be very good. Please have a look Here[http://www.codeproject.com/Articles/34405/WPF-Data-Virtualization] and here[http://www.zagstudio.com/blog/498#.U7a6OvldV1F].

Dynamical loading and displaying images collection in WPF

I'm using Telerik WPF controls but I'll appreciate any suggestions that might be helpful.
I have a GridView (RadGridView with RadGridView.RowDetailsTemplate actually, but it doesn't really matters). Also I have a collection of Images (It's actually a ICollection where items of this collection are relative Uris to the Images). Everything works fine if I have a fixed amount of Images in my collection, because I can create a fixed amount of Image controls in my View (XAML) and create a binding for each of these images in collection to each of my Image controls.
I want to create a mini-gallery. The question is how can I generate a non-fixed amount of Image controls to be shown in my GridView.RowDetails (or tell me if there is another way to display a list of images) during runtime. I'd like to know if there are few ways to do this (i.e. through binding in XAML and throught the Code.
Thank you in advance.
I will use CellTemplate to do this.
Basically, you will need a collection of ImageSource/Uri, which will be the ItemsSource of the DataGrid/RadGridView. This collection should implement INotifyCollectionChanged and INotifyPropertyChanged, e.g. ObservableCollection<T>. So you code behind or view model, you could build this collection dynamically, and bind the Source property of your Image in Xaml.
Sample Code
Note the sample code here are for MS DataGrid, but the Telerik RadGridView has the similar interface, so you will be able to easily modify you code.
In Xaml, you could have something like following:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="DataTemplate1">
<Image Source="{Binding}"/>
</DataTemplate>
</Grid.Resources>
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn CellTemplate="{StaticResource DataTemplate1}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
C# code will be like:
var items = new ObservableCollection<Uri>();
this.DataContext = items;
for (int i = 1; i < 4; i++)
{
items.Add(new Uri("youimage.jpg"));
}

Picking the right ItemsSource container

I have a UserControl I've written to display a few properties from a custom object. There are multiple instances of these objects so I have an ObservableCollection of them so I can set them as an ItemsSource binding to a ListView. Now I can get an instance of this UserControl show up for each instance of my class in my ListView.
The problem is I don't really want the behavior of a ListView. I don't want the user to be able to select the entire UserControl. In fact, the user should be able to select individual elements in the UserControl.
I thought about just using a StackPanel to put these UserControls in, but it doesn't have an ItemesSource property. Is there an easy way to make this happen?
Replace your ListView with an ItemsControl and set the ItemTemplate to a suitable DataTemplate for your objects. You can set the ItemsPanel if you want to change how the panel lays out the items.
<ItemsControl ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource ItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
See this example

Adding UI Components in C# WPF in Code behind

I'm new to C# and WPF format. In my program, I have an array of strings with text, and I want to create a button in the canvas for each string in the array. I've used flex, and in that i can use the addChild command to put something in something else, but I haven't figured out how to do it in WPF yet. Any help would be appreciated, thanks.
WPF is streams ahead with the ability to use Binding: you can bind an ItemsControl to your array directly, then tell WPF how to display each item with a Template and it will do it.
<!-- ItemsControl is a customisable way of creating a UI element for each item in a
collection. The ItemsSource property here means that the list of items will be selected
from the DataContext of the control: you need to set the DataContext of this control, or
the window it is on, or the UserControl it is in, to your array -->
<ItemsControl ItemsSource="{Binding}">
<!-- The Template property specifies how the whole control's container should look -->
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<ItemsPresenter/>
</ControlTemplate>
</ItemsControl.Template>
<!-- The ItemsPanel tells the ItemsControl what kind of panel to put all the items in; could be a StackPanel, as here; could also be a Canvas, Grid, WrapPanel, ... -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- The ItemTemplate property tells the ItemsControl what to output for each item in the collection. In this case, a Button -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- See here the Button is bound with a default Binding (no path): that means the Content be made equal to the item in the collection - the string - itself -->
<Button Content="{Binding}" Width="200" Height="50"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Hope that helps!
Reference:
http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.aspx
I hope this can get you started (untested!):
foreach ( var s in textArray )
{
Button b = new Button();
//set button width/height/text
...
myCanvas.AddChild( b );
// position button on canvas (set attached properties)
Canvas.SetLeft( b, ... ); // fill in the ellipses
Canvas.SetTop( b, ... );
}
A more advanced technique would let you sync the UI to the contents of your array.
I highly recommend the book "WPF Unleashed" to learn WPF. http://www.adamnathan.net/wpf/

Categories