MVVM UserControl and binding when using that control - wpf

I made a UserControl with the MVVM pattern, where the UserControl's "intelligence" is in its viewModel.
I want to user that UserControl in different views (xaml) so the developer of that view doesn't have to mind about how it is done.
I added some dependencyProperties in my UserControl so the end-programmer could give the control some context informations.
But I have some binding issues.
In the client.xaml:
<Grid>
<MyUserControl MyDependencyProperty0={Binding ClientViewModelProperty0}/>
</Grid>
and in myusercontrol.xaml
<Grid>
<TextBlock Text={Binding TextToDisplay}/>
</Grid>
where TextToDisplay is a property of MyUserControlViewModel.
I only need the ClientViewModelProperty0 to be set once, I do not need the clientViewModel to be set as the DataContext of MyUserControl since it has its own dataContext(its view-model)
I assume the solution would be a different Binding Expression syntax (relative source? self?) but I cannot see which one...

Reusable controls tend to follow a somewhat different design than full-blown application views. Specifically, they don't follow MVVM in quite the same way.
Remember that in WPF, controls are "lookless": their visual appearance is governed by templates. The underlying class is the "model" for the control. Like #Will mentioned in his comment, a TextBox does not have a TextBoxViewModel; the TextBox instance is the "view model". The "view" is the template that gets applied. While a UserControl is a bit different from a templated control (its content is self-contained, so it's effectively both the "view" and the "view model"), the same basic rules apply:
When you create your own reusable controls, put your properties and behavior in the control class itself. That way, when you plug it in to a view, you can set the parameters however you like, e.g., by binding them against the parent view model. A reusable control should never rely on some external/ambient view model being present.

Related

How can I bind Text property of TextBox (VIEW) to a vaiable (in VIEWMODEL)

I am a newbie in WPF. I was exploring MVVM Pattern for WPF applications. I am having trouble in binding Text property of a TextBox from VIEW to a variable in VIEWMODEL
Here is the TextBox from MainWindow.xaml
<TextBox x:Name="UsernameTxt" Grid.Row="4" materialDesign:HintAssist.Hint="Username"/>
I just need to know how to bind its Text Property to ViewModel Class in Class Library
Thanks
I think it's possible to give a very generic answer to this very generic question.
If the question changes context this answer is very likely to be deleted but here goes anyhow.
You want your viewmodel to be in the datacontext of the textbox. Because datacontext is inherited down the visual tree this usually means you want to set datacontext of your window to an instance of the viewmodel. Or maybe the usercontrol your textbox is in, but we know nothing about your app so let's just cover the simple scenario.
Your options are to instantiate a viewmodel using code or xaml.
If you look at this article:
https://social.technet.microsoft.com/wiki/contents/articles/31915.wpf-mvvm-step-by-step-1.aspx
That instantiates in xaml.
Note the xmlns is
xmlns:local="clr-namespace:wpf_MVVM_Step01"
That's saying where you see some bit of markup which is prefaced "local:" then go get the class out of this namespace.
To point to a different dll ( a class library ) you need to tell it which assembly. You do that by adding ;assembly=Whicheverdll to your equivalent of that xmlns. And of course that won't be local then so give it a different name. You also need a reference to that dll or project added to the entry point exe.
Once you've done all that and your viewmodel is instantiated into memory and in the datacontext of that textbox you need some sort of binding.
Which the article covers but that will be something like:
<TextBox Text="{Binding YourPublicStringProperty}"/>

Use properties of the base control that is inside the UserControl

How can I use the properties of the controls that are inside a user control without having to use DependencyProperty?
Since, if for example I want to use all the properties of a button, I would have to declare all these?
And if there is another way without user control and it is the correct one, I would appreciate it if you answered it. (Google translator, sorry)
UserControl:
<UserControl x:Class="UserControls.UserControl01"
...
>
<Grid>
<Button x:Name="uc_btn" />
<TextBox x:Name="uc_txt" />
<DataGrid x:Name="uc_dtg" />
</Grid>
</UserControl>
Code using the UserControl:
<Window x:Class="UserControls.wnd02"
...
>
<Grid>
<local:UserControl01 uc_btn.Background="Red" uc_txt.Margin="10" uc_dtg.BorderThickness="5" Margin="90" />
<local:UserControl01 uc_btn.Background="Green" uc_txt.Margin="25" uc_dtg.BorderThickness="20" Margin="5" />
</Grid>
</Window>
It is not usual to do what you are asking.
Let's consider a usercontrol which is intended to work as if it is one single control. For example a time picker. This contains two sliders which increase/decrease hour and minute. You can also overtype in the hour and minute textboxes and there's a : between the two textboxes.
This usercontrol is all about the one property though. Time. You don't care what the minutes background is externally. If this changes it's internal to the usercontrol.
In this scenario you'd usually add a TimeSpan dependency property to the usercontrol and this is the only thing anything external to it uses.
Pretty much all commercial WPF development uses the MVVM pattern and that TimeSpan would be bound to a property in the parent view's viewmodel.
That's one scenario.
Another is where a usercontrol encapsulates a bunch of UI which is then re-usable.
Styling has scope so when you apply a style to say a Button in a window then that would apply to any Buttons in a usercontrol within it. Setting their properties.
There are also certain dependency properties marked as "inherits" whose values propogate down the visual tree.
One such is DataContext and it is this which most teams would use to deal with properties within a usercontrol.
Using MVVM there would be a MainWindowViewModel.
That would have (say ) a ChildUserControlViewModel property. That would be associated with usercontrol using a datatemplate specified datatype.
You'd then bind properties of whatever is in a usercontrol to properties of ChildUserControlViewModel or properties of MainWindowViewModel using RelativeSource binding.
ViewModel first is a common navigation and composition pattern for WPF. You should be able to find numerous blogs explain it better than I can in a SO post.
Here's one:
https://social.technet.microsoft.com/wiki/contents/articles/30898.simple-navigation-technique-in-wpf-using-mvvm.aspx

Binding Parent View DataContext to DataTemplate Child View DataContexts

I've been experimenting with WPF, Xaml, MVVM, and DependencyInjection lately. Consequently, I am creating a UI using MVVM principles. A certain portion of the UI is designed to act like a wizard wherein not all of the available options are presented to the user at the same time. Each section of options is its own View (sub-View) with a single View (Parent View) hosting these sub-Views in a ContentControl. The user sets certain options and uses buttons to move from one section to the other.
View Navigation
To switch between these views I'm using a DataTemplateSelector with each sub-View defined as a DataTemplate in my Xaml resources.
Content Control in the Main View:
<ContentControl Content="{Binding ElementName=ParentViewControl, Path=ViewState, Mode=TwoWay}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
Example sub-View Data Template:
<DataTemplate x:Key="SubViewATemplate">
<local:SubViewAView x:Name="SVAView" DataContext="{Binding ElementName=ParentViewControl, Path=DataContext}" ViewState="{Binding ElementName=ParentViewControl, Path=ViewState, Mode=TwoWay }" />
</DataTemplate>
On the Parent View and each sub-View I've created a Dependency Property called ViewState (an enum). These bind to each other through the DataTemplates. In each View's code-behind I update this ViewState Property based off user input and it propagates up to the Parent View which in turn triggers the DataTemplateSelector. So far, so good. The navigation works beautifully.
ViewModel Info
The Parent View has a ViewModel which implements INotifyPropertyChanged as its DataContext. I'm attempting to use this single ViewModel to bind Properties to the Parent View and the sub-Views. The problem is the DataContext binding in the DataTemplate snippet above does not work. (Which is odd to me since the ViewState binding does.) After various attempts to get this to work, the DataContext on a sub-View is either null or the ViewState control variable.
I am currently using the UnityContainer as my dependency injector.
Various Attempts
Here are the various other things I've tried that have all failed:
1) Registered the ViewModel as a singleton in the UnityContainer thereby using Constructor Injection on the sub-Views to set the DataContext. (Does not work because there must be a Parameter-less Constructor for the DataTemplate resource.)
2) Registered the ViewModel as a singleton in the UnityContainer and then using Property Injection on the sub-Views to set the DataContext. (Does not work. I think this is due to the UnityContainer not working when an object is instantiated in Xaml.)
3) Creating sub-ViewModels for each sub-View that needs a ViewModel to display properties that would have existed on the Parent ViewModel. I've used this before to get around the Xaml instantiation problem with the UnityContainer. I then replace the DataContext binding in the DataTemplate with the associated sub-ViewModel. (Does not work because for some reason the DataContext of my Parent View is getting set to the ViewState variable instead of remaining my ViewModel which I've set in the view's Constructor. This in turn means the sub-ViewModel property on my Parent ViewModel can't be found to bind to the DataContext of the sub-View.) Are the Content and DataContext of UserControls the same thing? Does setting one affect the other?
4) Moving the ViewState dependency property from the View to the ViewModel and then setting the ContentControl's Content to bind to the ViewModel. This violates MVVM principles but by this time I was trying anything to get this to work. (It doesn't work because when ViewState is changed in the code-behind of the view the ViewModel does not trigger as changed.) I haven't gone any further with this one because I didn't want to go deeper violating MVVM.
Conclusion
I've found most of these attempted solutions on this site over the last couple days. I haven't had any formal training in WPF, Xaml, and MVVM so I suspect I'm missing something obvious, or am attempting to do something that isn't possible. I'm going to keep attempting variations on the above and researching until I find something that works, but I thought I would tap into the collective knowledge here to help me find a solution.
What I'd prefer is to have the group of Views use the single ViewModel as their DataContext so I can bind properties to their controls. And have the Views' navigation be controlled by a DataTemplateSelector. Is there a way to do this that I'm not seeing?
Thank you for your time!
I have had similar issues before, I have had good luck using a RelativeSource binding. Maybe try something like this:
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=DataContext}"
Just a thought.

When creating VMs for UserControls, who should create and set the VM?

I'm getting a little confused about how to layout my code in MVVM - if I have a UserControl with a corresponding VM class, how should other controls consume my UserControl?
Should consumers bind directly to the VM or should I duplicate only a subset of these properties I want to actually be used as DependencyProperties of the UserControl?
For that matter, should the UserControl's VM be injected into the UserControl's code-behind or should the VM of any control that uses this UserControl contain it as a dependency and bind it to the UserControl instead?
Just to make it clear: Suppose I have a ListBox in a UserControl and use it in a Window that is already implemented with MVVM. But I'm confused about the implementation of the UserControl VM and the corresponding bindings.
I would think the ideal solution would be to expose the SelectedItems of the ListBox via dependency properties in the UserControl, and then the Window which uses the UserControl would bind to these.
Or should the Window's VM have a reference to the VM as a property, have it injected and bind directly to the properties on that instead?
Should dependency properties only be defined in UserControls or can / should they be defined in the VM?
I'm thinking the Window would bind, from within the XAML of the Window, either via
{Binding ElementName=myUserControl, Path=SelectedItems}
or
{Binding Path=MyViewModel.SelectedItems}
It just seems to make more sense to do it via the former, since the latter requires that the View knows about another VM?
> how should other controls consume my UserControl?
Via exposed dependancy properties on the usercontrol only.
>Should consumers bind directly to the VM or should I duplicate only a subset of these properties I want to actually be used as DependencyProperties of the UserControl?
Each control should be a stand alone entity, there shouldn't be any secret handshake (either to or from) to use the control. Think of the design like you are Microsoft and many different users will use your controls. So answer #1 is just as relevant; think a stand alone entity.
>I would think the ideal solution would be to expose the SelectedItems of the ListBox via dependency properties in the UserControl, and then the Window which uses the UserControl would bind to these.
The window which hosts your controls will have a View Model which contains a Observable list of data items. That will hold the data which the user control(s) will bind via their dependency properties. THink of it as a producer pattern with many consumers. The consumers are the controls. Whether the controls have VMs or not is immaterial to the running of the main program; for each control is its own island.
Keep in mind when working with WPF and MVVM that your View layer is merely a user-friendly way of drawing your Models and ViewModels, and that your View is not actually your application. Your View actually has to know the basics about your data layer so it can define how to draw it.
So if your application needs to display a list of Items and maintain a SelectedItem, than that should be in your ViewModel or Model somewhere, not in the actual View layer.
Typically for me UserControls are one of two things:
Either a standalone UserControl that can be used anywhere without a specific DataContext, and that expose DependencyProperties for any control-specific values. Examples are things like a Calendar control or a Popup control
<local:MyUserControl Items="{Binding SomeItemList}"
SelectedItem="{Binding SomeItem}" />
Or they are a UserControl that is meant to be used with a specific ViewModel only. This is far more common for me. The ViewModel is a property somewhere in the data layer, and I usually have an implicit DataTemplate in the application somewhere to tell WPF to use that UserControl anytime it needs to render that specific ViewModel
<DataTemplate DataType="{x:Type local:SomeViewModel}">
<local:MyUserControl />
</DataTemplate>
<ContentPresenter Content="{Binding SomeViewModelProperty}" />
Also, at no time should you set the DataContext of a UserControl from inside the UserControl itself, because the UI layer is only meant to be a UI representation of your data layer (your Models/ViewModels), and by setting the data layer from inside the UserControl you are making it so that the UserControl cannot be used to draw any other data object.

Dynamically bind Views into a ContainerControl with MVVM

I've been learning the MVVM pattern with Josh Smith's article and I want to create a classic layout with some links to the right (managed with commands) so when I click one I can show my view to the right into a tab control (inside it there is a ContentControl).
This is simple when I use a DataTemplate with the specific View and ViewModel I want to show on screen like this.
<!-- this section into my MainWindow's resources file -->
<DataTemplate xmlns:vm='clr-namespace:WpfFramework.ViewModels'
xmlns:vw='clr-namespace:WpfFramework.Views'
DataType="{x:Type vm:MySpecificViewModel }" >
<vw:MySpecificView />
</DataTemplate>
But, I want something more generic. I mean that my mainWindow should not know a specific View nor a specific ViewModel. It should only know that it binds to some commands and has a tab control which shows "some view". Every sample including Josh Smith's article seems to have limited universe of views and viewmodels, that's great with a sample.
So, how can I tell my ContentControl that some view (with its corresponding viewModel) is gonna be there without being so specific (without "burning" into the mainView the concrete types)?
best regards
Rodrigo
PD. I have tryed with base a ViewModel and Base View but it doesn't seem to work.
In your main View, bind a ContentControl to a generic ViewModelBase property
<ContentControl Content="{Binding CurrentPage}" />
CurrentPage would be defined in the main ViewModel as a ViewModelBase object, and to switch pages you simply set CurrentPage to whatever you want.
So when you click on something like the HomePageCommand, the main ViewModel would execute CurrentPage = new HomePageViewModel(); providing that HomePageViewModel inherits from ViewModelBase.
I wrote something a little while ago that shows some samples here if you're interested

Resources