I'm still trying to wrap my head around some of the finer points of using WPF and MVVM. One thing that has bugged me for a while is how to get values from the View into the ViewModel. Let me explain the scenario.
I have a Window with several UserControls inside. One of those UserControls has a DependencyProperty of type TimeSpan named CurrentTime. It is bound (in the XAML for the window) to the Window's ViewModel.
Now, I also have a ViewModel for the UserControl. It needs access to the CurrentTime property in order to create objects based off it. How do I get that value into my ViewModel? I see a couple of options:
The simplest is probably just to pass the View into the ViewModel's constructor (or somehow otherwise reference the View from the ViewModel). But that seems so wrong and not WPF/MVVMy.
I could use messaging to actually get the CurrentTime from the other ViewModel. This would certainly work, but seems like overkill.
I could set up a binding in the XAML of the UserControl to bind, OneWayToSource, between the DP and the property in the ViewModel. I can't figure out exactly how to set that up (how do you reference a UserControl's new properties in its own XAML?), but it seems like it could work.
Am I missing any other options? Which of the above (or ones I missed) is the best way to do this?
I read through this thread here on SO, because it was similar, but the answers didn't really answer my question - I would love to do it with bindings or commands or something, but I just can't figure out how exactly.
The third option is the way to go here, but you'll have to specify binding in the code-behind of your UserControl:
public partial class MyUserControl : UserControl
{
public MyUserControl() {
InitializeComponent();
SetBinding(MyPropertyProperty,
new Binding {Path = new PropertyPath("MyViewModelProperty"), Mode = BindingMode.OneWayToSource});
}
}
Related
I'm new to WPF and I'm attempting to incorporate MVVM design pattern into my projects. In all the MVVM examples I've seen, the MainWindow.xaml.cs is only used to set the DataContext to the view model.
this.DataContext = viewModel;
Everything is very neat and decoupled away from the UI. events were also replaced with commands. There are two questions that I have regarding this.
I'm wondering about how you are supposed to hook up controls that don't have the command property.
What am I supposed to do when I would typically interact directly with a control e.g. Perhaps I want to set a combobox's index to -1. How am I supposed to do this on the view model?
The collected comments by #EdPlunkett, #Clemens and #BionicCode answered my questions.
To Summarise:
I can interact with controls by binding to their properties through INotificationChanged and ObservableCollection
Elements that don't have a command property can still have properties bound to an ICommand property in the viewmodel.
I am having a really weird problem. I am trying to bind a property to the DataContext but it is not working. This is what I am doing (in the Window.Resources section):
<myNS:MyClass x:Key="myObj" MyProp="{Binding}"/>
Elsewhere in the code, I set the data context like this:
myWindow.DataContext = MyNameSpace.MySingleton.Instance;
I didn't get any errors, but the binding didn't happen. So I added a Debug converter to see if I could figure out what was going on:
<myNS:MyClass x:Key="myObj" MyProp="{Binding Converter={StaticResource Debug}}"/>
I set a breakpoint in the converter and the value being passed was null. Figuring that things were out of order, I set a breakpoint on the line that sets the DataContext. It was hit first, then the breakpoint in the converter. So the DataContext is being set before the binding occurs.
Finally, to try to get something to work, I changed to this:
<myNS:MyClass x:Key="myObj" MyProp="{Binding Source={x:Static myNS:MySingleton.Instance}}"/>
That worked.
I really don't like spreading out the bindings like this. I would rather just bind to the DataContext. The window in question contains many bindings to properties on the DataContext and these all work fine.
Can anyone explain what I am doing wrong here?
JAB
Resources, as well as some other elements like Context Menus are not part of the visual tree.
Thus, they have no governing FrameworkElement to get a data context from. Usually, a standard class won't take advantage of the binding syntax, as it requires deriving from DependencyObject, but if you do end up needing a binding in a resource (say, for a converter) you can use this trick:
Set your root element to have x:Name="Root"
Use your bindings like this:
MyProp="{Binding Source={x:Reference Root}, Path=DataContext.<YourProp>
This binds using the root framework element as the "starting point" and you can get to the data context normally.
What an idiot I am!
I started to respond to #BradleyDotNET (thanks for the response, by the way. It is what helped me solve the problem), and figured out the solution. My class DOES derive from FrameworkElement. I did that because I needed to make use of data binding, even though it has no visible component.
I posted another question dealing with how to instantiate an object declared in the resource section. I would still like to know the answer to that, but since my class derives from FrameworkElement, I don't need to declare it in the resource section; I can put it directly in the tree. This causes it to be instantiated AND inherit the DataContext.
I have a UserControl where the data gets passed in via a dependency property.
The UserControl is backed by a view model and the data is assigned to a property of the view model.
The XAML binds to both properties in the view model as well as properties within the passed in data.
The problem is if the user changes the data then databinding with the UI breaks. The UI is still bound to the original data object.
How can I cause the binding to get refreshed? INotifyPropertyChanged is implemented throughout, but it is not the property that is getting stale, but the binding. Basically, how do you go about replacing a backing model?
Since I'm not sure my description is clear I will try to describe it again in pseudo code.
<MyControl Source="{Binding Data}"/>
Where source is a dependency property of MyControl. In the PropertyChangedCallback this data is handed to the view model.
MyViewModel.Data = Source;
Within the MyControl XAML things are bound to this model.
{Binding Path=MyViewModel.Data.Item}
If you are wondering why the dependency property is defined in the UserControl, it is because it is a reusable control and the end user should not know about the view model.
"The UserControl is backed by a view model" and thus breaketh the application.
The UserControl's DataContext should be the Model. Period. If you need to perform UI logic, do it in the codebehind. Need something else? Create DependencyProperties on the surface of your UserControl to supply them.
When you create a ViewModel specifically for your UserControl, you break the natural flow of the DataContext (at this point, probably the Model the UC is designed to work with) and binding within the UserControl. It's pointless in most cases and harmful in some (as you have noticed).
My canonical answer on the subject contains more details.
Are you trying to swap-out the DataContext of the usercontrol during an application's session?
If you are, then I am fairly confident that this will not work.
Have you considered spinning up a new instance of that user control with the other DataConext?
I have a CustomControl B, which uses a DataContext/MVVM (viewModelB). Now I want to bind one Property of my CustomControl to another control A (uses viewModelA as DataContext).
So I have two Ideas:
Whenever PropA in viewModelA changes, I could directly update PropB in the viewModelB. But this creates a dependency between the viewModels, which seems ugly to me. Or is this a common way in the MVVM pattern and can't be avoided?
As an alternative I could think of a dependency property on CustomControlB and wire it to CustomControlA's viewModel by a binding, something like that:
<myControlB PropB={Binding ElementName=myControlA, Path=DataContext.PropA} />.
So far so good, but the dependency property is defined on the view now. How should I visualize it?
a) Should I transfer the value (from the property wrapper) to viewModelB and bind to it from viewB's XAML code?
b) Or should I directly update the view from B's codeBehind? Would this be still a proper MVVM "style"?
Which of the options would you recommend?
regards
Andreas
As long as ViewModelA doesn't actively update ViewModelB, there is no real coupling between the two viewmodels. What I mean is that if your main view model (which knows both viewmodels) is the one that wires up the binding, the view models are still loosely coupled.
So to me any of these are fine:
Bind directly to myControlA.DataContext.PropA from XAML
Have the MainViewModel register for ViewModelA's property changed event and modify ViewModelB's property as necessary. Here MainViewModel knows about the two view models, but they know nothing of each other.
I've got an AllTopicsViewModel and its got a property ExerciseVM which is an AllExerciseViewModel, since I want to be able to refresh the AllExerciseViewModel of an ExerciseView so I am doing it like this (not even sure if it violates MVVM, pls. tell me). Well, I want to convert the 2 lines following the InitializeComponent to XAML but not sure how, can anyone help me out?
public MainWindow()
{
InitializeComponent();
AllTopicsViewModel vm = (AllTopicsViewModel)topicsView.DataContext;
vm.ExerciseVM = (AllExercisesViewModel)exercisesView.DataContext;
}
Yes, this is a misconception, according to the idea of MVVM.
Ideally, your View's codebehind (view.xaml.cs) contains nothing more than the auto generated code. Your view only accesses the ViewModel via WPF's data binding mechanisms. Because data binding via WPF is a loose coupling between the binding view and the bound-to ViewModel, you achieve the seperation that drives people to use MVVM.
You are retrieving the ViewModel in the Views codebehind from your control's DataContexts. With this, you create a strong reference between View and ViewModel. So, to help you with your question: You should think about what you are trying to to do with your ViewModel in the View's codebehind and how you can do it differently, either in the view's XAML or in the ViewModel's code itself.
If you like, post the complete MainWindow() class for some advice...
EDIT:
Ok, so its just about setting the child ViewModel on the parent ViewModel. The parent ViewModel AllTopicsViewModel should be responsible for setting its own ExerciseVM on initialization. This is not the View's job. the parent viewModel should assemble the data from one or more models and then create the child view models which the view consumes. Does that make sense for you?