WPF: Proper configuration for Window with a child UserControl (MVVM) - wpf

I am trying to properly accomplish the following. I have a UserControl (ProgramView). It has a viewmodel (ProgramViewViewModel). ProgramView is consumed as a child control within a Window (ProgramWindow). ProgramWindow has a public property ProgramId, so the consumer of the window can specify the desired Program (data entity) to show. ProgramView has a property ProgramId, as it's primary job is to display this data. ProgramWindow is little more than a wrapper window for this user control.
ProgramViewViewModel also has a property ProgramId. Changes to this property drive out the operation of the view model, which are surfaced out of the view model using other properties, which ProgramView can bind to.
I am trying to hide the operation of the view model from the consumer of the ProgramView and ProgramWindow.
This ProgramId should be bound through all of these layers. Changes to ProgramWindow.ProgramId should flow to ProgramView.ProgramId and then to ProgramViewViewModel.ProgramId. I cannot figure out how to properly implement this.
My current approach is to surface ProgramId in all three classes as a DP. Within the Window, I would imagine ProgramView being instantiated thusly:
<local:ProgramView ProgramId="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ProgramWindow}}, Path=ProgramId}" />
This appears to actually work. Within ProgramView, I do obtain changed events for the property, and they do appear to have the correct value. FindAncestor seems to operate properly.
How then should I synchronize the ProgramViewViewModel.ProgramId property? I see two ways. One way would be to set a Binding on the ProgramViewViewModel instance itself, to also use FindAncestor, and find the ProgramId on the ProgramViewViewModel This has two downsides. It requires ProgramViewViewModel to surface ProgramId as a dependency property. I'd rather like to avoid this, but it might be acceptable. Either way, I cannot accomplish it in XAML.
<local:View.DataContext>
<local:ProgramViewViewModel
ProgramId="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ProgramView}}, Path=ProgramId}" />
</local:View.DataContext>
This does not work. It appears that I cannot introduce a binding expression within the instantiation of the instance. FindAncestor reports that it cannot find ProgramView. My theory here is that the instance is outside of the logical tree, and thus cannot traverse to it's parent.
The second option, which makes more sense, is to bind the ProgramView.ProgramId property to "ProgramId" (in the DataContext). I cannot accomplish this because I cannot figure out how to specify a binding expression on a property defined in the code-behind. is required in the XAML, but the type ProgramId exists on is actually . I cannot figure out how to specify this property.
If I manually (in code-behind of ProgramView) create a Binding instance and call SetBinding(ProgramIdProperty, binding), the value no longer flows into the View itself. I believe this is because I just replaced the binding on ProgramView.ProgramId, which was previously set by ProgramWindow. One binding per-property?
My remaining ideas are to provide TWO ProgramId properties in ProgramView. One bound to the DataContext, the other publicly available to be bound by the consumer (ProgramWindow), and then write OnValueChanged handlers that synchronize the two. This feels like a hack. The other is to manually watch for changes on ProgramView.ProgramId and ProgramView.DataContext within the code-behind of ProgramView, and propagate the value myself. Neither of these ideas feel ideal.
I'm looking for other suggestions.

Your description seems detailed but I'm having trouble understanding why you need to implement this design. I can't help but think DRY.
If you need to expose a dependency property in two such-related view models, I would suggest that you make the child view model (for the user control view) a property of the first (for the program window view). Something like:
public class MainViewModel : ViewModelBase
{
public ProgramViewModel ChildViewModel { get; private set; }
}
public class ProgramViewModel : ViewModelBase
{
private int _ProgramId;
public int ProgramId
{
get { return _ProgramId; }
set
{
if (value != _ProgramId)
{
// set and raise propery changed notification
}
}
}
}
The MainView can get the property using ChildViewModel.ProgramId (data context set to MainViewModel). The ProgramView accesses it by ProgramId (data context set to MainViewModel.ChildViewModel).

Related

How to apply Metadata from a ViewModel to the View in WPF with MVVM

I have a viewModel with properties like the following and a set of specific attributes used throughout the viewmodels.
public class MyViewModel : BaseModel
{
[StringLength(50), Required]
[SetLockedForExistingEntities]
public string FirstName { get ... set ... }
public bool IsInNewMode { get; }
}
Now I want to apply such metaData in a view in a consistent way. Like... If bound, set TextBox maxlength from the MaxLengthAttribute. If SetLockedForExistingEntitiesAttribute is set, disable the control in case viewModel is not in some 'New' Mode etc..
Is that doable/a good idea to do with a custom MarkupExtension that replaces "Binding" for VM Bindings? Or would it be better to use a Behavior (applied via attached property) which tries to apply anything it can from the bound ViewModel property to the control it is attached to?
Usage would be like
(A) Attached dependencyproperty that reads the binding from TextBox.Text and applies behaviors
<TextBox Text="{Binding Model.FirstName, ValidatesOnDataErrors=True}" "bb:MyBindingHelper.ApplyViewModelBehaviors="True" />
(B) Custom MarkupExtension that does all in one
<TextBox Text="{BindingWithModelBasedBehaviors Model.FirstName}" />
You could write a markup extension that gets the property from the datacontext and reads attributes.
That would be kind of complicated but you can get the property name of properties where the source changed event was raised.
That looks rather like validation to me.
You could implement inotifydataerrorinfo in a base viewmodel and write code there that validates properties using attributes.
That's how the code in this works:
https://gallery.technet.microsoft.com/scriptcenter/WPF-Entity-Framework-MVVM-78cdc204
That works by the view telling the viewmodel which property's value just passed to the viewmodel.
You can extend the method you use for raising property changed to pass the property name to the validation.
Or you could even do the check from a method called in the property setter before you set the value on a property and not set the value if the new one fails validation.
As a specific property fails validation in a particular way you could run an action.
The production code version of that app I linked also has a dictionary of predicates used as well as attributes. They could have code in them references and sets other viewmodel properties.

WPF/XAML - binding control commands to parent window's viewmodel?

I have an application, and I have an assembly.
In the application, I have a window, and in the assembly I have a user control.
There is an instance of the user control in the window.
Both the user control and the window are backed by separate viewmodels.
In the user control, there is a button. The button should be enabled/disabled based on the state of the user control's viewmodel. When the button is clicked, processing needs to be done, based on the information in the user control's viewmodel, but it needs to be done by the window's viewmodel. (There are aspects of what needs to be done that are, and should be, outside of the scope of the user control.)
And here's the twist - this user control won't be used exclusively in this window, it might be used in another, or in a control that is used in a third. The user control can't be allowed to know what kind of window or control contains it, or is handling the process when its button is clicked.
So, what to do?
Define a command in the assembly, and bind the user control's button to it, passing the user control's viewmodel as the command parameter? How, then, do I bind the command to the window's viewmodel?
Or should I define the command in the user control's viewmodel, then raise an event to tell the parent window that the appropriate action needs to be taken?
It's not clear to me which is cleaner.
If you always know that the parent's property is going to be exposed the same with the same name, you can do something like this that has worked for me plenty of times:
Command={Binding Parent.DataContext.SomeCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}
This gets the usercontrol, then goes to the parent and gets that datacontext and binds it to that command. This works when the user control will be encompassed by many windows / controls that expose the same command (you could implement an interface here).
You could then pass the user control's viewmodel to the command (again, implement some interface) like so:
CommandParaemter={Binding }
You could use a Messenger structure to communicate between ViewModels.
MVVMLight contains one that you could use or you could write your own.
Before doing this make sure you did separate the responsibilities correctly or you'll end up with spaghetti-messages-code.
There should be hierarchy with your view models, just like you have with your controls. The main window has a child user-control. The Main View Model should be able to get connected with User Control View Model (and assign it if needed). Here is how I would do it:
public class MainVM:NotificationObject
{
// Make this a Notify Property
public UserVM userVM { get{return _userVM;}; set {_userVM = value; RaisePropertyChanged("userVM");}
public MainVM
{
userVM = new UserVM();
userVM.ExecuteCmd = new DelegateCommand (yourAction);
}
}
public class UserVM:NotificationObject
{
public DelegateCommand ExecuteCmd {get{return _executeCmd;} set{_executeCmd = value; RaisePropertyChanged("ExecuteCmd");
}
}
XAML:
<local:urUserCtrl DataContext={Binding userVM}/>
This is of course psuedocode
Sounds like a case for the Strategy pattern. http://en.wikipedia.org/wiki/Strategy_pattern
Define an interface for a strategy object that can be assigned to the UserControl's viewmodel, (or used to initialise it). The interface defines whatever properties/methods/events are required to enable the strategy object to retrieve from the UserControl viewmodel the data needed for the processing, plus a means of returning the result of the processing back to the UserControl viewmodel.
Then create a concrete implementation of that strategy object that collaberates with the Window's viewmodel to perform whatever task it needs to. In this case the Window's viewmodel might even implement the strategy interface itself.
Other instances of the UserControl in other scenarios can then be initialised with other concrete implementations of the strategy object that perform the same required task, but possibly in very different ways.

passing data to a mvvm usercontrol

I'm writting a form in WPF/c# with the MVVM pattern and trying to share data with a user control. (Well, the User Controls View Model)
I either need to:
Create a View model in the parents and bind it to the User Control
Bind certain classes with the View Model in the Xaml
Be told that User Controls arn't the way to go with MVVM and be pushed in the correct direction. (I've seen data templates but they didn't seem ideal)
The usercontrol is only being used to make large forms more manageable so I'm not sure if this is the way to go with MVVM, it's just how I would of done it in the past.
I would like to pass a class the VM contruct in the Xaml.
<TabItem Header="Applicants">
<Views:ApplicantTabView>
<UserControl.DataContext>
<ViewModels:ApplicantTabViewModel Client="{Binding Client} />
</UserControl.DataContext>
</Views:ApplicantTabView>
</TabItem>
public ClientComp Client
{
get { return (ClientComp)GetValue(ClientProperty); }
set { SetValue(ClientProperty, value); }
}
public static readonly DependencyProperty ClientProperty = DependencyProperty.Register("Client", typeof(ClientComp),
typeof(ApplicantTabViewModel),
new FrameworkPropertyMetadata
(null));
But I can't seem to get a dependancy property to accept non static content.
This has been an issue for me for a while but assumed I'd find out but have failed so here I am here.
Thanks in advance,
Oli
Oli - it is OK (actually - recommended) to split portions of the View into UserControl, if UI became too big - and independently you can split the view models to sub view models, if VM became too big.
It appears though that you are doing double-instantiations of your sub VM. There is also no need to create Dependency Property in your VM (actually, I think it is wrong).
In your outer VM, just have the ClientComp a regular property. If you don't intend to change it - the setter doesn't even have to fire a property changed event, although it is recommended.
public class OuterVm
{
public ClientComp Client { get; private set; }
// instantiate ClientComp in constructor:
public OuterVm( ) {
Client = new ClientComp( );
}
}
Then, in the XAML, put the ApplicantTabView, and bind its data context:
...
<TabItem Header="Applicants">
<Views:ApplicantTabView DataContext="{Binding Client}" />
</TabItem>
I answered a similar question as yours recently: passing a gridview selected item value to a different ViewModel of different Usercontrol
Essentially setting up a dependency property which allows data from your parent view to persist to your child user control. Abstracting your view into specific user controls and hooking them using dependency properties along with the MVVM pattern is actually quite powerful and recommended for Silverlight/WPF development, especially when unit testing comes into play. Let me know if you'd like any more clarification, hope this helps.

WPF + MVVM - How to bind a property to the parent view's data context

Working with the MVVM pattern, I have a pair of view model classes that represent a two-tier data hierarchy, each with a corresponding UserControl that represents its view. Both view model classes implement INotifyPropertyChanged and the root level view model exposes a property that is relevant to both its own view and the child view.
The root level view acquires the root level view model as its data context and explicitly assigns a data context to its contained view. However, it also needs to bind one of the properties of the child view to the above-mentioned shared property. Here is how I have attempted to achieve this, but it's not working:
<UserControl x:Name="rootView">
<StackPanel>
<!-- other controls here -->
<my:ChildView
DataContext="{Binding Path=SelectedChild}"
EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
</StackPanel>
</UserControl>
Although there are no runtime binding errors and the child view correctly binds to the appropriate child view model instance, its EditingMode property is never set. I have run tests to verify that the corresponding view model property is being modified and that it is notifying this change via INotifyPropertyChanged, but the binding fails to detect it.
Is there a better way to declare this binding or have I made a more basic architectural error?
Many thanks for your advice,
Tim
Update: As requested, I am posting some code to show a very simplified version of my views and view models, together with the results of an experiment that I have conducted that may provide some additional clues.
// The relevant parts of the ParentViewModel class
public class ParentViewModel : INotifyPropertyChanged
{
// Although not shown, the following properties
// correctly participate in INotifyPropertyChanged
public ChildViewModel SelectedChild { get; private set; }
public ContentEditingMode EditingMode { get; private set; }
}
// The relevant parts of the ChildViewModel class
public class ChildViewModel : INotifyPropertyChanged
{
// No properties of ChildViewModel affect this issue.
}
// The relevant parts of the ParentView class
public partial class ParentView : UserControl
{
// No properties of ParentView affect this issue.
}
// The relevant members of the ChildView class
public partial class ChildView : UserControl
{
public static readonly DependencyProperty EditingModeProperty =
DependencyProperty.Register(
"EditingMode",
typeof(ContentEditingMode),
typeof(PostView)
);
public ContentEditingMode EditingMode
{
get { return (ContentEditingMode)GetValue(EditingModeProperty); }
set { SetValue(EditingModeProperty, value); }
}
}
// The enumeration used for the EditingMode property
public enum ContentEditingMode
{
Html,
WYSYWIG
}
My intention is that the DataContext of the parent view instance will be assigned an instance of ParentViewModel and it will, in turn, assign the value of its SelectedChild property to the DataContext of the nested ChildView. All of this seems to work correctly, but the problem arises because the binding between ParentViewModel.EditingMode and ChildView.EditingMode does not work.
In an attempt to test whether there is a problem with my binding expression, I introduced a TextBlock adjacent to the ChildView and bound it similarly to the ParentViewModel.EditingMode property:
<UserControl x:Name="rootView">
<StackPanel>
<!-- other controls here -->
<TextBlock Text="{Binding ElementName=rootView, Path=DataContext.EditingMode}" />
<my:ChildView
DataContext="{Binding Path=SelectedChild}"
EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
</StackPanel>
</UserControl>
In this test, the TextBlock is correctly updated every time the source property changes. However, if I set a breakpoint on the setter of ChildView.EditingMode, it never gets hit.
I'm baffled !
The simplest way to fix this is in your view model. Implement an EditingMode property in the child view model and bind to it. That way, you don't have to make any kind of guesses about what the right way to establish the binding might be; also, it's something that you can test outside of the UI.
Edit
Actually the right solution is not quite as simple, but it's worth knowing how to do.
What you want is for EditingMode in the child control to efficiently inherit its value from the parent control. Does that sound like something that anything else in WPF does? Like just about every framework element that implements dependency properties?
Implement EditingMode as a dependency property in both the parent and child UserControls and use property value inheritance, as described here. That takes the inheritance behavior out of the view model entirely and puts it where it belongs.
See if you can just use a full path to get the editing mode of the selected child:
<my:childView
DataContext="{Binding SelectedChild}"
EditingMode="{Binding SelectedChild.EditingMode />

WPF Exposing a calculated property for binding (as DependencyProperty)

I have a complex WPF control that for some reasons (ie. performance) is not using dependency properties but simple C# properties (at least at the top level these are exposed as properties).
The goal is to make it possible to bind to some of those top level properties -> I guess I should declare them as DPs.(right? or is there some other way to achieve this? )
I started reading on MSDN about DependencyProperties and DependencyObjects and found an example:
public class MyStateControl : ButtonBase
{
public MyStateControl() : base() { }
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
"State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));
}
If I'm right - this code enforces the property to be backed up by DependencyProperty which restricts it to be a simple property with a store(from functional point of view, not technically) instead of being able to calculate the property value each time getter is called and setting other properties/fields each time setter is called.
What can I do about that? Is there any way I could make those two worlds meet at some point?
//edit
I guess I have to tell you a little more about what I want to do and what my limitations are. So:
I have TabControl that is bound to a collection of ViewModel(I'm using MVVM pattern) objects. Every tab is meant to be an editor for one object of that collection. Objects can be of different types so I have multiple definitions each with a different DataType property. Now I have that complex WPF Control that I want to use as a part of one of those DataTemplates. If I use usual TextBox I can simply bind to its Text property, but I can't do the same with Text property of that custom control simply because its Text property is not a dependency property.
In this scenario I have :
no direct access to the control itself nor to its events
no code behind file that I can use to do that kind of thing
I can see however a dirty solution -
In the Window class I would have to subscribe to CollectionChanged event of the collection that is bound to the TabControl.
Whenever an item is added to that collection use ItemContainerGenerator to obtain a copy of I suppose TabItem and use it to find the right copy of 'complex control'
Regiter items handlers to 'complex controls' events to do the job
This is wrong because:
this is agains MVVM - I have to play with tab control to do the job instead of doing it in the ViewModel class
this couples in an unwanted way the view and viewmodel
I think you are mixing up Dependency Properties and implementing INotifyPropertyChanged on your classes.
You don't need your property to be a dependency property, you just need your class to implement INotifyPropertyChanged and call OnPropertyChanged whenever the state of your object changes in a way that would affect the value you want to expose to binding.
So let's say you have a property Sum that you want to bind to. The Sum property simple adds two other properties (or fields, or whatever) together. When anything happens that affects the Sum calculation, you want to notify that the Sum value has changed, so the any controls bound to Sum get updated.
public int Sum => Value1 + Value2;
public int Value1
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value1 = value;
OnPropertyChanged("Sum");
}
}
public int Value2
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value2 = value;
OnPropertyChanged("Sum");
}
}
It seems to me that you've been saddled with a WPF user control that was built by someone who didn't intend it to be used with data binding. I would assume that this is for one of two reasons: a) there's some logical reason that you shouldn't be able to bind to this property, or b) the original author of this control didn't know what he was doing. You haven't provided enough information for me to know which of those two conditions is the one you're really working under.
But in general, the reason you can't expose calculated properties for binding is that calculated properties generally don't have a setter. It doesn't make sense to set a property whose value is calculated.
If there are other properties whose values need to be updated when this one changes, the right approach (or at least the one consonant with the design of dependency properties) is to handle those updates in the dependency property's callback function, which is kind of what the callback function is for.

Resources