-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How do button/menu handlers work? #2
Comments
Hi Paul, First thing to appreciate is that a window or usercontrol in WPF has a data context. When using an MVVM architecture this is set to your view model (which is typically a class implementing INotifyPropertyChanged). If you look at the MapWindow user control file ($:\Lunatic.TelescopeControl\Controls\MapWindow.xaml.cs) you will see that an instance of the MapViewModel class is passed to the Window's constructor it is assigned to the Windows's DataContext property. public MapWindow(MapViewModel viewModel)
{
InitializeComponent();
_ViewModel = viewModel;
this.DataContext = _ViewModel; Now of look the MapWindow.xaml file you can find the definitions for the OK and Cancel button. <StackPanel DockPanel.Dock="Bottom">
<Button Content="OK" Margin="0,0,0,10" Command="{Binding SaveChangesAndCloseCommand}" Width="80"/>
<Button Content="Cancel" Command="{Binding CancelChangesAndCloseCommand}" Width="80"/>
</StackPanel>
Notice the Command bindings on each of the definitions. The OK button is bound to a command Looking at ($:\Lunatic.Core\Classes\ViewModelBase.cs) you will see the following definition for the SaveChangesAndCloseCommand. private RelayCommand _SaveChangesAndCloseCommand;
/// <summary>
/// Gets the SaveChangesAndCloseCommand.
/// </summary>
public RelayCommand SaveChangesAndCloseCommand
{
get
{
return _SaveChangesAndCloseCommand
?? (_SaveChangesAndCloseCommand = new RelayCommand(
() => {
if (OnSaveCommand()) {
// Don't need to do anything here as the assumption is that the properties
// and bound and therefore already saved.
if (this.SaveAndCloseAction != null) {
SaveAndCloseAction();
}
}
}));
}
} Relay commands are defined like a property's getter. I'll explain the above a little... We have a private member variable of type RelayCommand The getter looks a little odd with the The bit that does the work is lamda expression passed to the RelayCommand constructor. () => {
if (OnSaveCommand()) {
// Don't need to do anything here as the assumption is that the properties
// and bound and therefore already saved.
if (this.SaveAndCloseAction != null) {
SaveAndCloseAction();
}
}
})); This is a parameter less relay command so the lamda parameters are empty. This command then calls a virtual OnSaveCommand() function (which can be overridden in derived class). If the OnSaveCommand returns true the command then looks to see if the viewmodel's SaveAndCloseAction has been hooked up (i.e. != null). If it is then the action is called. Just rolling back and looking further down the constructor in MapWindow.xaml.cs you can see view model actions being hooked up. // Hook up to the viewmodels close actions
if (_ViewModel.SaveAndCloseAction == null) {
_ViewModel.SaveAndCloseAction = new Action(() => {
this.DialogResult = true;
this.Close();
});
}
if (_ViewModel.CancelAndCloseAction == null) {
_ViewModel.CancelAndCloseAction = new Action(() => {
this.DialogResult = false;
this.Close();
});
} This is a very brief introduction to relay commands and command binding in WPF. Hopefully as the project progresses I will get some more advanced examples in there for you to look at. Moving from VB6 to a truly objected orientated language such as VB.NET or C# requires more than just learning a new language. To truly benefit from the effort you need to make a paradigm shift from thinking of programs as a procedural list of instructions to thinking of the application as being made up of objects with specific jobs to do. This is also the case when you move Winforms development to Windows Platform Foundation (WPF). While the underlying event model is still there the binding framework allows your code to be much more structured, re-usable and ultimately easier to maintain. |
Chris, I have to admit I'm not a great fan of the expando property of Javascript. With C# being compiled I would expect one should never need to check: < if (this.SaveAndCloseAction != null) { > because the linker would sort this out. However I am a hardcore C++ engineering developer who links over 180 C++ libraries with tens of thousands of header files - so my expectations are skewed. Is there anyway to force the declaration of SaveAndCloseAction()? At least in the base class. That would simplify the GUI code greatly. A blunt CheckNullFunctions() called by unit tests and within the main program might do the trick; it would avoid most null exception crashes. Thanks |
I have been used to a "OnButton1" function type in MFC C++, with MFC's Document-View architecture. This is a simple architecture, very suitable for a small program like EQMOD.
I understand Lunaticsoftware uses MVVM Lite or Model-View-ViewModel. It offers more complex commands via the ICommand interface. Architecturally there are benefits, but it can also add complexity that keeps less confident developers contributing to the project. There are some very experienced astro-photographers who can contribute, in fact some wrote the original EQMOD using VB6. We need to keep access simple for them.
So, how will the button callbacks work? Can we see a code sample?
cheers
Paul
The text was updated successfully, but these errors were encountered: