Binding Silverlight UserControl to a complex object - silverlight

What is the "best" (or the most-often-used) approach when binding a complex class to a user control for re-use?
I am trying to create some reusable libraries for classes, and I am not sure which approach I should use. Example: I want to create an Address library that defines and Address class (with properties Line1, Line2 etc.), it's validation logic and an AddressControl which acts as the viewer/editor with bound fields for each property.
In use I might have a customer class with BillingAddress, DeliveryAddress properties and I would want to bind these in my customer control thus:
<addressLib:AddressControl [xxx]="{Binding BillingAddress}" />
So the question is what do I put in XXX?
Initially I thought of creating a DependencyProperty 'Address' on the control:
<addressLib:AddressControl Address="{Binding BillingAddress}" />
But now I am thinking surely I could just use the existing DataContext property?
<addressLib:AddressControl DataContext="{Binding BillingAddress}" />
Is this the best approach? Are there any issues e.g. updates or NotifyPropertyChange issues?
many thanks for your help!

One difference to remember is that with dependency property you get change notification and with datacontext you don't (at least not untill SL5 is out)

Setting the DataContext of your control to your model (in this case a BillingAddress) is the way to go. If your Control is used in a DataTemplate for an ItemsControl then the DataContext would be of course just "{Binding}".

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}"/>

Defining type of viewmodel for usercontrol and then handing it from parent control

I'm trying to wrap my head around WPF and MVVM.
In my test application I have a MainViewModel with a ChildViewModel property.
Similarly I have a Window that instantiates a MainViewModel and has a child control that should receive MainViewModel.ChildViewModel
For my current application I have a Window with (snipped for brevity)
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Window.Content>
<view:ChildView DataContext="ChildViewModel"/>
</Window.Content>
How do I have my ChildView usercontrol define that it requires a datacontext of the type ChildViewModel and also receive it?
Currently I create it by setting it like so:
<UserControl.DataContext>
<vm:ChildViewModel>
</UserControl.DataContext>
but this creates a new instance of the ChildViewModel.
I tried to make the question as bare boned as possible. Hope it's still clear
Googling turned up a LOT of (contesting) approaches, but I can't see the forest for the trees anymore.
Your UserControl doesn't need to specify it's own ViewModel - you're already creating one and binding it.
That being said, your binding should be specified as:
<view:ChildView DataContext="{Binding ChildViewModel}"/>
The confusion likely stems from the fact that there are two distinctly different approaches to MVVM. Creating the VM within the Xaml's <DataContext> tag is a "view-first" approach - the View is creating and instantiating the ViewModel.
You're currently working in more of a ViewModel-First approach (similar to my series on MVVM), where the ViewModel's create the other ViewModels, and the VIew just binds to them.
Personally, I find VM-first easier to maintain from a code standpoint, and often prefer it. View-first has the advantage of (potentially) providing a better design time experience, which is why many MVVM advocates use that approach.

Binding instances being re-used in DataTemplates when ValidationRules can't be shared

I'm looking for a solution and/or the rationality behind why a Binding instance is shared within a DataTemplate. This ultimately comes down to the fact that within a DataTemplate, there's seemingly no way to force a new instance of a Binding on a DependencyProperty for each control generated. That is probably a fair and good assumption in all cases except when there are ValidationRules representing something specific about the instance of that control.
To elaborate (I can provide code, but I don't believe it's necessary), I use a DependencyPropertyDescriptor on IsEnabled to update one or more ValidationRules that belong to a TextBox.Text Binding, DatePicker.Text Binding, or a ComboBox.SelectedValue Binding, etc. The idea is that validation will be different or undesired when a control is not enabled.
Because of this, the ValidationRule's state of IsEnabled is specific to the individual control and because the ValidationRule Collection is a part of the Binding and the Binding instance is being shared - each control that ends up sharing that binding will update/override the previous IsEnabled value that was applied by the previously generated control's IsEnabled value.
IsEnabled is only one of at least two properties in the ValidationRule (another custom IsRequired DependencyProperty) that represent the state of the control to which the Binding is applied. When working outside of a DataTemplate (IE: The Binding instance is not shared), this works very well and ignores/alters the validation logic based on the state of the control. I'm not closed off to alternatives but do feel this has been (this issue aside) a very flexible and dynamic option that allows the Binding instance ValidationRule's and the control-changed state of the Rules to evolve effortlessly. This has also allowed me to avoid other obvious but much uglier options such as creating several Bindings, each representing one of the combinations of the ValidationRule's control properties and switching the whole binding out at the time the DependencyPropertyDescriptor fires. shivers
Any thoughts are MUCH appreciated!
I suggest you to use x:Shared attribute in the DataTemplate definition.
<DataTemplate x:Key="DataTemplateKey" DataType="{x:Type YourType}" x:Shared="False">
...
</DataTemplate>
Since you use it WPF will create a new DataTemplate instance on every request.

How to expose view-model properties as externally bindable dependency properties?

I'm writing a WPF user control which internally uses MVVM. In order to work out-of-the-box, it creates its own view-model (this part is not relevant to the question).
My control exposes a number of dependency properties. The host view (whoever uses my control) can set these DPs or bind to them. Some of these DPs have corresponding view-model properties.
This is what I want to achieve (see diagram): the DPs in my control's view are bound to the corresponding properties of the view-model. Additionally, the host view needs to be able to set or bind these DPs, too - the same way you use any other control.
However, as soon as the host view sets or binds the DP's value, the original binding (binding it internally to the control's view-model) is lost. This is because every DP can only have a single binding set. So this doesn't work:
One solution is to make the view-model a DependencyObject and its properties DPs. This way, I could bind the control's view-model to its view rather than the other way around, effectively reversing the left arrow. However, for various reasons, I want my view-model to use plain C# properties and INotifyPropertyChanged.
Any ideas on how to expose view-model properties as externally bindable dependency properties?
Edit:
To clarify on the requirements: the view does not in fact create the view-model itself (I realize that would violate the separation of concerns); rather, it uses a view-model locator to do so. (It's a terribly modular enterprise application.) I know this is not a typical approach (see Rachel's answer). However, accepting the described set-up, is there any way to achieve the desired result?
So the situation is that you have VM and View, they must be data bound to each other and VM can`t be DependencyObject? Well, triple binding VM-V-Host is not possible, AFAIK.
One possible approach is to make duplicate set of DependencyProperties in View. Original properties will be bound to VM and will affect the look of View, duplicate properties will be bound to Host and somehow affect the VM (with DP changing logic, for example).
If I'm creating a ViewModel for a CustomControl, it is expected that the user will use that ViewModel as the CustomControl.DataContext. Any other time, my UserControl's don't have ViewModels associated with them.
For example, suppose I wanted a PopupControl.
I would either create a UserControl with all custom properties and logic in the code-behind the UserControl (no ViewModel)
<local:Popup Header="{Binding PopupHeader}"
IsVisible="{Binding IsVisible}" />
OR
I would create a PopupViewModel and build my PopupUserControl expecting that the DataContext will be of type PopupViewModel
<DataTemplate DataType="{x:Type local:PopupViewModel}">
<local:PopupView />
</DataTemplate>
<ContentControl Content="{Binding MyPopupViewModel}" />
Properties like IsVisible or PopupHeader would exist in the ViewModel, not in the PopupUserControl

working with WPF usercontrol and MVVM

I have following questions
Should the consumer of my usercontrol assign the usercontrol's DataContext or set some dependency property. (related to #3 : if DataContext then my individual items need to bind directly to the object given in DC, if DP then I have the luxury to have bind to any VM)
If they set property, and if I am using 3 primitive items, should I accept them as individual properties or combine them together to a Model for my usercontrol
Should I ask the consumer of my usercontrol to send me model or viewmodel ( I say viewmodel but for all the controls I have used so far, I have never seen anybody asking me to send them VM - I am sure some could be implementing MVVM internally
Your consumer wants a user control. So I presume the user control should be able to work in any context/application(WPF). So, to answer your questions
1) The consumer should set dependency properties which is defined in the user control. By using the datacontext you will be coupling the usercontrol to the consumer.
2)Take them as individual primitive properties, otherwise the consumer needs to create an object unnecessarily to cater with your model(coupling again-why should the consumer need to know about your model?).
3)No, you should not ask the cosumer to send you the view model.Why do you need to know which consumer is using your "generic" user control.
If you cannot do any of the above because of practical considerations - then dont worry about breaking any/all the rules because your user conrol is coupled with a specific context-it is not generic any more. If you write a generic user control, any WPF application can use your user control. It is your call.
1.
I would say this depends on the kind of UserControl, if it is "generic" you should be able to change the DataContext as the control internally should not have anything to do with the DataContext. For example if i create an ImageButton UserControl which exposes the properties Caption and ImageSource then those should be bound internally independent of the DataContext, the on the instance those can be bound and the DataContext may be changed as well, e.g.
<uc:ImageButton Caption="{Binding ButtonInfo.Caption}"
ImageSource="{Binding ButtonInfo.Image}"/>
Here one could then change the DataContext to simplify the bindings a bit:
<uc:ImageButton DataContext="{Binding ButtonInfo}"
Caption="{Binding Caption}"
ImageSource="{Binding Image}"/>
If on the other hand the UserControl is a view for a viewmodel i would expect the UserControl to bind to viewmodel properties internally, relative to the DataContext.
So in a DataTemplate where the current DataContext is already the viewmodel of that view a simple instance without anything should do, i.e.
<v:StatisticsView />
If the viewmodel to be passed is in a property of the current DataContext you may bind the DataContext as well:
<v:StatisticsView DataContext="{Binding StatisticsViewModel}"/>
2.
This can be handled either way i would say, especially if you have only three properties its not too much of a hassle to create those. You might want to consider some aspects like dependency, e.g. does it make sense to group all three propeties in an object?
3.
As noted in 1. this should be apparent from the UserControl itself, if it's a StatisticsView the consumer should pass in a StatisticsViewModel (either implicitly by inheriting the current DataContext or by binding it explicitly).

Resources