Where to create UserControl for ContentControl - c#

In my application I have a window with several buttons on the top. By a click on one button a usercontrol is displayed in a contentcontrol under the buttons.
All buttons are bound to one Command in the ViewModel. The decission which usercontrol should be displayed is done by the commandparameter with an enum like:
<Button Content="Pupils" Margin="3" Height="30" Command="{Binding OpenSectionCommand}" CommandParameter="{x:Static local:SectionType.Section1}"/>
My question now is: Where shall I create the new Usercontrol and assign it to the ContentControl?
I had several ideas:
Bind the Content direct to the ViewModel and assign the new
UserControl there
Bind the Enum and use a converter to create the control

Since for each type of Content you have separate UserControls, i would suggest to use ContentTemplateSelector.
Create DataTemplates for multiple userControls you have and put them under window resources.
Have a ContentControl in your window and bind its Content to selected content property in ViewModel.
Create a ContentTemplateSelector and based on content selected return corresponding DataTemplate.
XAML:
<ContentControl Content="{Binding SelectedContent}"
ContentTemplateSelector="{StaticResource ContentSelector}"/>
Refer to the example here.
This way in future if you need to add another content, all you had to do is create a DataTemplate for it under resources and put the check in ContentSelector and you are good to go. (easily extensible).

Related

WPF: Change the user control programatically

I am new to WPF, I wanted to display a window with multiple user controls.
example.xaml
<DockPanel>
<Border x:Name ="TopRegion" DockPanel.Dock = "top">
<local:userControl1/>
</Border>
<Border x:Name ="leftRegion" DockPanel.Dock = "left">
<local:userControl2/>
</Border>
</DockPanel>
The usercontrol1 and usercontrol2 are other views in the same project. I.e usercontrol1.xaml and usercontrol2.xaml.
Problem is that i need to change the usercontrol of leftRegion from usercontrol2 to usercontrol3 during run time i.e programatically.
How to achieve this in example.xaml.cs program.
You already named the Border leftRegion, so you could use this Border to set a new child like
leftRegion.Child = new userControl3();
programatically in code behind.
This means you are replacing the instance of userControl2 of the Border with a new instance of userControl3.
While this is a legitimate request, it crosses with the logic of XAML and data binding, so I would suggest an alternative way that will have the same effect for end-user but is more in the spirit of XAML.
The solution is simple - just have both controls in your XAML and switch their Visibility based on whether you need one or another control to be displayed.

navigation/load different views on WPF/MVVM

I am quite new to WPF development, and currently I am trying to use the MVVM on my application development. I have read a lot about MVVM navigation and switching views, but I can't find a solution for my current situation. Let's explain what it is:
First of all, I have my main View element, a Dockpanel, with some fixed areas, and a main "dynamic" area where the content should change, depending on actions:
<DockPanel>
<Label Content="Top Fixed element"/>
<StackPanel Orientation="Vertical" Height="auto" Width="150" DockPanel.Dock="Left">
<Label Content="SomeOptions"/>
<!-- some more elements -->
</StackPanel>
<Label DockPanel.Dock="Bottom" Content="Foot"/>
<ContentControl Content="{Binding CurrentMainViewElementViewModel}"/>
</DockPanel>
I have defined some DataTemplates that I would like to load in this ContentControl, here there is one of the Data Templates as example:
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:FileLoaderVM}">
<View:FileLoaderView/>
</DataTemplate>
</Window.Resources>
This FileLoader (View and View Model are implemented, using the RelayCommand and the INotifyPropertyChanged) opens a dialog box after clicking a button, where after selecting a file it is opened and parsed, and show all the found elements inside a ListView with multiple selection(in this case, persons with their data).
What I want to do now is to load another user control in this ContentControl, when I click a button. This button is defined in my view model like this:
public ICommand LoadPersons
{
get { return new RelayCommand(param => this.loadSelectedPersons(), param => (SelectedPersons!=null && SelectedPersons.Any()));}
}
My question comes at this point, how can I modify the content of the ContentControl, loading another User Control instead of the current one directly from my view model (in this "this.loadSelectedPersons()")?
If this is not possible, how should I approach to solve this problem?
Next to this action, I want to show all the previously selected elements and manipulate in different possible ways (inserting in a DB, saving in another file and so on), and I have already for that the appropriate User Control, that I would like to show in my main view element in the ContentControl section, keeping the other elements as they are originally.
lets see if i get you right.
you have a mainviewmodel with a property (CurrentMainViewElementViewModel) bound to the ContentControl. your MainViewmodel set the FileLoaderVM to this Property. now you wanna show a "new/other" Viewmodel when a File is seleted in your FileLoaderVM?
why dont you simply expose a event from your FileLoaderVM and subscribe to this event in your MainViewModel? if you do so your MainViewModel can then set the "new/other" Viewmodel to the ContentControl
To change content of ContentControl you do not load another user control, but change value of CurrentMainViewElementViewModel (to which ContentControl.Content is bound) to a new ViewModel, which will load another UserControl (defined in DataTemplate same way as FileLoaderVM is).
This looks like a job for main ViewModel (where CurrentMainViewElementViewModel is located).
Easiest solution is to provide a method in that ViewModel
public Switch()
{
CurrentMainViewElementViewModel = SomeViewModel;
}
and call this method from FileLoaderVM.

WPF share common property between controls

i've two content controls how can i share a common property between them,
for example if i select some value from combobox in the first content control,
how can the second control know it
<telerikNavigation:RadTabItem Header="1">
<StackPanel>
<ContentControl Content="{Binding EGRPExtractViewModel.View}" />
</StackPanel>
</telerikNavigation:RadTabItem>
<telerikNavigation:RadTabItem Header="2">
<ContentControl Content="{Binding EGRPRightObjectViewModel.View}" />
</telerikNavigation:RadTabItem>
Thanks
You need to use two way binding then respond to the property changing in your ViewModel.
<ContentControl Content="{Binding EGRPRightObjectViewModel.View,Mode=TwoWay}" />
See the MSDN docs for how to respond to changed properties: http://msdn.microsoft.com/en-us/library/ms743695(v=vs.110).aspx
You do not bind view properties. You can bind control properties in same view, so one possibility for you will be to create a control which exposes bindable properties specifically for this reason.
When using mvvm normally view-model should provide all needed properties to the view. If it's a property from another view-model, then it is still have to be provided by view-model of this view (search for questions about how to pass data between view-models to example, here is one).

WPF and MVVM: bind UserControl to XAML dynamically

seems like a trivial task: i am building a wpf application, using MVVM pattern. what i want is dynamically change part of a view, using different UserControls, dependent on user input.
let's say, i have got 2 UserControls, one with a button, and another with a label.
in main view i have a container for that. following XAML "works":
<GroupBox Header="container" >
<local:UserControlButton />
</GroupBox>
and a UserControl element with buttons pops up. if i change it to another one, it works too.
question is how to feed that groupbox dynamically. if i put something like that in my model view:
private UserControl _myControl;
public UserControl MyControl
{
get
{
return _myControl;
}
set
{
_myControl= value;
InvokePropertyChanged("MyControl");
}
}
and change my view XAML to something like:
<GroupBox Header="container" >
<ItemsControl ItemsSource="{Binding MyControl}" />
</GroupBox>
and feed it from command with usercontrol for button or for label: nothing happens, although "MyControl" variable is set and is "invoke property changed"..
Obviously there are many ways to skin this particular cat - but to answer the question of why it doesn't work you need to look into the ItemsSource property of ItemsControl on MSDN.
The items control is designed to show multiple items, provided through an IEnumerable passed to the ItemsSource property. You are passing a UserControl, so the binding will fail.
For your example, I would change the ItemsControl to a ContentControl and bind the content to your MyControl property. This should then work.
<GroupBox Header="container" >
<ContentControl Content="{Binding MyControl}" />
</GroupBox>
However, I would strongly recommend looking into other ways of doing this - having a control in your VM breaks MVVM to my mind. Depending on what you are doing look at data templates - #Sheridan's link in the comments provides an great description of a way to do it.
Couldn't post this as a comment so adding as answer..
Have a look at this:
Implementing an own "Factory" for reusing Views in WPF
It uses DataTemplates but doesn't require the DataTemplate section for each view. If you potentially have a lot of user controls/views you wish to display or you are reusing through multiple views or you are intending to actually dynamically generate a view (versus just loading an existing user control) then this might suite your needs.

How to define the usercontrols in mvvm pattern?

I using from mvvm in my application. I want know how to define my user control in mvvm pattern.
Must I define it by using from mvvm, or I can define it generally?
Let's just call the control that embeds the user control MainWindow, and the user control UserControl. Since you are in MVVM pattern, you have at least one View Model for the outer view - I usually use the name MainVm.
You have two choices for the user control: They can share the same View Model, or you could have a sub view model, just for the UserControl, i.e. UserVm.
For your first choice, you do nothing. You define UserControl (Visual Studio 'add new item' -> User Control is a pretty good start). Then, you simply embed it in Main Window.
<Window
x:Class="SO.MainWindow"
...
xmlns:src="clr-namespace:SO"
...
>
...
<src:UserControl />
...
</Window>
UserControl will inherit the same DataContext from MainWindow, and do all the {Binding} as you would do in the MainWindow.
If you want to have a sub view model (UserVm) - it would typically be a public property of the MainVm (say, userVm). In that case, you'll set the DataContext of the UserControl when you reference it.
<src:UserControl DataContext="{Binding Path=userVm}" />
Another popular paradigm would be to declare the DataTemplate instead of the UserControl. If you do that, you just need to put the UserVm (either instantiate it in the XAML, or through binding):
<Window x:Class="MainWindow" ...>
<Window.Resources>
<DataTemplate x:Key="UserDt"> <!-- or user TargetType instead of x:Key -->
...
</DataTemplate>
</Window.Resources>
...
<!-- You can put in a ContentControl like here: -->
<ContentControl Content="{Binding Path=userVm}"
ContentTemplate="{StaticResource UserDt}" />
<!-- or, if you defined TargetType for the DT, you can simply instantiate
the sub VM here. I don't like this apporach but it exists. -->
<src:UserVm />
</Window>
I think that depends on the user control. The user control can be just a view, in which case you would compose a larger control or page which has this user control as part of the whole. The larger control or page would provide the view and the view model parts for this view.
Or you could create a self contained user control which has all of mvvm and use events to interact with the larger user control that it is a part of.
I suspect you'll get better reuse and modularisation with the second approach.
In short: it depends.

Categories