Dynamic WPF form creation approaches - c#

I'm about to create a dynamic WPF UI form from DataTable data. The screens would be fairly complex. They would contain textboxes, groupboxes, checkboxes, buttons, datagrids etc. Some of them visible, some hooked up event handlers and thing like that.
What approach of creating those dynamic screens would you choose considering performance impact and complexity requirements to write and maintain source code. Please note that this code will run a LOT so it must be efficient and blazing fast. I'm considering these options:
Create Controls in code, assemble them to a tree and use the tree (Grid control) as a root element for a WPF form.
1.a) Create a XAML via XAMLReader from that screen object tree and Load it via XAMLReader inside WPF Form. Creating XAML would seem redundant to me since I can use the built tree as a Content for WPF form directly.
Use XMLDocument class to create tags, obejcts and their atributes. Create a XAMLlike that and then load that XAML in WPF form.
Thanks,
Michal

Consider displaying your form in a listview and creating a DataTemplate for each of your form fields textboxes, groupboxes, checkboxes, buttons, datagrids etc.
<ListBox ItemsSource="{Binding DataFormFields}"
<DataTemplate DataType="YourTextClass">
<StackPanel>
<TextBlock Text="{Binding LabelText}" />
<TextBox Text="{Binding ValueText}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="YourCheckClass">
<StackPanel>
<CheckBox Content="{Binding LabelText}"
IsChecked="{Binding Checked}"/>
</StackPanel>
</DataTemplate>
For more on DataTemplates see
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/data-templating-overview
Each data template should be associated with a one of your form fields classes, using the DataType attribute, this will cause the listbox to automatically use the correct DataTemplate.
For more details:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.datatemplate.datatype?view=netframework-4.7.2

Related

WPF - MVVM DataTemplate load to memory for reuse

I have a window which has several different DataTemplate that are load to a ContentControl based on a RadioButton (The RadioButton sends a command to the ModelView which sets the Content property of the ContentControl.
It works well, but now several views contain a "heavy" object (Eyeshot CAD viewer).
Switching to any of these view causes a delay (at this moment there's absolutely zero logic in the whole software other than the view/view model)
Is there a way to load the view and the heavy control to memory once and then reuse it when switching to its view? (The ViewModel of that view is currently a singleton but that doesn't help)
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Height="160" Margin="0,0,0,12">
... Removed for clarity
</StackPanel>
<ContentControl x:Name="Tabs" Content="{Binding SelectedTabViewModel}" Margin="0,12,0,12"/>
</DockPanel>
On your DataTemplate, you can set the attribute x:Shared="True", this will allow the framework to reuse the visual control (inside the datatemplate) for another ContentPresenter.
This doesn't load the component at starting, but, this reuse it once instantiated.

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 does a WPF window know how to access a nested element?

I have the following working XAML code:
<Window x:Class="DrawShape.Window1"
...
<Grid>
<Polygon Name="poly"/>
</Grid>
</Window>
In the corresponding C# code, a static callback method (for a property called Sides) accesses the poly element as follows:
static void OnSidesChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
Window1 win = obj as Window1;
win.poly.Points.Clear();
...
How is it that poly is accessed directly through Window1 win? poly is nested within a Grid element (albeit nameless). Is this type of access a feature of WPF?
PS: I am aware about the need for access through an object (because the method is static), it is the nesting that I don't understand.
You are confusing the WPF logical tree with how names are handled in XAML. In the logical tree the Polygon is contained in the Grid. However, all names belong to the same scope and are available as fields in the class generated from the XAML.
However, WPF has the concept of Namescopes which makes it possible to use the same name in multiple scopes.
Styles and templates in WPF provide the ability to reuse and reapply content in a straightforward way. However, styles and templates might also include elements with XAML names defined at the template level. That same template might be used multiple times in a page. For this reason, styles and templates both define their own XAML namescopes, independent of whatever location in an object tree where the style or template is applied.
In the simple XAML below you have a Grid named grid containing a ListBox named listBox. In the class generated from the XAML there are fields named grid and listBox allowing the code behind to access both controls.
Each list box item generated by the ItemTemplate contains a TextBlock named textBlock. However, each list box item is in a separate Namescope and there is no field named textBlock in the class generated from the XAML.
<Grid x:Name="grid">
<ListBox x:Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="textBlock" Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
In this simple example there is no need to name the TextBlock objects. However, in more advanced scenarios you may want to refer to named elements within the template, e.g. in triggers.
Locate the file Window1.g.cs in your project directory.
Window1.g.cs contains a partial class that was generated from your XAML. In there you find the variable definition for poly.

C# wpf - Adding to xaml file during execution

This is what my main program GUI will look like, what I am attempting to do, is to create a reminder application in C# using wpf.
I am going to use a scroll viewer which is going to be displaying the data to the user, namely the reminders they currently have.
Each time the user adds a reminder, it will add this:
What I am wanting to do is that, when ever the user add's a new reminder, there will be a new set of data added to the scrollviewer.
What would be the best way of achieving this?
Am I able to hold the xaml data and add it during execution?
Thanks for the help
What you want to do can be accomplished not by dynamic Xaml, but by the use of a templated control which can accept dynamic data. For example you wouldn't consider using a listbox for your labels because you are not showing the data in a list right?
But a listbox is just a conveyor belt for what you want to achive. Say you want more than a label, how about three labels. Via binding to a proper structure you can get what is needed.
Here is an example
<ListBox ItemsSource="{Binding myReminders }">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=ReminderName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Once that is bound to a list of my reminder data objects (which the list can dynamically change), we have the ability to show the reminders in any format we want. One just uses the above to style it appropriately. (Check out WPF's Templating Overview for a great example).
The use of templates is done in other controls, so if the listbox is not to your liking, look into other templated controls.

How do build a VS-like propertypanel in WPF?

I got a TreeView containing different objects from different classes. Now I want to build a propertypanel, which shows up different content, depenting on what object/class is selected in the TreeView. What is the best way to build such panel? Differnt panels and collapsing panels depending on the selection(Whould make implementing the ObserverPattern this easier for me?)? Or an other approach?
I would bind the property panel (which could be just a ContentControl) to the SelectedItem in the TreeView:
<ContentPanel Content="{Binding SelectedItem, ElementName=_treeView}"/>
Then I would use DataTemplates to show the correct panel for each class of item you have:
<DataTemplate DataType="{x:Type local:SomeClass}">
<Label>This is displayed for SomeClass</Label>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SomeOtherClass}">
<Label>This is displayed for SomeOtherClass</Label>
</DataTemplate>
Obviously your DataTemplates can be as complex as needed to display the various classes present in the TreeView.
Do you mean a property grid?

Categories

Resources