How to build form at runtime in WPF-MVVM (PRISM) based application.
Requirement is like user should be able to add control like textbox, checkbox, combobox etc. at runtime.
after adding the crontol user will save the form and all the configuration will get saved in database.
So that application can create the form at runtime based on the configuration stored in database.
How can we achieve this?
Thanks.
I've done something similar, although not with standard UI controls. I have a series of classes representing the "controls" I want to display - in my scenario these represent physical devices like pumps, valves, switches, displayed on a machinery "control panel" that the user can configure. These classes inherit from a base class (call it "HardwareItem") which exposes some properties that are common to all controls, e.g. Top, Left, Width, Height, Tooltip, etc.
The "designer"
The window where the user "designs" a form consists of the following components:-
A "toolbox", basically an ItemsControl bound to a VM List<HardwareItem> property that exposes the available HardwareItems (created and populated by the VM's constructor)
A canvas, that the user can drag items onto from the toolbox. When a drop happens, I instantiate the appropriate HardwareItem object and add it to a collection (used to keep track of what controls have been added). To render the control on the canvas, I create a ContentControl and set its "Source" property to the HardwareItem object, then add that to the canvas at the drop position. The control's visual is rendered using XAML DataTemplates that I've created for each HardwareItem type.
A PropertyGrid control (part of the free Xceed toolkit). When the user selects a control on the canvas, the corresponding HardwareItem object is wired up to the PropertyGrid, allowing the user set its property values (I use a custom attribute to control which properties should appear in the grid).
When the user clicks "save", I basically just serialize my collection of HardwareItem objects to a string using Json.Net then saved to file.
"Runtime"
To render a previously designed form, the file is deserialized back into a collection of HardwareItem objects, and added to a canvas in pretty much the same way as described above.
Doing something similar with standard WPF controls shouldn't be too dissimilar. You could create classes that expose just the properties that you want a user to manipulate, e.g.:-
// Base class
public class MyControl
{
public double Top {get;set;}
public double Left {get;set;}
public double Width {get;set;}
public double Height {get;set;}
}
// TextBox
public class MyTextBox : MyControl
{
public string Text {get;set;}
}
// Button
public class MyButton : MyControl
{
public string Caption {get;set;}
public ICommand ClickCommand {get;set;}
}
The DataTemplates might look something like this:-
<DataTemplate DataType="{x:Type MyTextBox}">
<TextBox Text="{Binding Text}"
Width={Binding Width}" />
</DataTemplate>
<DataTemplate DataType="{x:Type MyButton}">
<TextBox Content="{Binding Caption}"
Width={Binding Width}"
Height={Binding Height}"
Command={Binding ClickCommand} />
</DataTemplate>
Much of the canvas manipulation is done in code-behind rather than the VM. It's "UI logic" so is a perfectly acceptable approach.
Prism is not used at all here (I use it for navigating between views, but it doesn't play a part in this "form designer" functionality).
Related
I'm developing WPF MVVM application and I want to create a Window with many panels that changes when user choose another panel from navigation.
I've read this article but it's not working due to Can't put a Page in a Style error. I can't find any answer about how to create a WPF application that navigate through different panels in one single window, how I can achieve what I want using MVVM pattern?
You can place various panels in a Grid, sharing the same space (overlapping) and change Visibility to make "Visible" only the one you want shown.
I've used this thecnique same time ago, and is compatible with MVVM too.
I have created an ContentPresenter and bind it to the MainWindow ViewModel and set the DataTemplate for each ViewModels.
<ContentPresenter Name="WindowContent" Content="{Binding CurrentPageViewModel}"/>
<Window.Resources>
<DataTemplate DataType="{x:Type viewModels:MainViewModel}">
<views:MainView />
</DataTemplate>
</Window.Resources>
And so when the binded property is changed, ContentPresenter display proper ViewModel and due to DataTemplate, the actual View.
public IPageViewModel CurrentPageViewModel
{
get
{
return _currentPageViewModel;
}
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentPageViewModel"));
}
}
}
private IPageViewModel _currentPageViewModel;
Every ViewModel implements simple IPageViewModel interface so only ViewModels could be set as content of ContentPresenter.
I am writing a terminal in WPF, which communicates the host with an embedded device through RS232.
I want to be able to open multiple connections that will reside on different tabs, I believe that for that purpose WPF's tabContorl is sufficient, However the customer wants to be able to tile the different tabs on the screen, and as I understand the basic tabControl doesn't allow you to do that.
Any ideas how can this be done?
any help will be appreciated,
Thanks in advance.
Maybe it's overkill, but I would give a try to Avalon Dock from the WPF Toolkit, it's free. With that, you will be able to move terminals around, dock them wherever you wish and even have only opened at a time if you unpin others.
I have made a custom WPF control called ModalContentPresenter that allows you to display modal content which I think will be suitable. The control allows you to conditionally display additional content on top of your primary content.
Your application window will comprise a single ModalContentPresenter which will contain your TabControl in it's primary content and an ItemsControl in it's modal content.
<c:ModalContentPresenter IsModal="{Binding IsTiling}">
<DockPanel>
<Button Content="Show tiled view"
Command={Binding ShowTiledViewCommand}
DockPanel.Dock="Top"/>
<TabControl ItemsSource="{Binding Connections}"> />
</DockPanel>
<c:ModalContentPresenter.ModalContent>
<DockPanel>
<Button Content="Hide tiled view"
Command={Binding HideTiledViewCommand}
DockPanel.Dock="Top"/>
<Itemscontrol ItemsSource="{Binding Connections}" />
</DockPanel>
</c:ModalContentPresenter.ModalContent>
</c:ModalContentPresenter>
By default the modal content will not be displayed so all the user will see is the TabControl.
Both the TabControl and ItemsControl are bound to the same collection of data in your viewModel ensuring that the tiled view is in sync with the items in the tab control.
Your viewModel must have a Boolean property called IsTiling which should return true when you want the 'tiled' view to be shown and false when it is hidden.
When the tiled view is displayed users will be unable to interact with the primary content.
You can change the layout panel used by the ItemsControl to change how the collection of data is 'tiled'.
See this answer for an additional example of how to use the control.
The interface of your viewModel will look something like this:
public interface ViewModel {
public ObservableCollection<Connection> Connections { get; }
public boolean IsTiling { get; }
public ICommand ShowTiledViewCommand { get; }
public ICommand HideTiledViewCommand { get; }
}
Once you have this working you can add some additional enhancements to improve the look and feel of the user interface.
I would start by assigning a custom control template to the TabControl so that a Button is displayed in the 'header' area. This Button is bound to a command in your viewModel which is responsible for changing the IsTiling property to true.
This question provides a link to an answer which explores ways of achieving this.
A second enhancement is to remove the button from the modal content and call the HideTiledViewCommand command when the user selects an item in the items control. You can then add some additional logic which selects the correct tab when the tiled view is closed. I think this can be achieved by having an additional property in your viewModel representing the selected connection.
This question is about how to factor the app design with MVVM in mind. I understand the general concepts of MVVM (ViewModel is not aware of the View, Models represent domain objects, etc...). I'm also aware of Commands and how some controls can invoke commands on the ViewModel.
What I can't figure out is how to factor a real-life app to fit this model (as opposed to simple textbox/button/query database examples). Here are some questions:
In a main view I have preview section that displays contextual data depending on what the user is doing (i.e. by interacting with certain controls, show some preview section). Should the View contain all the possible previews in XAML and then show/hide/update? Should ViewModel have properties like "public bool ShowPreviewA" and "public bool ShowPreviewB" set by ViewModel's internal state machine?
Where should the logic live when some controls have intricate interactions between them. For example, 3 checkboxes where at least one must be selected, by preventing unchecking. Seems to me that it would pollute the ViewModel on one hand, and also feel like there is certain "knowledge" about the View on the other hand.
This question is difficult to articulate properly (if I could, I would probably understand MVVM much better).
Any tips are welcomed.
EDIT:
The real question is how to break down the problem when writing the functionality of ViewModels. Is it a combination of top-down - i.e. encode every possible View state in ViewModel properties - and bottom-up - i.e. each logically related set of controls receive their properties from child ViewModels that report "up" some logical state (e.g. valid input)?
In a main view I have preview section that displays contextual data
depending on what the user is doing (i.e. by interacting with certain
controls, show some preview section). Should the View contain all the
possible previews in XAML and then show/hide/update? Should ViewModel
have properties like "public bool ShowPreviewA" and "public bool
ShowPreviewB" set by ViewModel's internal state machine?
No, of course not, if these "previews" are completely different UIs with completely different data, then use DataTemplates.
For example:
Given some classes:
public class Person: BusinessEntity //BusinessEntity is just a fictional base class for Model classes
{
public string LastName {get;set;}
}
public class Product: BusinessEntity
{
public string ProductName {get;set;
}
suppose your ViewModel is defined like this:
public class SomeViewModel: ViewModelBase //Same comment as above
{
public BusinessEntity SelectedEntity {get;set;} //NotifyPropertyChanged() etc
}
Your XAML can be defined like this:
<Window ...>
<Window.Resources>
<!-- DataTemplate for Person class -->
<DataTemplate DataType="Person">
<TextBox Text="{Binding LastName}"/>
</DataTemplate>
<!-- DataTemplate for Product class -->
<DataTemplate DataType="Product">
<TextBox Text="{Binding ProductName}"/>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding SelectedEntity}"/>
</Window>
WPF will take care of rendering the appropiate DataTemplate inside the ContentPresenter depending on which object type is put in the SelectedEntity property in the ViewModel.
Where should the logic live when some controls have intricate
interactions between them. For example, 3 checkboxes where at least
one must be selected, by preventing unchecking. Seems to me that it
would pollute the ViewModel on one hand, and also feel like there is
certain "knowledge" about the View on the other hand.
You can easily inherit ObservableCollection<T> to create this logic in a reusable manner. Then in your ViewModel just put some public SelectableCollection<T> MyItems {get;set;} where the selection / mutual exclusion, etc is handled by SelectableCollection and so on.
Bottom line: MVVM is all about reusability and encapsulation of functionality.
I speak about josh smith article.
can anyone show me please how the CustomerView.xaml specifically this:j
<TextBox
x:Name="firstNameTxt"
Grid.Row="2" Grid.Column="2"
Text="{Binding Path=FirstName, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{x:Null}"
/>
Why is there a Binding to FirstName which is public property in the CustomerViewModel.
There is a datacontext set for the MainViewModel, but not for the CustomerViewModel, so why does the binding work ???
Check out the ResourceDictionary in MainWindowResources.xaml. Josh uses the following code to describe what View should be used if an instance of CustomerViewModel is shown in the main window:
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<vw:CustomerView />
</DataTemplate>
We've described that when our DataType is of Type CustomerViewModel, we'll create a new instance of the CustomerView. WPF takes care of the DataContext and creation when it sees the CustomerViewModel type.
From the rest of the article:
Applying a View to a ViewModel
MainWindowViewModel indirectly adds
and removes Workspace ViewModel
objects to and from the main window's
Tab Control. By relying on data
binding, the Content property of a
TabItem receives a
ViewModelBase-derived object to
display. ViewModelBase is not a UI
element, so it has no inherent support
for rendering itself. By default, in
WPF a non-visual object is rendered by
displaying the results of a call to
its ToString method in a TextBlock.
That clearly is not what you need,
unless your users have a burning
desire to see the type name of our
ViewModel classes! You can easily tell
WPF how to render a ViewModel object
by using typed DataTemplates. A typed
DataTemplate does not have an x:Key
value assigned to it, but it does have
its DataType property set to an
instance of the Type class. If WPF
tries to render one of your ViewModel
objects, it will check to see if the
resource system has a typed
DataTemplate in scope whose DataType
is the same as (or a base class of)
the type of your ViewModel object. If
it finds one, it uses that template to
render the ViewModel object referenced
by the tab item's Content property.
The MainWindowResources.xaml file has
a Resource Dictionary. That dictionary
is added to the main window's resource
hierarchy, which means that the
resources it contains are in the
window's resource scope. When a tab
item's content is set to a ViewModel
object, a typed DataTemplate from this
dictionary supplies a view (that is, a
user control) to render it, as shown
in Figure 10.
The DataContext for the MainViewModel in App.xaml.cs serves as a starting point for our application.
Still picking my way through learning XAML/WPF. If someone can show me how to accomplish the following - it will go a long way to helping me develop my next project (and several similar projects down the road).
Say I have a collection of objects that defines objects to be drawn on a canvas. The objects contain all the information necessary to render the objects, including the shape, color, and location. Can a XAML control be created that binds to this collection and handles the rendering, or is this better done by drawing on the canvas in the code-behind?
One other point - the objects must eventually be click-selectable, selectable via rectangle-lasso, and draggable. This doesn't have to be solved in the example code someone supplies, but I thought it might be relevant to know this as it might affect the various implementations.
Example class below. thanks in advance.
Class DrawingElement
readonly property Shape as string ("circle", "square", "triangle")
readonly property Position as point (coordinates)
readonly property Color as string ("red", "blue", "yellow")
end class
Sub Main
dim lst as new List(of DrawingElement)
lst.add(new DrawingElement("Circle", 10,20, "Blue"))
lst.add(new DrawingElement("Square", 80,35, "Red"))
lst.add(new DrawingElement("Triangle", 210,120, "Yellow"))
<draw lst!>
End Sub
Can be done, but not by using magic strings (e.g., "circle") like in your example.
First, you should be designing your models based on existing framework elements rather than designing the model with the idea of whipping up some new UI elements or struggling to create code that interprets between them.
WPF already has an ellipse (circle), rectangle (square) and a whole host of other geometric primitives for you to use. You'll want to create models that contain public bindable properties that you can bind to instances of these elements to control their shape and location.
Without going into much detail (or testing), I'd do something like this
public class GeometricElement : INotifyPropertyChanged
{
// these are simplified and don't show INPC code
public double Left {get;set;}
public double Top {get;set;}
public Brush Fill {get;set;}
// ...
}
// ...
public class Square : GeometricElement
{
public double Width {get;set;}
public double Height {get;set;}
}
// ...
// bound to the window
public class CadAppDataContext
{
public ObservableCollection<GeometricElement> Elements {get; private set;}
}
And in my xaml, it would look something like
<ItemsControl ItemsSource="{Binding Source={StaticResource cadAppDataContext}}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate TargetType="{x:Type me:Square}">
<Rectangle
Canvas.Left="{Binding Left}"
Canvas.Top="{Binding Top}"
Width="{Binding Width}"
Height="{Binding Height}"
Fill="{Binding Fill}" />
</DataTemplate>
<!-- more DataTemplates for circle, triangle, etc etc -->
</ItemsControl.Resources>
</ItemsControl>
The ItemsControl will create a Canvas element and for every GeometricElement in my Elements collection it will add a new child UI element based on the type of object in Elements.
Items controls are bound to collections and can add or remove elements based on what's happening in the collection within your code. It determines the UI element to add by looking for a DataTemplate that is designed for a particular Type. This is a common, and important, pattern and is why using magic strings will hurt you in the long run; the magic string route won't let you leverage the power of the framework already built into WPF.
Now, I'm not saying this will work out of the box. You'll probably run into properties that won't bind without some heavy lifting. You might even have to extend the geometry primitives to get them to behave how you want. But this is the pattern used in WPF applications. Understanding the pattern and using it will help you avoid hours of stress and failure.