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();
}
#re