I'm trying to learn WPF/MVVM, and I'm currently working on how to switch between views. I've started by finding some example to study in the interntet. The one I'm using is quite simple: two views (named "Home and "Account") that only display a label, to keep the xaml and VM simple, and a main window with two buttons to switch between the views.
The DataTemplates are declared in the App.xaml file (together with the namespaces), so they should be global to the whole project:
<Application.Resources>
<DataTemplate DataType="{x:Type viewmodels:HomeViewModel}">
<views:HomeView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:AccountViewModel}">
<views:AccountView/>
</DataTemplate>
</Application.Resources>
The way I've understood it, the trick is done by a third VM (called MainViewModel.cs) that implements a SelectedViewModel attribute that keeps track of the VM that must be displayed, plus and ICommand bound to the buttons:
private BaseViewModel _selectedViewModel;
public BaseViewModel SelectedViewModel
{
get { return _selectedViewModel; }
set
{
_selectedViewModel = value;
OnPropertyChanged(nameof(SelectedViewModel));
}
}
public ICommand UpdateViewCommand { get; set; }
MainWindow.xaml looks like this:
<ContentControl Grid.Row="0" Content="{Binding SelectedViewModel}"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Margin="10" Width="200" Content="Home" Command="{Binding UpdateViewCommand}" CommandParameter="Home"/>
<Button Margin="10" Width="200" Content="Account" Command="{Binding UpdateViewCommand}" CommandParameter="Account"/>
</StackPanel>
In a separate class file (UpdateViewModel.cs) the UpdateViewModel class implements the Execute method as follows:
public void Execute (object parameter)
{
if (parameter.ToString() == "Home")
{
viewModel.SelectedViewModel = new HomeViewModel();
}
else if (parameter.ToString() == "Account")
{
viewModel.SelectedViewModel = new AccountViewModel();
}
}
I hope I've given the idea without boring you. It all works and lets me understand the basics. Now I wanted to try a variant, i.e. take one view (the "Account" one) and implement a button that would switch directly to the other view. I thought all I had to do was to bind the button to the UpdateViewModel class, and initially I modified the Account.xaml code as follows:
<Button Content="Button" Command="{Binding Path=UpdateViewCommand}" CommandParameter="Home"/>
The program runs, but when I click on the button in the Account view, nothing happens. So I changed this to something more complex:
<UserControl.DataContext>
<src:MainViewModel/>
</UserControl.DataContext>
...
<Button Content="Button" Command="{Binding Path=UpdateViewCommand}" CommandParameter="Home"/>
But the result is the same. I suspect it has to do with the binding, but can't see how to change it. Anybody can help?
Welcome to SO!
Your button bindings are done relative to the current DataContext, so binding to UpdateViewCommand in your AccountView (say) will try to bind to the UpdateViewCommand in your AccountViewModel, rather than your MainViewModel.
There are two ways to solve this. The first is to change your button bindings to bind to the parent's view model instead:
<Button Content="Button" Command="{Binding Path=DataContext.UpdateViewCommand, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}" CommandParameter="Home"/>
A major disadvantage here, of course, is that the visual hierarchy your child views reside in may not always be as predictable as this.
The second (better) way is to add UpdateViewCommand handlers to each of your child view models, and then have them pass control on to whatever you actually want to handle it. In practice you would typically create a base class for all your children, to reduce code duplication, and you would create a service (e.g. INavigationService) for them to call. Your MainViewModel (say) would then implement this interface, and you would use dependency injection to inject that reference into the child view models at their moment of creation (or just have the parent pass itself directly into their constructors, if you don't want to use a full DI framework).
I have the following xaml view:
<UserControl x:Class="MyViews.PersonView"
xmlns:views="clr-namespace:MyViews"
[...]
>
[...]
<dxb:BarManager x:Name="MainBarManager">
<dxb:BarManager.Items>
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
Command="{Binding PrintPersonsCommand}"
CommandParameter="{Binding PersonsCardView, ElementName=CardUserControl}"
/>
</dxb:BarManager.Items>
<Grid>
<Grid.RowDefinitions>
[...]
</Grid.RowDefinitions>
<views:CardView x:Name="CardUserControl" Grid.Row="2"/>
</Grid>
[...]
</UserControl>
The CardView is defined as follows:
<UserControl x:Class="MyViews.CardView"
[...]>
[...]
<dxg:GridControl ItemsSource="{Binding Persons}" SelectedItems="{Binding SelectedPersons}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionMode="MultipleRow">
[...]
<dxg:GridControl.View>
<dxg:CardView x:Name="PersonsCardView"
[...]
CardTemplate="{StaticResource DisplayCardTemplate}"
PrintCardViewItemTemplate="{StaticResource PrintCardTemplate}"/>
</dxg:GridControl.View>
[...]
</dxg:GridControl>
</UserControl>
The PrintPersonsCommand is defined as follows in my ViewModel:
public class PersonViewModel
{
public PersonViewModel(...)
{
[...]
PrintPersonsCommand = new Prism.Commands.DelegateCommand<DataViewBase>(PrintPersons, CanPrintPersons);
}
public Prism.Commands.DelegateCommand<DataViewBase> PrintPersonsCommand { get; private set; }
private void PrintPersons(DataViewBase view)
{
_printService.ShowGridViewPrintPreview(view);
}
private bool CanPrintPersons(DataViewBase view)
{
return true;
}
}
Now, when I click the Print button, the above PrintPersons method is always fed with null. How do I pass CardUserControl.PersonsCardView in my MyViews.PersonView xaml above, how do I pass that PersonCardView to my command? In other words, how do I fix
CommandParameter="{Binding PersonsCardView, ElementName=CardUserControl}"
to make it work?
Currently, the only solution I've found to this problem is to replace the Command and CommandParameter with
ItemClick="OnPrintBtnClick"
and then in the PersonView's code-behind file to do:
private void OnPrintBtnClick(object sender, ItemClickEventArgs e)
{
var ctxt = DataContext as PersonViewModel;
ctxt.PrintPersonsCommand.Execute(CardUserControl.PersonsCardView);
}
That works but I can't believe there is no other way. I'm not happy with that solution because I don't have the benefits of using the Command any more, like e.g. the automatic evaluation of the Command's CanExecute method. I could also put the CardView's xaml code in the PersonView.xaml but I like my controls to be in separate files because I have the feeling it's more structured and each user control has its own responsibilities which can nicely be split into separate files. Also, that solution binds my view to my view model too tightly.
Can someone help me out please?
Without changing your existing view and viewmodel hierarchy, I was able to pass the GridControl.View to the PersonViewModel using the Tag property
You can assign the CardView to the Tag property at the bottom of your CardView UserControl, and then access this Tag as CommandParameter.
CardView UserControl
<UserControl x:Class="MyViews.CardView"
[...]>
[...]
<dxg:GridControl ItemsSource="{Binding Persons}" SelectedItems="{Binding SelectedPersons}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionMode="MultipleRow">
[...]
<dxg:GridControl.View>
<dxg:CardView x:Name="PersonsCardView"
[...]
CardTemplate="{StaticResource DisplayCardTemplate}"
PrintCardViewItemTemplate="{StaticResource PrintCardTemplate}"/>
</dxg:GridControl.View>
[...]
</dxg:GridControl>
<UserControl.Tag>
<Binding ElementName="PersonsCardView"/>
</UserControl.Tag>
</UserControl>
Print Button Xaml:
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
Command="{Binding PrintPersonsCommand}"
CommandParameter="{Binding ElementName=CardUserControl, Path=Tag}"
/>
Based on the valuable input of Insane, I came up with the following two cleaner fixes:
Code-behind solution
In the PersonView, use the ItemClick event handler on the Print button:
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
ItemClick="OnPrintBtnClick"/>
Adapt the corresponding code-behind file like this:
public partial class PersonView : UserControl
{
readonly IPrintService _printService;
public PersonView(IPrintService printService)
{
_printService = printService;
InitializeComponent();
}
private void OnPrintBtnClick(object sender, ItemClickEventArgs e)
{
_printService.ShowGridViewPrintPreview(CardUserControl.PersonsCardView);
}
}
Because I want to gray-out the Print button when there is no selection, I still need to add some code to make that happen. I can get it by
1. updating the button code to
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
ItemClick="OnPrintBtnClick" IsEnabled="{Binding CanPrintPersons}"/>
refreshing the CanPrintPersons property in the PersonViewModel upon Persons selection change
That's it.
CardViewModel solution
In that solution, we have a PersonView with its underlying PersonViewModel and a CardView with its underlying CardViewModel. I will not describe that solution with all the details as it is overkill in my situation but for the sake of completeness, I'll give the main points. Upon clicking the Print button on the PersonView, the PersonViewModel's PrintCommand is called. That command emits a Print event to the CardViewModel which in turn calls its own PrintCommand. That latter command calls
_printService.ShowGridViewPrintPreview(View);
where the View is a CardViewModel's property that is set upon CardView loading with e.g.
<dxmvvm:Interaction.Behaviors>
<dxmvvm:EventToCommand EventName="Loaded" Command="{Binding ViewLoadedCommand}" CommandParameter="{Binding ElementName=PersonsCardView}" />
</dxmvvm:Interaction.Behaviors>
Because I have two child views I want to print, I'd need to add a view model for each one of those. In addition, those two view models plus the PersonViewModel need access to the list of Persons to be printed. In particular, they need a shared access to the same data, so that they are synchronized. A simple way to do that is explained here and is totally doable. But I think it is not worth the trouble for the simple use case I have as it adds more complexity than necessary.
I have one class with a xaml which contains a label. I want to change content of this label from one different class.
for example i have a class mainwindow.xaml.cs with mainwindow.xaml and i want to handle the label of mainwindow.xaml from the newclass.cs.
How can i do this??
edit: i have this label in a grid and i want to change the content from another class:
<Label Content="" Panel.ZIndex="1" FontWeight="SemiBold" FontSize="16px" Name="lb1" Margin="0,0,0,0" VerticalAlignment="Bottom" Height="30" HorizontalAlignment="Right" Width="250" HorizontalContentAlignment="Right" VerticalContentAlignment="Top"/>
What I would do is something like this, I'm not sure if it's the most logical thing to do but it works for me.
In your newclass.cs :
Class Newclass
{
MainWindow main;
public Newclass(MainWindow win)
{
main = win;
main.label.content = "";
}
}
and then in your mainwindow.xaml.cs:
Newclass class = new Newclass(this);
Data binding and MVVM would be the most elegant solution.
But you can simply use code-behind.
Give the label a name <Label x:Name="myLabel"> so you can access it in your code with that name like any other variable.
You can then pass this variable to your newclass.cs and change its properties there.
you can use binding - or even better binding with MVVM pattern and viewmodel first.
but nevertheless, when asking a question you should post some code
<Button x:Name="btn_binding" Content="Binding" HorizontalAlignment="Right" Height="44" Margin="0,127,63,0" VerticalAlignment="Top" Width="67"/>
<TextBox x:Name="txt_binding" Text="{Binding Content,ElementName=btn_binding}" Height="48" Margin="0,48,31,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Right" Width="130"/>
it's result will be like this
Then i can get the same result by the below code
public partial class biding : Window
{
public biding()
{
this.InitializeComponent();
txt_binding.Text=btn_binding.Content.ToString();
}
}
please tell me what are the differences of both and i want to which one is best to use...
In first case you bind your TextBox.Text property to Button.Content property and it will be changed everytime the Content is changed. In second you just set Text property once in constructor and changing the Button.Content won't affect into it.
Hope it's clear.
In my point of view... Xaml binding will be reflected in the designer at the time when you typed... No need to wait to run the solution to see the output..
In case of code behind .. you need to run the solution to see the results..
Being new to WPF, and its apparently amazing ability to change, bind, enable, and otherwise manipulate. I'm trying to get a mental overview of what is happening and hope some can either confirm or correct my readings.
Before WPF, you have delegates and events. You could have a dozen controls all listening (via being registered to the event), so when the event fires, all other controls will be notified automatically and can act on however they were so coded. Such as...
From Code Behind, you would do something like
GotFocus += MyMethodToDoSomething;
Then, the signature method
private void MyMethodToDoSomething(object sender, RoutedEventArgs e)
{
.. do whatever
}
Additionally, by using standard getter / setter, the setter can call its own methods in its own class to do something every time someone tries to get or set a value
private int someValue;
public int SomeValue
{
get { this.DoSomeOtherThing();
return someValue;
}
set { this.DoAnotherThing();
someValue = value;
}
Now, there's dependency properties, and the one/two-way binding. I understand (I think) about one-way to simulate more of a read-only operation.
Anyhow, with two way binding, the dependencies automatically notify anyone "depending" on a change in either the source or target respectively, without an explicit check if something has subscribed to an event, the framework automatically handles the announcing of the change to the respective controls (target or source).
So, let me through this scenario out with an old Add/Edit Save/Cancel maintenance form.
In an older framework, if someone clicked on an add or edit button, all the data entry fields would become "enabled" with either blank data for a new record, or editing existing data. At the same time, the add/edit buttons would become disabled, but the Save/Cancel buttons would now become enabled.
Likewise when finished via Save/Cancel, it would disable all the entry fields, save/cancel, and re-enable the Add/Edit buttons.
I don't quite understand how such this type of scenario would be handled under this dependency property scenario (yet), but am I close? I also understand you can bind to almost anything, including color schemes, show/hide, fonts, etc... But I'm taking small steps on trying to really grasp this stuff.
Thanks.
The getter/setter stuff is a feature of regular C# properties. It isn't unique to WPF.
This one-way/two-way stuff is talking about WPF data binding, which doesn't require you to create Dependency Properties - just to use them.
Dependency properties are built into controls themselves. They let you directly reference those properties when adding instances of your control to the form. They allow your custom control to feel a bit more "native".
Generally they are used to implement a property that can use data binding. In your apps, you'll mostly just use data binding, rather than implement new hooks for it.
... if someone clicked on an add or edit button, all the data entry fields would become "enabled" with either blank data for a new record, or editing existing data. At the same time, the add/edit buttons would become disabled, but the Save/Cancel buttons would now become enabled.
Likewise when finished via Save/Cancel, it would disable all the entry fields, save/cancel, and re-enable the Add/Edit buttons.
I would accomplish what you want to accomplish with:
A view model
Data binding on the view to that view model
Exposing ICommand on that view model (for buttons)
INotifyPropertyChanged on the view model (for all properties)
No new dependency properties need to be created for this scenario. You'll just use existing ones to do data binding.
Here's a code sample/tutorial of doing WPF with data binding and MVVM style.
Setting up the project
I created a WPF application in the New Project wizard, and named it MyProject.
I set up my project name and namespaces to match the generally accepted scheme of things. You should set these properties in solution explorer -> project -> right click -> properties.
I also have a custom folder scheme I like to use for WPF projects:
I stuck the view in its own "View" folder for organizational purposes. This is also reflected in the namespace, since your namespaces should match your folders (namespace MyCompany.MyProject.View).
I also edited AssemblyInfo.cs, and cleaned up my assembly References and app config, but that is just some tedium that I'll leave as an exercise for the reader :)
Creating a view
Start off in the designer, and get everything looking nice. Don't add any code behind, or do any other work yet. Just play around in the designer until things look right (especially when you resize). Here's what I ended up with:
View/EntryView.xaml:
<Window x:Class="MyCompany.MyProject.View.EntryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Entry View" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Text="Test 1" Grid.Row="0" />
<TextBox Text="Test 2" Grid.Row="1" Margin="0,6,0,0" />
<TextBox Text="Test 3" Grid.Row="2" Margin="0,6,0,0" />
<TextBox Text="Test 4" Grid.Row="3" Margin="0,6,0,0" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="Edit" IsEnabled="True" Grid.Column="0"
HorizontalAlignment="Left" Width="75" />
<Button Content="Save" IsEnabled="False" Grid.Column="1"
Width="75" />
<Button Content="Cancel" IsEnabled="False" Grid.Column="2"
Width="75" Margin="6,0,0,0" />
</Grid>
</Grid>
</Window>
View/EntryView.xaml.cs:
using System.Windows;
namespace MyCompany.MyProject.View
{
public partial class EntryView : Window
{
public EntryView()
{
InitializeComponent();
}
}
}
I didn't create any Name properties on these controls. That is on purpose. I am going to use MVVM, and won't use any code behind. I'll let the designer do what it wants to do, but I won't touch any of that code.
Creating a view model
Next I will make my view model. This should be designed in a way that it services the view, but could ideally be view independent. I won't worry about that too much, but the point is you don't have to have a 1-to-1 parity of view controls and view model objects.
I try to make my views/view models make sense in a bigger app context, so I'll start purposing the view model here. We'll make this "editable form" a rolodex entry.
We'll create a helper class that we need first...
ViewModel/DelegateCommand.cs:
using System;
using System.Windows.Input;
namespace MyCompany.MyProject.ViewModel
{
public class DelegateCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public DelegateCommand(Action execute)
: this(execute, CanAlwaysExecute)
{
}
public DelegateCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
if (canExecute == null)
throw new ArgumentNullException("canExecute");
_execute = o => execute();
_canExecute = o => canExecute();
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
private static bool CanAlwaysExecute()
{
return true;
}
}
}
ViewModel/EntryViewModel.cs:
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace MyCompany.MyProject.ViewModel
{
public class EntryViewModel : INotifyPropertyChanged
{
private readonly string _initialName;
private readonly string _initialEmail;
private readonly string _initialPhoneNumber;
private readonly string _initialRelationship;
private string _name;
private string _email;
private string _phoneNumber;
private string _relationship;
private bool _isInEditMode;
private readonly DelegateCommand _makeEditableOrRevertCommand;
private readonly DelegateCommand _saveCommand;
private readonly DelegateCommand _cancelCommand;
public EntryViewModel(string initialNamename, string email,
string phoneNumber, string relationship)
{
_isInEditMode = false;
_name = _initialName = initialNamename;
_email = _initialEmail = email;
_phoneNumber = _initialPhoneNumber = phoneNumber;
_relationship = _initialRelationship = relationship;
MakeEditableOrRevertCommand = _makeEditableOrRevertCommand =
new DelegateCommand(MakeEditableOrRevert, CanEditOrRevert);
SaveCommand = _saveCommand =
new DelegateCommand(Save, CanSave);
CancelCommand = _cancelCommand =
new DelegateCommand(Cancel, CanCancel);
}
public string Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
public string Email
{
get { return _email; }
set
{
_email = value;
RaisePropertyChanged("Email");
}
}
public string PhoneNumber
{
get { return _phoneNumber; }
set
{
_phoneNumber = value;
RaisePropertyChanged("PhoneNumber");
}
}
public string Relationship
{
get { return _relationship; }
set
{
_relationship = value;
RaisePropertyChanged("Relationship");
}
}
public bool IsInEditMode
{
get { return _isInEditMode; }
private set
{
_isInEditMode = value;
RaisePropertyChanged("IsInEditMode");
RaisePropertyChanged("CurrentEditModeName");
_makeEditableOrRevertCommand.RaiseCanExecuteChanged();
_saveCommand.RaiseCanExecuteChanged();
_cancelCommand.RaiseCanExecuteChanged();
}
}
public string CurrentEditModeName
{
get { return IsInEditMode ? "Revert" : "Edit"; }
}
public ICommand MakeEditableOrRevertCommand { get; private set; }
public ICommand SaveCommand { get; private set; }
public ICommand CancelCommand { get; private set; }
private void MakeEditableOrRevert()
{
if (IsInEditMode)
{
// Revert
Name = _initialName;
Email = _initialEmail;
PhoneNumber = _initialPhoneNumber;
Relationship = _initialRelationship;
}
IsInEditMode = !IsInEditMode; // Toggle the setting
}
private bool CanEditOrRevert()
{
return true;
}
private void Save()
{
AssertEditMode(isInEditMode: true);
IsInEditMode = false;
// Todo: Save to file here, and trigger close...
}
private bool CanSave()
{
return IsInEditMode;
}
private void Cancel()
{
AssertEditMode(isInEditMode: true);
IsInEditMode = false;
// Todo: Trigger close form...
}
private bool CanCancel()
{
return IsInEditMode;
}
private void AssertEditMode(bool isInEditMode)
{
if (isInEditMode != IsInEditMode)
throw new InvalidOperationException();
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged Members
}
}
As is usual for this type of workflow, there are some requirements I missed when initially creating the view. For example, I figured out that it would make sense to have a "revert" feature that undoes the changes, but keeps the dialog open. I also figured out that I could reuse the edit button for this purpose. So I made a property that I will read to get the edit button's name.
The view model contains a lot of code to do something simple, but most of it is boilerplate for hooking up the properties. This boilerplate gives you some power, though. It helps isolate you from your view, so your view can change drastically with no changes or only minor changes to the view model.
If the view model gets too big, you can start pushing it into a additional sub view models. Create them wherever makes the most sense, and return them as properties on this view model. The WPF data binding mechanism supports chaining down the data context. You'll find out about this data context a little later when we hook things up.
Hooking up the view to our view model
To hook up the view to a view model, you have to set the DataContext property on view to point to your view model.
Some people like to instantiate and specify the view model in the XAML code. While this can work, I like to keep the view and the view model independent of each other, so I make sure I use some third class to hook the two up.
Normally I'd use a dependency injection container to hook up all my code, which is a lot of work, but keeps all the parts independent. But for an app this simple, I like to use the App class to bind my stuff together. Let's go edit it:
App.xaml:
<Application x:Class="MyCompany.MyProject.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="ApplicationStartup">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs:
using System.Windows;
namespace MyCompany.MyProject
{
public partial class App : Application
{
private void ApplicationStartup(object sender, StartupEventArgs e)
{
// Todo: Somehow load initial data...
var viewModel = new ViewModel.EntryViewModel(
"some name", "some email", "some phone number",
"some relationship"
);
var view = new View.EntryView()
{
DataContext = viewModel
};
view.Show();
}
}
}
You can now run your project, though the logic we built won't do anything. This is because our initial view is created, but it doesn't actually do any data binding.
Setting up data binding
Lets go back and edit the view to finish hooking it all up.
Editing View/EntryView.xaml:
<Window x:Class="MyCompany.MyProject.View.EntryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Rolodex Entry"
Height="350" Width="525"
MinWidth="300" MinHeight="200">
<Grid Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Name:" Grid.Column="0" Grid.Row="0" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1"
Grid.Row="0" Margin="6,0,0,0" />
<TextBlock Text="E-mail:" Grid.Column="0" Grid.Row="1"
Margin="0,6,0,0" />
<TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1"
Grid.Row="1" Margin="6,6,0,0" />
<TextBlock Text="Phone Number:" Grid.Column="0" Grid.Row="2"
Margin="0,6,0,0" />
<TextBox Text="{Binding PhoneNumber, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1" Grid.Row="2"
Margin="6,6,0,0" />
<TextBlock Text="Relationship:" Grid.Column="0" Grid.Row="3"
Margin="0,6,0,0" />
<TextBox Text="{Binding Relationship, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1" Grid.Row="3"
Margin="6,6,0,0" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="{Binding CurrentEditModeName}"
Command="{Binding MakeEditableOrRevertCommand}"
Grid.Column="0" HorizontalAlignment="Left"
Width="75" />
<Button Content="Save" Command="{Binding SaveCommand}"
Grid.Column="1" Width="75" />
<Button Content="Cancel" Command="{Binding CancelCommand}"
Grid.Column="2" Width="75" Margin="6,0,0,0" />
</Grid>
</Grid>
</Window>
I did a lot of work here. First, the static stuff:
I changed the title of the form to match the Rolodex idea
I added labels for the fields, since I now know what they apply to
I changed the minimum width/height, since I noticed controls were getting cut off
Next the data-binding:
I bound all the text fields to the appropriate properties on the view model
I made the text fields update the view model on every keypress (UpdateSourceTrigger=PropertyChanged). This isn't necessary for this app, but could be helpful in the future. I added it to spare you from looking it up when you need it :)
I bound the IsEnabled field of each text box to the IsInEditMode property
I bound the buttons to their respective commands
I bound the edit button's name (Content property) to the corresponding property on the view model
Here's the result
Now all the UI logic works, except those we left a Todo comment on. I left those unimplemented because they have to do with a specific application architecture, and I didn't want to get into that for this demo.
Also, vanilla WPF doesn't have a very clean MVVM way to close a form that I know of. You can use code-behind to do it, or you can use one of the dozens of WPF add-on libraries that provide their own cleaner way of doing it.
Dependency Properties
You may have noticed that I didn't create a single custom Dependency Property in my code. The dependency properties I used were all on existing controls (e.g. Text, Content and Command). This is how it usually works in WPF, because data binding and styling (which I didn't get into) gives you a lot of options. It lets you completely customize the look, feel, and actions of built-in controls.
In previous Windows GUI frameworks, you'd often have to subclass existing controls or create custom controls to get a custom look and feel. The only reasons to make custom controls in WPF are to combine patterns of multiple controls in a reusable way, or to create a completely new control from scratch.
E.g. if you were making an auto-complete text box that is paired with a popup control to display the values it is auto-completing from. In such a case you might want to make a custom control, with custom dependency properties (such as the auto-completion source). That way you can reuse the control throughout your application, and other applications.
If you aren't making custom controls, or making special non-UI classes that can directly instantiate and use in XAML and data bind with, you probably won't need to create dependency properties.
The poster has requested that I repost my comment as an answer. Happy to oblige :-)
The video presentation I referred to: http://blog.lab49.com/archives/2650
Bonus link: awesome WPF article in MSDN: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
And in case you didn't know about it, there's a chapter in the online documentation: http://msdn.microsoft.com/en-us/library/ms752914.aspx
Also I've found this book very helpful: http://www.amazon.com/WPF-4-Unleashed-Adam-Nathan/dp/0672331195
My own experience with WPF involves going back between a bunch of different resources as I try to get my program to work. There's so much stuff in WPF it's really hard to keep it all in your head as you are learning it.
A simple way of looking at them is that they are properties that point to another property.
They're actually a definition of a property, that defines the property name, type, default value, etc, but the actual value of the property is not stored with the property definition.
So you can say a Button's Enabled property is going to point to a property on a specific class, or it it is going to point to CheckBoxA.IsChecked property, or you can even say it is simply going to be pointing to a boolean value of False.
// Value points to the current DataContext object's CanSaveObject property
<Button IsEnabled="{Binding CanSaveObject}" />
// Value points to the IsChecked property of CheckBoxA
<Button IsEnabled="{Binding ElementName=CheckBoxA, Path=IsChecked}" />
// Value points to the value False
<Button IsEnabled="False" />