I have a view PeopleView that displays a list of People bound to a collection of People in PeopleViewModel.
In a panel within PeopleView I have a form (just a collection of text boxes and a button) which allows users to add a new person (People).
Initially I had this collection of controls as part of PeopleView but I want to separate it out into a separate UserControl (AddPerson) as it doesn't necessarily depend on the existing list of people and this AddPerson control could exist anywhere else in the application. I might want to reuse it elsewhere.
However, I'm struggling to work out where I should now be putting my button command and the associated CanExecute logic now.
It would have been in the PeopleViewModel but my AddPerson UserControl doesn't have a ViewModel. I've read that typically you wouldn't have a ViewModel for a UserControl, but I don't know how else I can make a reusable control that also contains its own business logic?
Should my AddPerson UserControl have a ViewModel, or should I be doing the business logic in the codebehind (seems very unlikely), or something else?
Commands traditionally go in the ViewModel but there's no hard fast rule on this. If you'd like to make a more reusable control, you can create a command dependency property on the control itself and then bind it to a command in your view model when the control is implemented.
This is identical to how button command is implemented
For example
MyUserControl.Xaml
<UserControl x:name="self">
<Grid>
<Button Command="{Binding ElementName=self, Path=MyDPCommand}"/>
</Grid>
</UserControl>
MyUserControl.Xaml.cs
public ICommand MyDPCommand
{
get { return (ICommand)GetValue(MyDPCommandProperty); }
set { SetValue(MyDPCommandProperty, value); }
}
public static readonly DependencyProperty MyDPCommandProperty =
DependencyProperty.Register(nameof(MyDPCommand), typeof(ICommand), typeof(MyUserControl), new PropertyMetadata(null));
And then in your implementation xaml you can bind it back to your viewmodel
MyView.Xaml
<MyUserControl MyDpCommand="{Binding MyViewModelCommand}"/>
In the end I've gone the route of implementing a ViewModel for my AddPerson view because, although it is technically a UserControl, I really am using it like any other view rather than a generic control.
Related
Basically, I'm not sure how to use MVVM, and/or use commands correctly in my current situation. So, I have a View, containing a list box, and a panel of animation objects, that I created. These animation objects can be animated through a simple public method, Animate(). The goal here, is to associate this Animate() method with buttons inside the list box, like so:
As we can see in the diagram below, both the ListBox items and the visual elements inside of the animation area are associated with the same collection of models from the ViewModel, with the items in each being templated. For example, the ListBox items are simply defined to have some text related to a data item, and the AnimationObjects take on an appearance according to the data. These models, I feel, should not understand that an animation is occurring - they're simple data, and the animation does not change them.
Finally, I show in the below diagram, that I have created two FrameworkElement child types, one for holding animation objects, and another that defines these animation objects.
How can I connect this animation action to the buttons within the list box? It doesn't make sense to me that the models/viewmodels know about the animation, because it doesn't change the state of anything in my application - it's just for visual purposes. I've thought about using a RoutedCommand defined in AnimationObject, and having the buttons bind their command property accordingly, but I worry that will simply make every element animate at the same time.
It is also important for my sake, that I conform to MVVM, as these data will be used in many other situations, perhaps even a different version of this view.
Any advice would be appreciated.
What you can do is call a command in your ViewModel , i.e. the DataContext of your ListBox.
CS :
public class ViewModel
{
public ICommand AnimateObjectCommand { get; }
}
XAML :
<DataTemplate x:Key="AnimationObjectItemTemplate">
<Button Command="{Binding Path=DataContext.AnimateObjectCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}" />
</DataTemplate>
<ListBox ItemsSource="{Binding AnimationObjects}" ItemTemplate="{StaticResource AnimationObjectItemTemplate}"/>
your Command implementation should be one that accepts an argument which would be passed by the CommandParameter .
private ICommand _animateObjectCommand;
public ICommand AnimateObjectCommand
{
get
{
if (_animateObjectCommand == null)
{
_animateObjectCommand = new RelayCommand<AnimationObject>( ao => { ao.Animate(); });
}
return _animateObjectCommand;
}
}
The CommandParameter = {Binding} meaning this.DataContext where this is an Item in your ListBox and it's DataContext is an AnimationObject.
I have a Window with a ContentControl inside. I want to show multiple view-filling UserControls like a wizard with multiple steps. Those UserControls need their own ViewModel and the possibility to replace themselves with another UserControl in the Window's ContentControl.
I want to work with the MVVM pattern and am currently struggling how to access the Window's ViewModel from the ViewModel of the UserControl.
Here is the simplified code I have so far. The content changing works without any problem when I change it inside the main ViewModel:
Window XAML:
<Grid>
<ContentControl Content="{Binding CurrentView}" />
</Grid>
Window ViewModel:
public class MainWindowViewModel : ViewModelBase
{
private object currentView;
public object CurrentView
{
get { return currentView; }
private set
{
currentView = value;
OnPropertyChanged(); // <- Property name is set automatically, so no parameter needed
}
}
public MainWindowViewModel()
{
this.CurrentView = new UserControl1(); // Initial view to show within the ContentControl
}
}
UserControl1 XAML:
<UserControl>
<Grid>
<Button Command="{Binding SwitchToUserControl2}">Switch content</Button>
</Grid>
</UserControl>
Now I have the following "thinking problems":
If I set the DataContext of the UserControl to its ViewModel, I cannot access the MainWindowViewModel to change the CurrentView Property to UserControl2.
If I don't set the DataContext of the UserControl, I automatically inherit the correct ViewModel for Binding the Command to change the Content but haven't instantiated the ViewModel of the UserControl. I need this because many actions of the UserControl should be handled within it's own ViewModel.
In my understanding it is neccessary to have access to both ViewModels from the view but have no clue how to achieve this.
I would not have the MainWindowViewModel create a view, but rather create your first ViewModel. The ViewModel could then use events or any other mechanism to notify that it should transition to the next step.
The View portion can be handled easily in that case via DataTemplates that map the ViewModel to the appropriate View. The advantage here is that the ViewModel never knows about the View used to render it, which stays "pure" in an MVVM perspective. Right now, your ViewModel is manipulating the View layer, which is an MVVM violation.
Reed's answer is correct, and is one way to solve your problem, create the ViewModel of the control in the MainWindow, hook up the events and bind the ViewModel to the user control via a DependencyProperty.
To allow the binding of the ViewModel to work, make sure you do not set the DataContext in the Constructor of the UserControl or on the Root element of the Xaml of the UserControl. Instead, set the DataContext on the first content element of the UserControl. This will allow external bindings to the UserControl to continue working while the DataContext of the UserControl is what you want.
<UserControl x:Class="StackOverflow._20914503.UserControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:this="clr-namespace:StackOverflow._20914503"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type this:UserControl1}}, Path=ViewModel}">
</Grid>
</UserControl>
With regards to swapping controls in and out, Reed is again correct and DataTemplates are the way to go.
Another way to solve your communications problems is to use RoutedEvents. Create a RoutedEvent with in the application and since the event will have no real association to a ui element, lets create a class to publish the routed event.
public static class EventManagement
{
public static readonly RoutedEvent ChangeViewEvent = EventManager.RegisterRoutedEvent("ChangeView", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(UserControl));
}
Now, in each of the UserControls (and it must be done within the code behind of a UserControl), you can call RaiseEvent which is implemented in the UIElement class. In the following code, I am picking up an event from the ViewModel of my UserControl and firing the RoutedEvent
private void ViewModel_ChangeEvent(object sender, EventArgs e)
{
RaiseEvent(new RoutedEventArgs(EventManagement.ChangeViewEvent));
}
In my main window, without know where the RoutedEvent is going to be fired from, I can add a handler to the Routed event like so
public MainWindow()
{
InitializeComponent();
this.AddHandler(EventManagement.ChangeViewEvent, new RoutedEventHandler(SomeControl_ChangeView));
}
private void SomeControl_ChangeView(object sender, RoutedEventArgs routedEventArgs)
{
}
.Net will handle the routing of the event for you based on the RoutedEvent registration.
The advantage of this approach is the separation the functionality. Everything works without knowing anything else. You can use triggers to insert UserControl into the MainWindow, they can all Raise the same RoutedEvent, and the MainWindow will handle them all.
To Summarise the flow of control. The ViewModel of the UserControl raises a standard CLR event that the UserControl handles. The UserControl Raises the RoutedEvent. .Net Bubbles the event up to the main window. The main window receives the event via its handler.
A couple of points to note.
1. The default routing strategy for RoutedEvents is Bubbling (from lowest element, say a button, to the highest, say MainWindow).
1. An event will stop once a handler has flagged the event as Handled.
1. Routing is mostly done via the Visual Tree.
If necessary, I can post the component parts of my example.
I hope this helps.
This is a question that extends from the originally posted here:
Link to loading-xaml through runtime
I'm working on a WPF MVVM application that loads XAML content dynamically from an external source, very similar as the answer in the post above.
Here is what I got so far:
My View declares an instance of the ViewModel as a resource and creates an instance of that ViewModel
In my ViewModel constructor I'm loading a XamlString property coming from an external source (file or db..)
In my view I have a button that user clicks after ViewModel finishes loading and in the click-event code-behind I'm deserializing the dynamically loaded XAML and add it to my grid.
My question is, how can I eliminate code-behind and automate the logic so the View can render the new xaml section dynamically right after the ViewModel is done getting the XAML content and initializing the string property?
Should I use some kind of Messaging Bus so the ViewModel notifies once the property has been set so the View can add the new content?
What troubles me is the fact that ViewModels do have a reference to Views and should not be in charge of generating UI elements.
Thanks in advance!
Edit:
Just to clarify: in my particular case I am not trying to bind a Business Object or Collection (Model) to a UI element (e.g. Grid) which obviously could be accomplished through templates and binding. My ViewModel is retrieving a whole XAML Form from an external source and setting it as a string property available to the View. My question is: Who should be in charge of deserializing this XAML string property into a UI element and add it programmatically to the my grid once my Xaml string property in the VM is set?
This sounds to me more of like a View responsibility, not ViewModel. But the pattern as i understand it enforces to replace any code-behind logic with V-VM bindings.
I have a working solution now and I'd like to share it. Unfortunately I did not get rid of code-behind completely but it works as I expect it to. Here is how it works(simplified):
I have my simplified ViewModel:
public class MyViewModel : ViewModelBase
{
//This property implements INPC and triggers notification on Set
public string XamlViewData {get;set;}
public ViewModel()
{
GetXamlFormData();
}
//Gets the XAML Form from an external source (e.g. Database, File System)
public void GetXamlFormData()
{
//Set the Xaml String property
XamlViewData = //Logic to get XAML string from external source
}
}
Now my View:
<UserControl.Resources>
<ViewModel:MyViewModel x:Key="Model"></ViewModel:MyViewModel>
</UserControl.Resources>
<Grid DataContext="{StaticResource Model}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<!-- This is the Grid used as a Place Holder to populate the dynamic content!-->
<Grid x:Name="content" Grid.Row="1" Margin="2"/>
<!-- Then create a Hidden TextBlock bound to my XamlString property. Right after binding happens I will trigger an event handled in the code-behind -->
<TextBlock Name="tb_XamlString" Text="{Binding Path=XamlViewData, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Visibility="Hidden" Loaded="tb_XamlString_Loaded" />
</StackPanel>
</Grid>
Basically I created a hidden TextBlock bound to my XAML String property in the ViewModel and I hooked its Loaded event to an event handler in the code behind of the View:
private void tb_XamlString_Loaded(object sender, RoutedEventArgs routedEventArgs)
{
//First get the ViewModel from DataContext
MyViewModel vm = content.DataContext as MyViewModel;
FrameworkElement rootObject = XamlReader.Parse(vm.XamlViewData) as FrameworkElement;
//Add the XAML portion to the Grid content to render the XAML form dynamically!
content.Children.Add(rootObject);
}
This may not be the most elegant but gets the job done. Like some people say, in MVVM there are some cases like this where little code-behind code is needed. It doesn't hurt and also part of this solution still uses the V-VM Binding principles when using the VM to retrieve and populate the XamlString property and exposing it to the View. If we would like to Unit Test the XAML parsing and loading functionality we could delegate it to a separate class.
I hope someone finds this useful!
I'm having trouble understanding what you're saying, so my answer will be based on my interpretation. You should consider posting a sample (simplified) of what you're trying to do.
1) I think you're misunderstanding what MVVM does. MVVM is mostly a binding-based pattern. Your view model should be exposing properties containing business objects and your view should just be binding to those properties. If I am misunderstanding you, and that's what you are doing, then your problem is that your view needs to be aware of when the properties get updated (after you deserialize your xaml, etc). There are two ways to do this: INotifyPropertyChanged interface on your viewmodel, or make your view model inherit from DependencyObject, and make the properties dependency properties. I won't go into details here, because this is a large subject that you should research on Google before making a decision.
2) Generally speaking, you shouldn't use click events inside your view if you're using MVVM. Instead, create properties on the view model of type ICommand (and create ICommand implementations to match, or use an implementation of DelegateCommand (google it) which will allow you to use delegates to implement the interface. The idea is, your view binds to the property and executes the handler directly inside the viewmodel.
3) If you want to push information from the viewmodel to the view, then you should create an event on the viewmodel and subscribe to it in the view, but this is a last resort, only to be used in cases like displaying a new window, etc. Generally, you should be using binding.
4) To be more specific about what you're doing, you should be binding your Grid's ItemsSource property to some property on the view model. Note, the property on the view model should be of type ObservableCollection<T> if you want to be able to add items and get instant updates.
Hope this helps.
Right now I have a rather large ViewModel called MainViewModel, that contains a lot of commands and properties that could be organized into more reasonably named ViewModels.
I however, only have one window, and so need to be able to cherry-pick the valid data and Commands from each of the SubViewModels....
I figure this could be achieved using properties, but I'm not so sure how to approach this in the XAML. (Or if there is an implication in the ViewModels themselves)
I realize that I can set the DataContext on SubViews as I see fit, but I want to avoid having my View dictate the hierarchy/organization of my ViewModels.
Ex. pseudo code
SubAViewModel mvmB = new SubBViewModel();
SubAViewModel mvmA = new SubAViewModel();
MainViewModel mvm = new MainViewModel( mvmA, mvmB );
<Window DataContext="{StaticResource MainViewModel}">
//This is clearly wrong but is sort of what I am trying to achieve
<MenuItem Command="{Binding Path=MainViewModel.SubAVM.TargetCmd}" />
It's entirely possible that a MenuItem or some other UserControl would want to access a Command in SubBViewModel and a property in SubAViewModel.
If you set the data context of the View to the MainViewModel and you expose the SubAVM as a property like this one:
public SubViewModel SubAVM {
get {
return subAVM;
}
set{
if (subAVM == value)
{
return;
}
subAVM = value; //implement the OnPropertyChanged ...
}
}
Of course the Path on a MenuItem would be SubAVM.TargetCmd as by the dependency hierarchy the main path is already MainViewModel for a Menu.
I've built a WPF UserControl View/ViewModel pair: the view XAML handles the layout and bindings, and the ViewModel handles the logic, in-line with the recommended MVVM pattern.
I would like to be able to re-use this as a control.
How do I hide/encapsulate the ViewModel associated with the view, so that I can use the control as I would a standard control [such as a button] ?
i.e. How do I hide the control's viewmodel ?
depends on how you bind ViewModel class to the control.
if you do like this:
YourControl()
{
DataContex = new ViewModel();
}
then I don't see any problems. add reference to your control and use it.
You can create your ViewModel as a StaticResource within your XAML. The problem with setting the DataContext to your ViewModel is that you can't use that you can no longer use your DataContext from the window or page you in which you use the control.
In your XAML declare your ViewModel:
<myNS:MyViewModel x:Key="ViewModel />
Reference your view model within your XAML:
<TextBlock Text="{Binding Source={StaticResource ViewModel}, Path=TextToBind}" />
In your Code Behind you can access and initialize quickly, I usually make a property for easy reference to my view model.
private MyViewModel viewModel
{
get { return this.Resources["ViewModel"] as MyViewModel; }
}