Bind StackPanel to method that adds child elements - c#

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.

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...

Dynamic Panel of Buttons WPF C# Common Use

I am trying to find a best or common practice for creating a table of buttons from a database list.
For my use I am creating more of a Point of Sale type screen. Where I want the categories to load as buttons on the entry screen. The buttons would have a simple task of showing a screen of more dynamically created buttons of the actual items. Those buttons would add the items to, lets call it, a ticket.
The solutions I found were few. I am also trying to code this so others can pick it up fairly quickly. I am extremely rusty and only code once in a while. So I try to follow common ways of doing it. I had some ideas but the code becomes hard to read, mostly because of me.
I saw the below link but was not sure if a ListBox was a good container for this.
Dynamic filling WrapPanel buttons from DB, setting the event handlers
I am sure a wrappenel is what I would have to use, but do I put it in a container or use it directly. Do I put it in Xaml or code it all, for issues like spacing between buttons? I think I am overthinking it and need to skip for a little bit.
Thank you,
It sounds like you want an ItemsControl, bound to your categories, with a WrapPanel as the ItemsPanel. The Button would go in the ItemTemplate.
<ItemsControl ItemsSource="{Binding CategoriesFromDatabase}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}"
Command="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl},Path=DataContext.AddToTicketCommand}"
CommandParameter="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here I assumed that your view model has properties "CategoriesFromDatabase" (an IEnumerable that you populate from the database), and an ICommand "AddtoTicketCommand" which takes the category as a parameter.

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

Best Control to add Multiple UserControl in WPF MVVM?

I have a MainWindow in which i want to add my others Views.
Users can open Mutliple number of different Views to the MainWindow.
So for adding all those Views(UserControls), which is the best control to use.
Current I am using Canvas, but its not supporting MVVM.
So how can i add multiple control.
This is what i have done till now
Note:
The control should host multiple UserControl by the same time i should be able to drag One UserControl here and there in that control and then on a click on UserControl should bring it to Front(Focused), which i did in Canvas using ZIndex.
Proper MVVM solution migh be to use ItemsControl class and bind collection of view-models as an ItemsSource.
In DataTemplate of that ItemsControl, I would specify proper view for child view-models (sort of tool-window in your case).
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:ToolWindow DataContext={Binding} />
</DataTemplate>
</ItemsControl.ItemTemplate>
To achieve window-like behavior as on your picture I would specify custom panel based on Canvas which would allow drag and drop behavior.
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<panels:MyCustomMdiPanel />
</ItemsPanelTemplate>
<ItemsControl.ItemsPanel>
I suppose that you have already working canvas solution.

Categories

Resources