WPF Loading controls taking huge time - c#

I have a string array like
string[] strngData = new string[] {"12","11","23","34"};
This string array length may be up to 4000. I need create a textbox for each string and loading all the text boxes inside a stackpanel within scrollviewer.
As the number of strings increases and number of textboxes increases resulting, the time to render the controls to UI is taking more time. for displaying 4000+ strings it is taking around 18+ secs.
Is there a way to improve the rendering time?

n place of stackpanel you may use ListView (which has inbuild deferred UI loading) with custom ItemTemplate and ItemsPanelTemplate:
<ListView Name="x" ItemsSource="..." HorizontalContentAlignment="Stretch" VirtualizingPanel.IsVirtualizing="True">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel HorizontalAlignment="Stretch" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=/}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
There are some key moments: you have to use VirtualizingStackPanel in ItemsPanel template in place of usual StackPanel; TextBox.Text binding path have to be equal to /(working with current item); I also test the example for ObservaleCollection so using of List may disallow you to edit items position, count, etc.

Related

c# uwp virtualization issue

I have a grid view that is populated with lot of items...each item has a Image in it. And when user scrolls down, some items become unrealized by virtualization. And, when user scrolls back to show them, those items start to update data, image source. The problem is that updating image source blocks the UI, and items in my grid are not showing until the image is loaded. This looks bad, and very choppy scrolling...
Here is the example what virtualization is doing in gridview:
My question is, how to set number of items that virtualization will make realized?
Use the CacheLength property of the virtualizing panel.
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel CacheLength="0" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
If cache length is in multiples of view port size. So if 3 items are visible in the window and cache length is 1, then you'll have 3 realized items before and 3 realized items after, (3+3+3 = 9). If cache length is 2, then 6+6+3 = 15, etc.
This works with GridView as well:
<GridView>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid CacheLength="0" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>

Get an Image that is the child of a Panel

I have a panel and I added it in a Window as ItemsPanel of an ItemsControl
<Grid x:Name="outerGrid" >
<ItemsControl ItemsSource="{Binding ImageSourcesCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<view:CustomPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
As you can see in this panel I show a series of images. Everything works all right, but now I want to access those images from inside the CustomPanel to change the Source to one of them.
If I access them as elements of base.Children I obtain a ContentPresenter, i.e.:
var element = base.Children[i]; //<- this is a ContentPresenter
So my question is: how can I get the Image?
One solution, since I know the position of the image, would be to get the element at that position. But I would prefer something else because it is not really clean and if I have other images moving around it can be a source of troubles.
Thanks!
I found out that if you just want to change the source of the Image (and this was my case), you can change the property Content of the ContentPresenter. Not sure how to access the Image though...

Bind StackPanel to method that adds child elements

Here's my issue: I have a wrapper class that contains sets of lists that contain 15 images each. I want to bind a central StackPanel to a method that actually modifies the same StackPanel that was passed to it and adds child StackPanel elements that contain 15 images each.
To clarify:
I have a central StackPanel that has a vertical orientation. This StackPanel is located inside of a DataTemplate!.
<DataTemplate>
<Grid x:Name="ImageDisplayGrid" Height="861" Width="656">
<StackPanel x:Name="CentralImagePanel" HorizontalAlignment="Left" Height="841" Margin="10,10,0,0" VerticalAlignment="Top" Width="636"/>
</Grid>
</DataTemplate>
I have many instances of my wrapper class that contain up to 15 images each (as WritableBitmap objects.
I want to bind my central StackPanel to some method that will modify that StackPanel, iterating through my list of wrapper classes and adding child StackPanel controls to the central StackPanel for each instance of my wrapper class found.
For each instance of the (ImageSet1, ImageSet2, etc for example) wrapper class, the new StackPanel that will be added to the central StackPanel will be populated using the images contained in that wrapper class instance.
In my mind there isn't really anything to be 'returned' here, so I was hoping there was a way to just pass the control (the central StackPanel) to some method, let the method modify it, and then carry on after the central StackPanel is populated with its child `StackPanels'.
To clarify even more:
Think of NetFlix. You know how you can scroll vertically through each category and each category allows you to scroll horizontally? Thats what I am trying to emulate, only I want it to be dynamic and bound to my wrapper class that contains a list of Images to use.
My main obstacle right now is that the central StackPanel is located within a DataTemplate, so there isn't an easy way to access it during runtime. On top of that it would be nicer to use a binding anyway.
I have tried to use IValueConverter to turn my wrapper class into a list of StackPanel objects that the central StackPanel can use, but that didn't work. I've also searched for ways to bind a control to a method that has no return property without any luck as well.
Any help or examples would be greatly appreciated.
You are thinking about this wrong. Really, really, wrong. StackPanel is a layout control. You shouldn't ever be directly modifying its children or any other properties.
As you've noticed, there is no real way to do this task in the way you describe.
To display collections, use an ItemsControl. In your case, it would be something like:
<ItemsControl ItemsSource="{Binding Categories}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource={"Binding Videos"}>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Whatever -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Notice the inner template is another ItemsControl, this time with a horizontal StackPanel as the panel template.

How to fill ItemsControl using an integer

I am trying to create a kind of progress bar control in WPF. It will be ellipses that fill in as each step completes. However, I am failing at coming up what I would consider a clean example (minimal code behind and waste). Here is what I have:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Fill="Blue" Stretch="UniformToFill"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Then, I want to have two dependency properties:
TotalSteps
StepsCompleted
What I want is when TotalSteps = 14 then there will be 14 ellipses and the fill would work off of a trigger using StepsCompleted, a math converter and the ellipse's index. However, I haven't been able to figure out a way to do this unless I create a dummy list that is created with blank objects that will act as my DataContext...but, that seems like a waste. Is there a way to fill the template based off of the number, and not need a backing List?
Alternatively, I would accept a solution that would be an override of the progress bar template. However, I found nothing, so the user control seemed the easiest

Why is the ListView only showing 12 items?

I took the standard Item GridView template and modified it a bit to fit my needs. I actually have changed very little of the template code.
I have a single group, and I have a lot of items in it (92 items). The listview does render some of them, but it only renders 12 of them. Why is that? How can I override that and make it display all of the items?
Here's a screenshot of me broken into the debugger as I'm setting the DefaultViewModel:
I add items to my listview like so (as I parse XML from a service):
DataSource.AddItem(new DataItem(... title, name, etc, DataSource.getGroup("gallery")));
Then in my DataSource class (this is exactly the same one as the sample, I just renamed it), I added this method:
public static void AddItem(DataItem item)
{
item.Group.Items.Add(item);
}
Here's what the XAML that renders this looks like (it's the same as the GridView template:
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="1,0,0,6">
<Button
AutomationProperties.Name="Group Title"
Content="{Binding Title}"
Click="Header_Click"
Style="{StaticResource TextButtonStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
I'd really appreciate any help.
Grid application template limits amount of items displayed in each group to 12 for reasons explained in the comment below:
public class SampleDataGroup : SampleDataCommon
{
...
public IEnumerable<SampleDataItem> TopItems
{
// Provides a subset of the full items collection to bind to from a GroupedItemsPage
// for two reasons: GridView will not virtualize large items collections, and it
// improves the user experience when browsing through groups with large numbers of
// items.
//
// A maximum of 12 items are displayed because it results in filled grid columns
// whether there are 1, 2, 3, 4, or 6 rows displayed
get { return this._items.Take(12); }
}
}

Categories

Resources