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.
Related
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).
I have searched and tried for days and finally must ask the question here.
I have a Silverlight 5 application, Using MVVM Light, where I want to be able to dynamically switch views in the main view.
For the sake of simplicity, lets say I have 2 buttons.
Button1 will switch to TestView1.
Button2 will switch to TestView2.
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
The way I have done it is by binding a relaycommand to the button and then instanciating a new viewmodel of the corresponding view.
ie:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = ViewModelLocator.NinjectKernel.Get<TestViewModel1>();
}));
}
}
The CurrentViewmodel is then set to the new viewmodel.
In the MainView I have bound the CurrentView to a ContentControl:
<Border x:Name="displayedView" Grid.Row="2">
<ContentControl Content="{Binding CurrentView}" />
</Border>
This will actually work to some extend, since the CurrentView will change but instead of actually showing the content of the view it simply shows the Namespace of the ViewModel that is instanciated.
So far I have primarily used the knowledge taken from these sources:
http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/
Loading Views into ContentControl and changing their properties by clicking buttons
but they do not solve my problem, or I do not quite understand how to actually show the views.:-(
So does anyone have a good explanation on how to switch the views correct in Silverlight 5 using MVVM Light from GalaSoft.
Thanks
The part you are missing is the DataTemplates that tell WPF how to render your ViewModels
<Window.Resources>
<DataTemplate TargetType="{x:Type local:TestViewModel1}">
<local:TestView1 />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TestViewModel2}">
<local:TestView2 />
</DataTemplate>
</Window.Resources>
When you insert an object in the Visual Tree, such as placing a ViewModel object in ContentControl.Content, it will get drawn by default using a TextBlock bound to the .ToString() of the object, which is why you are only seeing the namespace.classname of the ViewModel in your ContentControl
By defining an implicit DataTemplate in your Resources somewhere (that's a DataTemplate with only a TargetType defined - no x:Key), you are telling WPF to draw the specified object using the specified DataTemplate anytime it tries to draw that object, instead of using the default TextBlock bound to the .ToString() of the object.
It should be noted that implicit DataTemplates are not supported in earlier versions of Silverlight, however they are supported in 5.0+. For earlier versions of Silverlight, I usually use a DataTemplateSelector instead.
Id first suggest that you do not display your views via a ContentControl but look into using the navigation Frame in the silverlight toolkit. Also, we dont want our ViewModel creating Views... that'd not be so good. We don't mind, however, if our ViewModel does business logic and DETERMINES which view to show. Get the toolkit here: http://silverlight.codeplex.com/
Now setup your XAML as so in your main page:
<Border x:Name="displayedView" Grid.Row="2">
<navigation:Frame x:Name="ContentFrame" />
</Border>
Since you are using MVVM Light, we will use messaging. Your View model will get the command to change views, determine which view to change, then send a message to the main page to instruct it to change views.
Setup a listener in your main page for a navigate request as so:
public MainPage()
{
InitializeComponent();
Messenger.Default.Register<Uri>(this, "NavigationRequest", (uri) => ContentFrame.Navigate(uri));
}
Next, setup your command in your view model.
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
Messenger.Default.Send<Uri>(new Uri("/Views/.../Page.xaml", UriKind.Relative), "NavigationRequest");
}));
}
}
These are the basics that work for me. You can expand on this and get real "architecty". For example, you can create a base class for you view models that sends the navigation requests, create a helper class that generates URIs (so they are not hard coded everywhere in your app, etc etc. Good luck!
So i actually solved this problem, in a way where there is no need to create datatemplates in the MainView, which i did not like. imo the MainView should know nothing about the views it is displaying, when we are talking about switching the views.
Prerequisite: You must use MVVM Light from GalaSoft for this solution
This is my test solution:
Two buttons are added to my MainView, Each button will open a new view. The clickevent are bound to Commands.
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
In the MainView i have a Border that should contain the views than can switch.
Since all views inherit from UserControl i bind the content to the property CurrentView of the MainViewModel
<Border x:Name="displayedView" Grid.Row="2">
<UserControl Content="{Binding CurrentView}" />
</Border>
In the MainViewModel i have the property CurrentView.
public const string CurrentViewPropertyName = "CurrentView";
private UserControl _currentView;
/// <summary>
/// Sets and gets the "CurrentView property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (_currentView == value)
{
return;
}
RaisePropertyChanging(CurrentViewPropertyName);
_currentView = value;
RaisePropertyChanged(CurrentViewPropertyName);
}
}
When a button is clicked the corresponding Command is called in the MainViewModel:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = new TestView1();
}));
}
}
private RelayCommand _callTestView2Command;
public RelayCommand CallTestView2Command
{
get
{
return _callTestView2Command ??
(_callTestView2Command = new RelayCommand(() =>
{
CurrentView = new TestView2();
}));
}
}
As seen each command will set CurrentView to a new view, and the views will switch in the MainView, because CurrentView will raise a ProperTyChanged Event.
This will actually work to some extend, since the CurrentView will
change but instead of actually showing the content of the view it
simply shows the Namespace of the ViewModel that is instanciated.
Because you are changing the CurrentView property to a viewmodel instance and bind that as the Content. This is wrong as the Content should be a view and you should set the DataContext of that view to a viewmodel.
The simplest thing you can do here is to create a View instance inside the command and set the viewmodel as its DataContext and then you can set the view to the CurrentView property. Of course this would violate the MVVM pattern so you should move this responsibility to a separate component. Instead of writing your own navigating logic I suggest you to pick up an existing solution as this kind of task is not as straightforward as it seems.
I suggest to use the Prism library
I am working on a dynamic search view wherein clicking a button should add a new row containing 3 combobox and 2 textboxes.
How should I go about doing this?
If you really want to do mvvm , try to forget "how can I add controls". You don't have to, just think about your viewmodels - WPF create the contols for you :)
In your case lets say we have a SearchViewModel and a SearchEntryViewmodel.
public class SearchEntryViewmodel
{
//Properties for Binding to Combobox and Textbox goes here
}
public class SearchViewModel
{
public ObservableCollection<SearchEntryViewmodel> MySearchItems {get;set;}
public ICommand AddSearchItem {get;}
}
Till now you dont have to think about usercontrols/view. In your SearchView you create an ItemsControl and bind the ItemsSource to MySearchItems.
<ItemsControl ItemsSource="{Binding MySearchItems}"/>
You see now all of your SearchEntryViewmodels in the ItemsControl(just the ToString() atm).
To fit your requirements to show every SearchEntryViewmodel with 3Comboboxes and so on you just have to define a DataTemplate in your Resources
<DataTemplate DataType="{x:Type local:SearchEntryViewmodel}">
<StackPanel Orientation="Horizontal">
<Combobox ItemsSource="{Binding MyPropertyInSearchEntryViewmodel}"/>
<!-- the other controls with bindings -->
</StackPanel>
</DataTemplate>
That's all :) and you never have to think about "how can I add controls dynamically?". You just have to add new SearchEntryViewmodel to your collection.
This approach is called Viewmodel First and I think it's the easiest way to do MVVM.
One option is that you can create TextBoxes and comboboxes in backend by creating a new instanse.
But the better option is that you can create one usercontrol which contains All texboxes and comboboxes which you want to add and in which format you want.
After creating when the button is pressed you can create a instace of this usercontrol and set it in the grid or any other control by using SetValue property of the control.
If you are new to WPF and MVVM this read this blogs to understand this.
https://radhikakhacharia.wordpress.com/2012/06/01/wpf-tutorial-3/
https://radhikakhacharia.wordpress.com/2012/02/13/model-view-viewmodel/
If you are new to both MVVM and WPF, there is a really wonderful video tutorial on how to
architect a C# / WPF / MVVM application by Jason Dollinger which is available here on lab49. All of the sourcecode he developes in this amazing video is available also right here on lab49.
After watching it, you will not have any problems developing your search view for sure.
I'm new to WPF but have created a window with a wrap panel that contains a collection of user control instances dynamically added from code behind. Each user control will ultimately display data from a row returned from a database call. I'd like to make this follow MVVM but am a little stuck on the architecture. I think I need to have a view model for the user control and a view model for the window that would possess an observablecollection of the user control view models. How do I get that bound to a wrap panel on the view side so that the wrap panel sees the collection of user control view models and knows to establish a user control for each instance in the collection?
I think once this is all bound properly I can make a background worker that at a regular interval queries the database and creates / updates the user control view model objects and if I am inheriting from INotifyPropertyChanged and firing property changed events in my user control view model everything should update based on the binding. Does that sound correct?
I've seen basic examples such as an observablecollection of strings bound to a list box but I'm having trouble applying this to a more complex case. Any suggestions as to a general architecture or where I should look to get started is much appreciated!
Basically, you need an enumerable control that has one item for each element in your ObservableCollection. The control's items will need to be templated to display the data using your custom control
To do this create an ObservableCollection which holds your data objects and use it as the ItemsSource for a ListBox. The ListBox will then need to be changed to display its items in a WrapPanel instead of the default layout. Modify the ItemTemplate of the ListBox to use your custom user control for each list item.
ViewModel:
public class WindowViewModel
{
public ObservableCollection<MyDatabaseObject> DatabaseObjects { get; set; }
}
public class MyDatabaseObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string DbName
{
get { return _dbName; }
set {
_dbName = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("dbName");
}
}
private _dbName;
}
Xaml:
<Grid>
<ListBox ItemsSource="{Binding DatabaseObjects}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<MyUserControl Title="{Binding DbName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Are you looking for the ItemsControl class? With ItemsControl.ItemTemplate set to a DataTemplate for your ItemUserControlViewModel (-> item user control view). And ItemsControl.ItemsPanel set to anItemsPanelTemplate with a WrapPanel.
ItemControl's ItemsSource property would be bound to your ObservableCollection<ItemUserControlViewModel> from WindowViewModel.
I have one query related to designing WPF using MVVM
Here is the scenario :
1> I have one WPF screen which contains various user controls which are reusable in some other screens too.
2> Can i have separate ViewModel class for each of those user controls , what could be ideal design in this scenario
3> Should i separate my Viewmodel based on individual screen or on UserControls .
4> If i create separate viewmodels based on UserControls how i should integrate it .
Is there any design guidelines around this !!
Urgent Help appreciated ..
This post describes what I do in certain scenario, I don't know if it is a best practice or not but it works for me.
I create ViewModel for my Window that holds all the user controls, so this called ContainerViewModel and I create an instance of that Viewmodel and put it in the DataContext of the Window. From that moment all the UserControls can access that ViewModel with Binding.
The next thing to do is to create a property on my ContainerViewModel for everty UserControl that holds the ViewModel for each UserControl.
Then use binding to attach the usercontrols ViewModel to the DataContext property of the Usercontrol.
example of the viewmodels and a window with 2 listboxes instead of usercontrols:
Viewmodel classes without any implementation but just empty classes to show the concept:
public class ContainerViewModel
{
public ContainerViewModel()
{
ViewModelForControl1 = new Control1ViewModel();
ViewModelForControl2 = new Control2ViewModel();
}
public Control1ViewModel ViewModelForControl1 { get; set; }
public Control2ViewModel ViewModelForControl2 { get; set; }
}
public class Control1ViewModel { }
public class Control2ViewModel { }
Window xaml:
<Window x:Class="ConfigHellp.UI.Windows.ContainerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:ConfigHellp.UI.ViewModel"
mc:Ignorable="d"
DataContext="{DynamicResource ContainerViewModel}" >
<Window.Resources>
<vm:ContainerViewModel x:Key="ContainerViewModel" d:IsDataSource="True" />
</Window.Resources>
<StackPanel>
<ListBox DataContext="{Binding ViewModelForControl1}" />
<ListBox DataContext="{Binding ViewModelForControl2}" />
</StackPanel>
</Window>
this depends on how complex the embedding of the UserControl into the environment is. If you think that its to much effort to build the view model logic for your user control again and again (which is also a very nice source for mistakes), you should infact encapsulate the logic in a single viewmodel for your control. If the user control will be an ListItem for example, i generally suggest you to build an own viewmodel for the control.
The infrastructure will be than:
A general viewmodel for your WPF screen, which holds instances of the viewmodels for your usercontrols. That DataContext of the screen will be the general viewmodel. The users controls's DataContext will be a Binding to the PropertyPath of the user control viewmodel in your general viewmodel. e.g:
In WPF Screen:
<ListBox DataContext="{Binding}" ItemsSource="{Binding Path=ItemList}">
<ListBox.ItemTemplate>
<yourControls:YourUserControl />
</ListBox.ItemTemplate>
</ListBox>
In the general viewmodel:
public class ScreenViewModel : INotifyPropertyChanged
{
private ObservableCollection<YourUserControlViewModel> _itemList =
new ObservableCollection<YourUserControlViewModel>();
public ObservableCollection<YourUserControlViewModel> ItemList
{
get { return _itemList; }
set { _itemList = value; }
}
}
This will automatically generate a your user control for each viewmodel in the ItemList of your general view model.