How to fill ItemsControl using an integer - c#

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

Related

Improve performance in WPF with many UIElements

I'm very new in WPF and I need some advise on how to create a view with many UIElements at the same time. What i want to do, is to view some kind of table with fixed width and height for each cell. In each cell could be a random number of textblocks with a different back and forecolor (see image).
Image for table-like view with many UiElements
That is what I have done so far... I created an usercontrol for the content of one cell which binds the items in an itemcontrol.
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="{Binding Backcolor, Converter={StaticResource IntegerToBrushConverter}}" >
<Viewbox MaxHeight="20" >
<TextBlock TextWrapping="Wrap" Text="{Binding Caption}"
Foreground="{Binding Forecolor, Converter={StaticResource IntegerToBrushConverter}}"/>
</Viewbox>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This usercontrol is also binded to an itemcontrol for representing the x-axis of my table.
<ItemsControl ItemsSource="{Binding}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:DayView DataContext="{Binding Days}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And the same is done for the y-axis with an nice scrollviewer around. So, the problem is, that there are many many elements in the virtual tree. In my sample case there are over 60.000 elements listed in the tree. This results in a bad performance, when opening the view. The scrolling performance is quite okay, but he takes several seconds to open the view.
I've tried some things like the CacheMode and so on, but all of that doesnt effect the opening performance. Using a VirtualizingStackPanel results in bad scrolling performance. I even cant figure out what takes so long to build up the Ui. Seems that he need a lot of time to measure all UiElemtents, but Im not sure...
Are there tips to make such an UiElement rich view any faster? As I said, Im very new in WPF and that is only a performance test. We have such an Ui in Winforms, but there, the whole table was user painted. In WPF it is so easy to rebuild the Winforms design with stock content-controls that I only want to draw all for myself as a last resort.
Take a look here for a possible solution to your problem by using virtualization:
WPF Data Virtualization
Also if you want to go another way, there are commercial 3rd party WPF control libraries that handle virtualization with various degrees of success. To name a few: Xceed, Syncfusion, DevExpress and Telerik.
I even cant figure out what takes so long to build up the Ui.
Hoping you are using VS2015 (or something better), there are a few ways to get an answer to what is taking so long. Visual Studio has a live visual tree tool (Debug->Windows) where you can see the visual tree in real time and it might help you find and eliminate some of the UI elements you don't really need (like borders inside more borders, etc)
Second, you can run the Diagnostic tools (Alt+F2), choose CPU usage and that should generate a report where you can see where your program is spending the most time. Hopefully this will isolate the issue to certain methods that can be optimized afterwards.
Also, more detail about your problem would be welcome. What is the code that populates the ItemsSource do exactly?

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.

WPF Loading controls taking huge time

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.

Calendar Programming: How can I place, create and size items that represent a task using MVVM?

I just have a conceptual question here. First of all I am programming a Windows 8.1 App using C# and XAML as well as the MVVM-Light Toolkit.
I am working on a Schedule at the moment which is backed by a calendar. The calendar should have a "DayView" which consists of a timeline on the left that goes from top to bottom. Now on the right of that timeline I want to show something similar to a rectangle as a visualization for a given task with a height that represents its duration. Every task has a starting time so the top of that rectangle should be placed next to the corresponding entry in the timeline.
Well I hope it is clear what I am trying to say here basically that should look similar to the Calendar-App coming with Windows 8. I will post a screenshot of that below that post.
Now I have tried or thought of multiple things by now that lead to more or less complicated problems. The best approach I had was creating a List a placing it on the right of that timeline. Now that List would contain a huge amount of items (maybe one for each pixel) which will be changed in size. The size then is 0 or in relation to the duration of the task depending on whether there is a one or not.
But that leads to huge problems when it comes to changing an entry or having more than one task for the same time since those items wont overlap. And I don't want to place more than one or two of those per page since the performance-issue would be overkill...
One of the issues I have is that I am using the MVVM-Concept and don't know how to be able to create new UI-Elements such as a rectangle without touching the code-behind. That is why basically the only thing I have thought about by now were lists...
I hope it is clear what I meant.
Thank you very much for your help!
FunkyPeanut
Problem solved using a Grid as the ItemsPanel of an ItemsControl:
<ItemsControl Grid.Column="2" ItemsSource="{Binding Day.ItemsList, Source={StaticResource Locator}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Width="20"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Height="{Binding Height}" Width="20" Margin="{Binding Margin}">
<Grid.Background>
<SolidColorBrush Opacity="{Binding Opacity}" Color="{Binding ColorHash, Converter={StaticResource HexToColorConverter}}"/>
</Grid.Background>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
With the following Data:
ItemsList = new ObservableCollection<object>(tmpPeriodsList.Select((x, i) => new
{
ColorHash = x.ColorHash,
Index = i,
Margin = StartPosition(i),
Opacity = 0.6,
Height = 45,
}));

Categories

Resources