I'm new to both C# and WPF. I've written a simple program. I have a class called Counter, that exposes a read-only property Count that starts out at 0, and a public method Increment that simply increments the count by one. Counter implements INotifyPropertyChanged.
I have a Window class (code is below). I pass an instance of a Counter object to the constructor and perform a binding. The window has a button and a label. The label is bound to the counter's Count property, and the button calls Increment.
This all works.
However, most examples I've seen around the net and MSDN mostly deal with defining the binding in XAML. How can I modify my example here to move the binding operation out of code behind and into the markup? The Binding property in the Properties window of VS2010 doesn't seem to know how to do what I want. Perhaps it's not possible?
One additional question: I don't think this example fits MVVM... My Counter class stands alone, is not tied to a view anywhere except through its property. However, the CounterWindow class is holding a reference to it. Is this the proper location for this reference? I also though that perhaps I should be creating the window, then setting a property (e.g. CounterObject) that I would use instead of passing through via the constructor.
public partial class CounterWindow : Window {
Counter ctr;
public CounterWindow(Counter ctr) {
InitializeComponent();
this.ctr = ctr;
Binding b = new Binding("Count");
b.Source = ctr;
CounterLabel.SetBinding(Label.ContentProperty, b);
}
private void IncrementButton_Click(object sender, RoutedEventArgs e) {
ctr.Increment();
}
}
Something like this:
public CounterWindow(Counter ctr)
{
InitializeComponent();
DataContext = ctr;
}
Markup:
<Label Content="{Binding Count}" />
UPD.
There's two common approaches in MVVM: view-first and model-first.
View first means that you initially create the view, and then view creates view model, which it is bound to.
Model-first means that first you create the view model, then view model creates its view and passes itself (via constructor or via DataContext property setter) as data context of the view.
Hope this helps you.
Related
I'm got an other question for my WPF/MVVM application I'm working on since a while.
The main idea is to use a main window providing a navigation bar and a ContentControl.
The different "Modules" are all built as UserControl with each its own ViewModel.
The main call from the main viewmodel to start a module is
private void ShowAddressModule() {
ContentControlBindingProperty = new AddressModule(new AddressModuleViewModel);
}
In the real application the viewmodels are pre-loaded and so on, but the start is more or less the same.
The main view model contains a boolean property LongRunningOperation to do multiple operations on the main window while any long running operation.
As example showing a loading image or disable the main navigation while loading a new module or whatever.
So my idea is to provide a possibility to the modules (their view models) to active this "mode".
Example how it could look in the modules view model:
private void LoadContactList() {
MainWindow.LongRunningOperation = true;
LoadAllContactsInAThread(); /*Takes a long time*/
MainWindow.LongRunningOperation = false;
}
I tried to mark the property as static and public, but this will not work because of the OnPropertyChanged event.
If possible it would be great if the solution could be applied also to methods (including parameters) from the main window - so (as example) the modules could use as example the parents statusbar or so.
MainWindow.ShowErrorMessageInStatusBar("The error xyz occured!");
Hopefully I described good enought, what's my idea...
And hopefully anybody could provide me the needed tip how to handle this requirement.
Thanks in advance for any hints
Regards Markus
Each module could raise an event to indicate the start of a long running operation, and raise an event to indicate the end of a long running operation. Your main view model, when loading modules for the first time, could hook-up to these events and react to them accordingly.
Your sub view model would have some events like this:
Sub view model
public delegate void OnLongRunningOperationStartedEventHandler(object sender);
public delegate void OnLongRunningOperationFinishedEventHandler(object sender);
public event OnLongRunningOperationStartedEventHandler OnLongRunningOperationStarted;
public event OnLongRunningOperationFinishedEventHandler OnLongRunningOperationFinished;
private void LoadContactList() {
OnLongRunningOperationStarted?.Invoke(this);
LoadAllContactsInAThread(); /*Takes a long time*/
OnLongRunningOperationFinished.Invoke(this);
}
And your main view model will hook-up to them like this:
Main View Model
public bool LongRunningOperation { get; private set; }
// Keep track of the number of modules currently running long operations
private int _countLongRunningOperations = 0;
public LoadSubModules(){
// Depending on how you load your sub modules, this piece of code could move around
foreach (var module in submodules){
module.OnLongRunningOperationStarted += Module_LongOperationStarted;
module.OnLongRunningOperationFinished += Module_LongOperationFinished;
}
}
private void Module_LongOperationStarted(object sender){
_countLongRunningOperations += 1;
LongRunningOperation = true;
}
private void Module_LongOperationFinished(object sender){
_countLongRunningOperations -= 1;
if (_countLongRunningOperations == 0) {
LongRunningOperation = false;
}
The same principle (using events) could be used to bubble up error messages from each submodule to the main view model.
The quick and very dirty approach:
Grab a reference to mainwindow out of application.current.mainwindow. Cast it to MainWindow. It's a property set to whatever the first window you show is - MainWindow just happens to be the default name of the main window.
You can then set the property on that if it's a public dependency property. Make sure the dp binds twoway in it's metadata.
This is bad because you're referencing ui in your viewmodels and you have no application when you run tests on viewmodels in some test runner.
The quick and dirty approach
Add a public property to app and set this to your instance of mainwindowviewmodel in it's ctor. You can reference app from an piece of code. Add a public property to mainwindowviewmodel and bind to that.
This is bad because you have no application when you run tests on viewmodels in some test runner.
You could add a static with an interface abstracts this away and work round that though.
My suggestion
This hinges on the fact you can use dot notation to bind and that includes
Content.IsBusy on yourcontentcontrol.
You can therefore bind from a parent window to a dependency property of any usercontrol that happens to be in it's contentcontrol.
Add that property using an attached property and bind that to IsBusy in a base viewmodel. Inherit the viewmodels of your child views from that.
One thing to mention is that binding to an attached property is a little odd and rather than just
ElementName=YourContentControl, Path=Content.YourAttachedProperty
You need something like:
ElementName=YourContentControl, Path=Content.(local:AttachClass.YourAttachedProperty)
I am a C++ developer and new to WPF and MVVM. please bear with me if I choose any wrong word to ask my question
I have my Main application in MFC/C++ which is passing some data to C# library(CLI is used as middle layer).
In C# library, there is a section of code where a dialog is opened , data is filled and user selection is notified to the calling object in below way -
public classA()
{
MyDialog dlg = new MyDialog(param1, param2, param3)
if(dlg.ShowDialog().GetValueOrDefault())
{
var name = dlg.name;
var roll = dlg.roll;
}
else
{
var name = string.Empty;
var roll = string.Empty;
}
}
Now Dialog has been modified and implemented using MVVM pattern.
I have created below files as part of implementation-
1
MyDialogView.Xaml
MyDialogView.xaml.cs
MyDialogViewModel.cs
MyDialogModel.cs
My question is, how to instantiate the new dialog now from my classA so that data is filled using the parameters passed to dialog in same way as previously it was doing and record user selection without loosing any data and safely closing the view.
Standard MVVM approach works like this (at least when using MVVM Light):
You have a VM layer, a Class Library.
You have a View layer, a WPF Controls Library or WPF Application.
View layer adds reference to VM layer. VM layer doesn't know anything about View.
You create a normal public class for your dialog's VM. Call it DialogVM or whatever. Make sure this class inherits from MVVM Light's built-in ViewModelBase. This will get you access to change notification methods provided by MVVM Light. Might look like this in your case:
public partial class DialogVM : ViewModelBase
{
private string _Name;
public string Name
{
get { return _Name; }
set { Set(ref _Name, value); }
}
private string _Roll;
public string Roll
{
get { return _Roll; }
set { Set(ref _Roll, value); }
}
}
VM layer has a global static class called ViewModelLocator. This class performs IoC/DI and provides public static properties to expose different VMs. (In your case your dialog's VM goes to the VM project and the ViewModelLocator looks something like this:
using System;
namespace VMLayer
{
public class ViewModelLocator
{
static ViewModelLocator()
{
SimpleIoc.Default.Register<DialogVM>(true);
}
public static DialogVM MyDialog => SimpleIoc.Default.GetInstance<DialogVM>();
}
}
Your dialog box (a Window) goes to View layer and uses this exposed property MyDialog to provide a DataContext for the dialog:
<Window x:Class="GlasshouseTestingShell.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:vm="clr-namespace:VMLayer;assembly=VMLayer"
DataContext="{x:Static vm:ViewModelLocator.MyDialog}"
d:DataContext="{d:DesignInstance Type=vm:DialogVM}">
</Window>
Look how cleanly we have created View layer's DataContext without writing a line of C# code in the View layer. This is also elegant in the sense that you get all design-time Intellisense in Binding expressions and elsewhere.
You now bind all your UI stuff (textboxes, buttons etc.) to the public properties and commands exposed by your dialog's VM. Still no lines in the code-behind. Might look like this in your case:
<TextBox Text="{Binding Name}" />
Rest of the stuff is in C++:
You add reference to your View and VM DLLs in your C++ project.
Create an object of your dialog. It will automatically instantiate its VM and perform binding. You call ShowDialog() to bring it to screen.
Use takes actions in the dialog and finally presses OK or Cancel.
You capture dialog result and then access your dialog object's DataContext property, which is an object of DialogVM class. You can access user-supplied values from therein as Binding has updated those properties for you in the VM.
I'm not sure I follow all of your requirements but this is roughly how I'd approach such a task:
Instantiate the view and viewmodel in class A.
Set whatever parameters you want on your viewmodel. Either as properties or via constructor injection.
Set the datacontext of the view to the viewmodel.
Everything you need to bind should then bind between them.
showdialog the view.
The user edits in the view and changes persist to the viewmodel properties.
They finish editing and you then work with the viewmodel properties. Maybe one of them is the model you mention. Maybe the model is instantiated by the viewmodel to get data or by classA if that is more convenient. In the latter case you probably have to pass that model to the viewmodel.
Bearing in mind the above.
Some rough code:
public class ClassA
{
MyDialogViewModel vm = new MyDialogViewModel { Name = "X", Roll = "Y" };
MyDialog dlg = new MyDialog();
dlg.ShowDialog();
var name = vm.Name;
var roll = vm.roll;
// Do something to persist your data as necessary. Either here or in a model within the viewmodel
}
Name and Roll presumably bind to some textboxes Text properties in the view or some such.
If it's as simple as obtaining two string values then I see no advantage to actually having a model at all. On the other hand, if processing is more involved then of course the viewmodel might instantiate a model.
MyDialogViewModel should implement inotifypropertychanged and anything you need to bind should be a public property. Not sure if you'll need propertychanged notification but always implement it. Optionally raise propertychanged from property setters.
x:Bind defaults to OneTime, which updates the target UI with the data when the Page's Loading event triggers the generated code's Initialize function.
I have a Page with a ViewModel property. This ViewModel class implements INPC for its properties. The data for the viewModel is loaded asynchronously, only after the page is loaded. So on Page initialization, and subsequently the generated code initialization, the UI target using x:Bind will have null data.
Since it is OneTime, it shouldn't change unless I manually call Update(which I don't).
So why does my x:Bind UI work?
The following is some simplified code snippets.
<Page x:Name="MyPage" x:Class="MyProject.Pages.MyPage">
<Button Command="{x:Bind ViewModel.GoToAnotherPageCommand}">
public sealed partial class MyPage : Page
{
public MyPageViewModel ViewModel { get; set; }
public MyPage()
{
this.InitializeComponent();
}
// called by an event bound to a Frame's Navigated, which all pages use
public void OnNavigatedTo()
{
this.ViewModel = new MyPageViewModel();
}
}
public class MyPageViewModel : INotifyPropertyChanged, INotifyPropertyChanging
{
// GoToAnotherPageCommand is an INPC property and its set in the constructor
The reason that your command works fine is because OnNavigatedTo will be called before the command instantiation. This means by the time the code tries to set the command, the ViewModel has already been instantiated and is no longer null.
To prove my point, first go open the file under the following path(could be ARM or *x64 depending on which platform you are running on) -
obj/x86/Debug/MyPage.g.cs
This is basically the code-generated file that hooks up all the x:Bind stuff for your page.
Now put a breakpoint at where the command is set. In my case, it's a method called Set_Windows_UI_Xaml_Controls_Primitives_ButtonBase_Command. Then put another breakpoint at OnNavigatedTo.
Now run the app, you will see that the OnNavigatedTo method gets called first.
If your page's NavigationCacheMode is set to Disabled, this behavior makes OnNavigatedTo the ideal place to instantiate x:Bind bindings so the page only uses memory to create these new objects when the user actually navigates to it, instead of doing everything inside the page constructor.
Don't do this inside the Loaded event of the Page though. Because it will get called after the command instantiation. You can try the following code to instantiate the ViewModel, and the result is very different(your command will not work).
public MyPage()
{
InitializeComponent();
Loaded += (s, e) => ViewModel = new MyPageViewModel();
}
The compiled binding system (x:Bind) is smart enough to check for initial null values and not consider them the actual value you wish to bind. It will wait for the first non-null value and bind that value.
This is by design, as binding to an initial null value is (almost) never the intention of the binding.
I didn't find the source of this information, but I believe it was in the Build talk detailing the x:Bind system in 2015.
Updated:
As Justin mentions in the comments below and in his own answer, the binding will not work if the view model is set after the binding operation happens.
I believe this is because the binding terminates when it encounter a null reference in the property chain, but I haven't tested this, so I might be incorrect.
I have a user control written in C# & WPF using the MVVM pattern.
All I want to do is have a property in the bound ViewModel exposed to outside of the control. I want to be able to bind to it and I want any changes to the property to be picked up by anything outside the control that is bound to the exposed value.
This sounds simple, but its making me pull out my hair (and there is not much of that left).
I have a dependency property in the user control. The ViewModel has the property implementing the INotifyPropertyChanged interface and is calling the PropertyChanged event correctly.
Some questions:
1) How do I pick up the changes to the ViewModel Property and tie it to the Dependency Property without breaking the MVVM separation? So far the only way I've managed to do this is to assign the ViewModels PropertyChanged Event in the Controls code behind, which is definitely not MVVM.
2) Using the above fudge, I can get the Dependency property to kick off its PropertyChangedCallback, but anything bound to it outside the control does not pick up the change.
There has to be a simple way to do all of this. Note that I've not posted any code here - I'm hoping not to influence the answers with my existing code. Also, you'd probably all laugh at it anyway...
Rob
OK, to clarify - code examples:
usercontrol code behind:
public static DependencyProperty NewRepositoryRunProperty = DependencyProperty.Register("NewRepositoryRun", typeof(int?), typeof(GroupTree),
new FrameworkPropertyMetadata( null, new PropertyChangedCallback(OnNewRepositoryRunChanged)));
public int? NewRepositoryRun
{
get { return (int?)GetValue(NewRepositoryRunProperty); }
set
{
SetValue(NewRepositoryRunProperty, value);
}
}
private static void OnNewRepositoryRunChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != e.NewValue)
{
}
}
public GroupTree()
{
InitializeComponent();
GroupTreeVM vm = new GroupTreeVM();
this.DataContext = vm;
}
Viewmodel (GroupTreeVM.cs)
private int? _NewRepositoryRun;
public int? NewRepositoryRun
{
get
{
return _NewRepositoryRun;
}
set
{
_NewRepositoryRun = value;
NotifyPropertyChanged();
}
}
And now for my weekly "don't do that" answer...
Creating a ViewModel for your UserControl is a code smell.
You're experiencing this issue because of that smell, and it should be an indication that you're doing something wrong.
The solution is to ditch the VM built for the UserControl. If it contains business logic, it should be moved to an appropriate location in another ViewModel.
You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.
Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.
Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.
Perhaps I've misunderstood, but it seems like you're trying to use binding in the code behind?
public MyUserControl()
{
InitializeComponent();
// Set your datacontext.
var binding = new Binding("SomeVMProperty");
binding.Source = this.DataContext;
SetBinding(MyDependencyProperty, binding);
}
I"m trying to wrap my head around MVVM. I understand a lot of it, but I'm having difficulty grasping one aspect: Setting DataContext.
I want to show an view with a particular object. The user doesn't get to decide what is visible, so I need to create the view in code. Then, I want to set the DataContext of the view to an object (for binding). I'm trying not to put code in the View for this, but View.LayoutRoot.DataContext isn't public.
What am I missing?
trying to avoid this:
public class View
{
public View(object dataContext)
{
InitializeComponent();
LayoutRoot.DataContext = dataContext;
}
}
with something like this:
public class ViewModel
{
...
public UIElement GetView()
{
UIElement *element = new View();
element.LayoutRoot.DataContext = element;
return element;
}
}
Don't forget that the View should know about the ViewModel, and not the other way around.
So in your case puting code in the ViewModel to create the view isn't the best way around.
Josh Smith's article on MVVM has a section on applying the View to the ViewModel. He recommends using WPF's DataTemplates to select your View in XAML.
If you use a XAML control or Window (which should be the case if you use MVVM), LayoutRoot (Grid by default) is public. In your example, you use just a plain class for View, so it is hard to tell what is going on.
Also, I second Cameron's opinion - nor View or ModelView should deal with assigning DataContext. It can be done in different ways (DataTemplate, dependency injection, special builder class, plain code) but normally it happens on the application level.