Suggestions for this tutorial project
So I made a tutorial project to help myself better understand how to use WPF and MVVM, but there are a couple of things I'm unsure about.
This is the tutorial I was using as a reference. I deviated from it a little to make it more interesting for myself.
Is it a good idea to merge MainWindowResources.xaml with MainWindow.xaml or should I merge it with App.xaml? Why or why not? I have tried both and they both run fine (granted the project is extremely basic), but I'm very suspicious about them doing the same thing.
Do you have any recommendations for naming conventions? I would like to know if there are naming conventions that most people would use in the context of MVVM; I'm pretty bad with naming if you leave me to my own devices.- Do you have any other general recommendations/guidelines?
Thanks in advance if you happen to read or answer!
Project Hiearchy:
Enums:
namespace LearningWPF.Enums
{
public enum MotorStatus
{
Off = 0,
On = 1
}
}
.
namespace LearningWPF.Enums
{
public enum RotationalDirection
{
Clockwise = -1,
CounterClockwise = 1
}
}
Models:
using LearningWPF.Enums;
namespace LearningWPF.Models
{
public class Motor : ProtoModel
{
#region Fields
private MotorStatus _status;
private int _id;
private string _motorName;
private RotationalDirection _direction;
#endregion //Fields
#region Properties
public MotorStatus Status
{
get { return _status; }
//This set method will determine if the input value is the
//same as the current value -> if they are not the same, then
//it will change the current value to the input value and
//then it will notify the event handler that its value
//has been changed.
set
{
if (value != _status)
{
_status = value;
OnPropertyChanged("Status");
}
}
}
public int ID
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
OnPropertyChanged("ID");
}
}
}
public string MotorName
{
get { return _motorName; }
set
{
if (value != _motorName)
{
_motorName = value;
OnPropertyChanged("MotorName");
}
}
}
public RotationalDirection Direction
{
get { return _direction; }
set
{
if (value != _direction)
{
_direction = value;
OnPropertyChanged("Direction");
}
}
}
#endregion //Properties
#region Constructors
public Motor(int id)
{
_status = MotorStatus.On;
_id = id;
_motorName = string.Format($"TestMotor{id}");
_direction = RotationalDirection.CounterClockwise;
}
#endregion Constructors
}
}
.
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace LearningWPF.Models
{
//Purpose: To provide base functionality common to all models.
public abstract class ProtoModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
//This property is used to notify that another property has
//changed.
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName"></param>
//This method is used to initiate the PropertyChanged event.
//Whenever a property is changed for whatever reason, you will
//want the class to notify subscribers to the
//PropertyChangedEventHandler that a property on this object
//has just been changed.
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
if (this.PropertyChanged != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
}
#endregion //INotifyPropertyChanged members
#region Debugging aides
/// <summary>
/// Warns the developer if this object does not have a
/// public property with the specified name. This method does not
/// exist in a release build.
/// </summary>
//This method is used during debugging to determine if the
//class has the public property with the name passed into
//the method.
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = String.Format($"Invalid property name: {propertyName}");
if (this.ThrowOnInvalidPropertyName)
{
throw new Exception(msg);
}
else
{
Debug.Fail(msg);
}
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail()
/// when an invalid property name is passed to the VerifyPropertyName
/// method. The default value is false, but subclasses used by unit
/// tests override this property's getter to return true.
/// </summary>
//This is basically used only for unit testing so that an
//exception is thrown instead of a Debug.Fail().
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion //Debugging aides
}
}
ViewModels:
using LearningWPF.Models;
using System.Windows.Input;
using System.Windows;
namespace LearningWPF.ViewModels
{
public class MotorViewModel : ProtoModel
{
#region Fields
private int _motorId;
private string _motorName;
private Motor _currentMotor;
private ICommand _getMotorCommand;
private ICommand _saveMotorCommand;
#endregion //Fields
#region Properties
public int MotorID
{
get { return _motorId; }
set
{
if (value != _motorId)
{
_motorId = value;
OnPropertyChanged("MotorID");
}
}
}
public string MotorName
{
get { return _motorName; }
set
{
if (value != _motorName)
{
_motorName = value;
OnPropertyChanged("MotorName");
}
}
}
public Motor CurrentMotor
{
get { return _currentMotor; }
set
{
if (value != _currentMotor)
{
_currentMotor = value;
OnPropertyChanged("CurrentMotor");
}
}
}
public ICommand SaveMotorCommand
{
get
{
if (_saveMotorCommand == null)
{
_saveMotorCommand = new RelayCommand(
param => SaveMotor(),
param => (CurrentMotor != null)
);
}
return _saveMotorCommand;
}
}
public ICommand GetMotorCommand
{
get
{
if (_getMotorCommand == null)
{
_getMotorCommand = new RelayCommand(
param => GetMotor(),
param => MotorID > 0
);
}
return _getMotorCommand;
}
}
#endregion //Properties
#region Private Helpers
private void GetMotor()
{
_currentMotor = new Motor(_motorId);
_motorName = _currentMotor.MotorName;
string msg = string.Format($"You got {_motorName}.");
MessageBox.Show(msg);
}
private void SaveMotor()
{
string msg = string.Format($"You saved {_motorName}.");
MessageBox.Show(msg);
}
#endregion Private Helpers
}
}
Views (XAML):
<Window x:Class="LearningWPF.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Views/ViewResources/MainWindowResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
Views (C#):
using System.Windows;
namespace LearningWPF.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
ViewResources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:LearningWPF.Models"
xmlns:vm="clr-namespace:LearningWPF.ViewModels">
<DataTemplate DataType="{x:Type m:Motor}">
<Border BorderBrush="Black"
BorderThickness="1"
Padding="20">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="ID"
VerticalAlignment="Center"/>
<TextBox Grid.Column="1"
Grid.Row="0"
Text="{Binding ID}"/>
<TextBlock Grid.Column="0"
Grid.Row="1"
Text="Name"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="1"
Text="{Binding MotorName}" />
<TextBlock Grid.Column="0"
Grid.Row="2"
Text="Status"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="2"
Text="{Binding Status}" />
</Grid>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MotorViewModel}">
<DockPanel Margin="20">
<DockPanel DockPanel.Dock="Top">
<TextBlock Margin="10,2"
DockPanel.Dock="Left"
Text="Enter Motor ID"
VerticalAlignment="Center" />
<TextBox Margin="10,2"
Width="50"
VerticalAlignment="Center"
Text="{Binding Path=MotorID, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Save Motor"
DockPanel.Dock="Right"
Margin="10,2"
VerticalAlignment="Center"
Command="{Binding Path=SaveMotorCommand}"
Width="100" />
<Button Content="Get Motor"
DockPanel.Dock="Right"
Margin="10,2"
VerticalAlignment="Center"
Command="{Binding Path=GetMotorCommand}"
IsDefault="True"
Width="100" />
<ContentControl Margin="10,20"
Content="{Binding Path=CurrentMotor}" />
</DockPanel>
</DockPanel>
</DataTemplate>
</ResourceDictionary>
App (XAML):
<Application x:Class="LearningWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
App (C#):
using System.Windows;
using LearningWPF.ViewModels;
using LearningWPF.Views;
namespace LearningWPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow startApp = new MainWindow();
MotorViewModel viewModel = new MotorViewModel();
startApp.DataContext = viewModel;
startApp.Show();
}
}
}
RelayCommand (idk where to put this):
using System;
using System.Windows.Input;
using System.Diagnostics;
namespace LearningWPF
{
/// <summary>
/// A command whose sole purpose is to relay its
/// functionality to other objects by invoking
/// delegates. The default return value for the
/// CanExecute method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
//This is a method that will perform the
//requested action.
readonly Action<object> _execute;
//This is a method that will determine
//if _execute SHOULD execute.
readonly Predicate<object> _canExecute;
#endregion Fields
#region Constructors
/// <summary>
/// Creates a new command that can always
/// execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
//This constructor should be used when you know
//absolutely positively that the delegate should
//execute.
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution stats logic.</param>
//This constructor should be used when you know which delegate you want
//to execute and when you have a different delegate to determine if
//your command should execute.
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
//if (execute != null)
//{
// _execute = execute;
//}
//else
//{
// throw new ArgumentNullException("execute");
//}
//The next line is equivalent to the above code.
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
#endregion Constructors
#region ICommand Members
/// <summary>
/// Determines if the command is able to be executed.
/// </summary>
/// <param name="parameters">Input parameters for Predicate.</param>
/// <returns></returns>
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
//if (_canExecute == null)
//{
// return true;
//}
//else
//{
// return _canExecute(parameters);
//}
//The next line is equivalent to the above code.
return _canExecute == null ? true : _canExecute(parameters);
}
/// <summary>
/// This is used to notify that the CanExecute property has changed.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="parameters">Input parameters for Action.</param>
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion ICommand Members
}
}
c# wpf mvvm xaml
add a comment |
So I made a tutorial project to help myself better understand how to use WPF and MVVM, but there are a couple of things I'm unsure about.
This is the tutorial I was using as a reference. I deviated from it a little to make it more interesting for myself.
Is it a good idea to merge MainWindowResources.xaml with MainWindow.xaml or should I merge it with App.xaml? Why or why not? I have tried both and they both run fine (granted the project is extremely basic), but I'm very suspicious about them doing the same thing.
Do you have any recommendations for naming conventions? I would like to know if there are naming conventions that most people would use in the context of MVVM; I'm pretty bad with naming if you leave me to my own devices.- Do you have any other general recommendations/guidelines?
Thanks in advance if you happen to read or answer!
Project Hiearchy:
Enums:
namespace LearningWPF.Enums
{
public enum MotorStatus
{
Off = 0,
On = 1
}
}
.
namespace LearningWPF.Enums
{
public enum RotationalDirection
{
Clockwise = -1,
CounterClockwise = 1
}
}
Models:
using LearningWPF.Enums;
namespace LearningWPF.Models
{
public class Motor : ProtoModel
{
#region Fields
private MotorStatus _status;
private int _id;
private string _motorName;
private RotationalDirection _direction;
#endregion //Fields
#region Properties
public MotorStatus Status
{
get { return _status; }
//This set method will determine if the input value is the
//same as the current value -> if they are not the same, then
//it will change the current value to the input value and
//then it will notify the event handler that its value
//has been changed.
set
{
if (value != _status)
{
_status = value;
OnPropertyChanged("Status");
}
}
}
public int ID
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
OnPropertyChanged("ID");
}
}
}
public string MotorName
{
get { return _motorName; }
set
{
if (value != _motorName)
{
_motorName = value;
OnPropertyChanged("MotorName");
}
}
}
public RotationalDirection Direction
{
get { return _direction; }
set
{
if (value != _direction)
{
_direction = value;
OnPropertyChanged("Direction");
}
}
}
#endregion //Properties
#region Constructors
public Motor(int id)
{
_status = MotorStatus.On;
_id = id;
_motorName = string.Format($"TestMotor{id}");
_direction = RotationalDirection.CounterClockwise;
}
#endregion Constructors
}
}
.
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace LearningWPF.Models
{
//Purpose: To provide base functionality common to all models.
public abstract class ProtoModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
//This property is used to notify that another property has
//changed.
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName"></param>
//This method is used to initiate the PropertyChanged event.
//Whenever a property is changed for whatever reason, you will
//want the class to notify subscribers to the
//PropertyChangedEventHandler that a property on this object
//has just been changed.
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
if (this.PropertyChanged != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
}
#endregion //INotifyPropertyChanged members
#region Debugging aides
/// <summary>
/// Warns the developer if this object does not have a
/// public property with the specified name. This method does not
/// exist in a release build.
/// </summary>
//This method is used during debugging to determine if the
//class has the public property with the name passed into
//the method.
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = String.Format($"Invalid property name: {propertyName}");
if (this.ThrowOnInvalidPropertyName)
{
throw new Exception(msg);
}
else
{
Debug.Fail(msg);
}
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail()
/// when an invalid property name is passed to the VerifyPropertyName
/// method. The default value is false, but subclasses used by unit
/// tests override this property's getter to return true.
/// </summary>
//This is basically used only for unit testing so that an
//exception is thrown instead of a Debug.Fail().
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion //Debugging aides
}
}
ViewModels:
using LearningWPF.Models;
using System.Windows.Input;
using System.Windows;
namespace LearningWPF.ViewModels
{
public class MotorViewModel : ProtoModel
{
#region Fields
private int _motorId;
private string _motorName;
private Motor _currentMotor;
private ICommand _getMotorCommand;
private ICommand _saveMotorCommand;
#endregion //Fields
#region Properties
public int MotorID
{
get { return _motorId; }
set
{
if (value != _motorId)
{
_motorId = value;
OnPropertyChanged("MotorID");
}
}
}
public string MotorName
{
get { return _motorName; }
set
{
if (value != _motorName)
{
_motorName = value;
OnPropertyChanged("MotorName");
}
}
}
public Motor CurrentMotor
{
get { return _currentMotor; }
set
{
if (value != _currentMotor)
{
_currentMotor = value;
OnPropertyChanged("CurrentMotor");
}
}
}
public ICommand SaveMotorCommand
{
get
{
if (_saveMotorCommand == null)
{
_saveMotorCommand = new RelayCommand(
param => SaveMotor(),
param => (CurrentMotor != null)
);
}
return _saveMotorCommand;
}
}
public ICommand GetMotorCommand
{
get
{
if (_getMotorCommand == null)
{
_getMotorCommand = new RelayCommand(
param => GetMotor(),
param => MotorID > 0
);
}
return _getMotorCommand;
}
}
#endregion //Properties
#region Private Helpers
private void GetMotor()
{
_currentMotor = new Motor(_motorId);
_motorName = _currentMotor.MotorName;
string msg = string.Format($"You got {_motorName}.");
MessageBox.Show(msg);
}
private void SaveMotor()
{
string msg = string.Format($"You saved {_motorName}.");
MessageBox.Show(msg);
}
#endregion Private Helpers
}
}
Views (XAML):
<Window x:Class="LearningWPF.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Views/ViewResources/MainWindowResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
Views (C#):
using System.Windows;
namespace LearningWPF.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
ViewResources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:LearningWPF.Models"
xmlns:vm="clr-namespace:LearningWPF.ViewModels">
<DataTemplate DataType="{x:Type m:Motor}">
<Border BorderBrush="Black"
BorderThickness="1"
Padding="20">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="ID"
VerticalAlignment="Center"/>
<TextBox Grid.Column="1"
Grid.Row="0"
Text="{Binding ID}"/>
<TextBlock Grid.Column="0"
Grid.Row="1"
Text="Name"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="1"
Text="{Binding MotorName}" />
<TextBlock Grid.Column="0"
Grid.Row="2"
Text="Status"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="2"
Text="{Binding Status}" />
</Grid>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MotorViewModel}">
<DockPanel Margin="20">
<DockPanel DockPanel.Dock="Top">
<TextBlock Margin="10,2"
DockPanel.Dock="Left"
Text="Enter Motor ID"
VerticalAlignment="Center" />
<TextBox Margin="10,2"
Width="50"
VerticalAlignment="Center"
Text="{Binding Path=MotorID, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Save Motor"
DockPanel.Dock="Right"
Margin="10,2"
VerticalAlignment="Center"
Command="{Binding Path=SaveMotorCommand}"
Width="100" />
<Button Content="Get Motor"
DockPanel.Dock="Right"
Margin="10,2"
VerticalAlignment="Center"
Command="{Binding Path=GetMotorCommand}"
IsDefault="True"
Width="100" />
<ContentControl Margin="10,20"
Content="{Binding Path=CurrentMotor}" />
</DockPanel>
</DockPanel>
</DataTemplate>
</ResourceDictionary>
App (XAML):
<Application x:Class="LearningWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
App (C#):
using System.Windows;
using LearningWPF.ViewModels;
using LearningWPF.Views;
namespace LearningWPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow startApp = new MainWindow();
MotorViewModel viewModel = new MotorViewModel();
startApp.DataContext = viewModel;
startApp.Show();
}
}
}
RelayCommand (idk where to put this):
using System;
using System.Windows.Input;
using System.Diagnostics;
namespace LearningWPF
{
/// <summary>
/// A command whose sole purpose is to relay its
/// functionality to other objects by invoking
/// delegates. The default return value for the
/// CanExecute method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
//This is a method that will perform the
//requested action.
readonly Action<object> _execute;
//This is a method that will determine
//if _execute SHOULD execute.
readonly Predicate<object> _canExecute;
#endregion Fields
#region Constructors
/// <summary>
/// Creates a new command that can always
/// execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
//This constructor should be used when you know
//absolutely positively that the delegate should
//execute.
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution stats logic.</param>
//This constructor should be used when you know which delegate you want
//to execute and when you have a different delegate to determine if
//your command should execute.
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
//if (execute != null)
//{
// _execute = execute;
//}
//else
//{
// throw new ArgumentNullException("execute");
//}
//The next line is equivalent to the above code.
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
#endregion Constructors
#region ICommand Members
/// <summary>
/// Determines if the command is able to be executed.
/// </summary>
/// <param name="parameters">Input parameters for Predicate.</param>
/// <returns></returns>
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
//if (_canExecute == null)
//{
// return true;
//}
//else
//{
// return _canExecute(parameters);
//}
//The next line is equivalent to the above code.
return _canExecute == null ? true : _canExecute(parameters);
}
/// <summary>
/// This is used to notify that the CanExecute property has changed.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="parameters">Input parameters for Action.</param>
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion ICommand Members
}
}
c# wpf mvvm xaml
add a comment |
So I made a tutorial project to help myself better understand how to use WPF and MVVM, but there are a couple of things I'm unsure about.
This is the tutorial I was using as a reference. I deviated from it a little to make it more interesting for myself.
Is it a good idea to merge MainWindowResources.xaml with MainWindow.xaml or should I merge it with App.xaml? Why or why not? I have tried both and they both run fine (granted the project is extremely basic), but I'm very suspicious about them doing the same thing.
Do you have any recommendations for naming conventions? I would like to know if there are naming conventions that most people would use in the context of MVVM; I'm pretty bad with naming if you leave me to my own devices.- Do you have any other general recommendations/guidelines?
Thanks in advance if you happen to read or answer!
Project Hiearchy:
Enums:
namespace LearningWPF.Enums
{
public enum MotorStatus
{
Off = 0,
On = 1
}
}
.
namespace LearningWPF.Enums
{
public enum RotationalDirection
{
Clockwise = -1,
CounterClockwise = 1
}
}
Models:
using LearningWPF.Enums;
namespace LearningWPF.Models
{
public class Motor : ProtoModel
{
#region Fields
private MotorStatus _status;
private int _id;
private string _motorName;
private RotationalDirection _direction;
#endregion //Fields
#region Properties
public MotorStatus Status
{
get { return _status; }
//This set method will determine if the input value is the
//same as the current value -> if they are not the same, then
//it will change the current value to the input value and
//then it will notify the event handler that its value
//has been changed.
set
{
if (value != _status)
{
_status = value;
OnPropertyChanged("Status");
}
}
}
public int ID
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
OnPropertyChanged("ID");
}
}
}
public string MotorName
{
get { return _motorName; }
set
{
if (value != _motorName)
{
_motorName = value;
OnPropertyChanged("MotorName");
}
}
}
public RotationalDirection Direction
{
get { return _direction; }
set
{
if (value != _direction)
{
_direction = value;
OnPropertyChanged("Direction");
}
}
}
#endregion //Properties
#region Constructors
public Motor(int id)
{
_status = MotorStatus.On;
_id = id;
_motorName = string.Format($"TestMotor{id}");
_direction = RotationalDirection.CounterClockwise;
}
#endregion Constructors
}
}
.
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace LearningWPF.Models
{
//Purpose: To provide base functionality common to all models.
public abstract class ProtoModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
//This property is used to notify that another property has
//changed.
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName"></param>
//This method is used to initiate the PropertyChanged event.
//Whenever a property is changed for whatever reason, you will
//want the class to notify subscribers to the
//PropertyChangedEventHandler that a property on this object
//has just been changed.
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
if (this.PropertyChanged != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
}
#endregion //INotifyPropertyChanged members
#region Debugging aides
/// <summary>
/// Warns the developer if this object does not have a
/// public property with the specified name. This method does not
/// exist in a release build.
/// </summary>
//This method is used during debugging to determine if the
//class has the public property with the name passed into
//the method.
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = String.Format($"Invalid property name: {propertyName}");
if (this.ThrowOnInvalidPropertyName)
{
throw new Exception(msg);
}
else
{
Debug.Fail(msg);
}
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail()
/// when an invalid property name is passed to the VerifyPropertyName
/// method. The default value is false, but subclasses used by unit
/// tests override this property's getter to return true.
/// </summary>
//This is basically used only for unit testing so that an
//exception is thrown instead of a Debug.Fail().
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion //Debugging aides
}
}
ViewModels:
using LearningWPF.Models;
using System.Windows.Input;
using System.Windows;
namespace LearningWPF.ViewModels
{
public class MotorViewModel : ProtoModel
{
#region Fields
private int _motorId;
private string _motorName;
private Motor _currentMotor;
private ICommand _getMotorCommand;
private ICommand _saveMotorCommand;
#endregion //Fields
#region Properties
public int MotorID
{
get { return _motorId; }
set
{
if (value != _motorId)
{
_motorId = value;
OnPropertyChanged("MotorID");
}
}
}
public string MotorName
{
get { return _motorName; }
set
{
if (value != _motorName)
{
_motorName = value;
OnPropertyChanged("MotorName");
}
}
}
public Motor CurrentMotor
{
get { return _currentMotor; }
set
{
if (value != _currentMotor)
{
_currentMotor = value;
OnPropertyChanged("CurrentMotor");
}
}
}
public ICommand SaveMotorCommand
{
get
{
if (_saveMotorCommand == null)
{
_saveMotorCommand = new RelayCommand(
param => SaveMotor(),
param => (CurrentMotor != null)
);
}
return _saveMotorCommand;
}
}
public ICommand GetMotorCommand
{
get
{
if (_getMotorCommand == null)
{
_getMotorCommand = new RelayCommand(
param => GetMotor(),
param => MotorID > 0
);
}
return _getMotorCommand;
}
}
#endregion //Properties
#region Private Helpers
private void GetMotor()
{
_currentMotor = new Motor(_motorId);
_motorName = _currentMotor.MotorName;
string msg = string.Format($"You got {_motorName}.");
MessageBox.Show(msg);
}
private void SaveMotor()
{
string msg = string.Format($"You saved {_motorName}.");
MessageBox.Show(msg);
}
#endregion Private Helpers
}
}
Views (XAML):
<Window x:Class="LearningWPF.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Views/ViewResources/MainWindowResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
Views (C#):
using System.Windows;
namespace LearningWPF.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
ViewResources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:LearningWPF.Models"
xmlns:vm="clr-namespace:LearningWPF.ViewModels">
<DataTemplate DataType="{x:Type m:Motor}">
<Border BorderBrush="Black"
BorderThickness="1"
Padding="20">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="ID"
VerticalAlignment="Center"/>
<TextBox Grid.Column="1"
Grid.Row="0"
Text="{Binding ID}"/>
<TextBlock Grid.Column="0"
Grid.Row="1"
Text="Name"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="1"
Text="{Binding MotorName}" />
<TextBlock Grid.Column="0"
Grid.Row="2"
Text="Status"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="2"
Text="{Binding Status}" />
</Grid>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MotorViewModel}">
<DockPanel Margin="20">
<DockPanel DockPanel.Dock="Top">
<TextBlock Margin="10,2"
DockPanel.Dock="Left"
Text="Enter Motor ID"
VerticalAlignment="Center" />
<TextBox Margin="10,2"
Width="50"
VerticalAlignment="Center"
Text="{Binding Path=MotorID, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Save Motor"
DockPanel.Dock="Right"
Margin="10,2"
VerticalAlignment="Center"
Command="{Binding Path=SaveMotorCommand}"
Width="100" />
<Button Content="Get Motor"
DockPanel.Dock="Right"
Margin="10,2"
VerticalAlignment="Center"
Command="{Binding Path=GetMotorCommand}"
IsDefault="True"
Width="100" />
<ContentControl Margin="10,20"
Content="{Binding Path=CurrentMotor}" />
</DockPanel>
</DockPanel>
</DataTemplate>
</ResourceDictionary>
App (XAML):
<Application x:Class="LearningWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
App (C#):
using System.Windows;
using LearningWPF.ViewModels;
using LearningWPF.Views;
namespace LearningWPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow startApp = new MainWindow();
MotorViewModel viewModel = new MotorViewModel();
startApp.DataContext = viewModel;
startApp.Show();
}
}
}
RelayCommand (idk where to put this):
using System;
using System.Windows.Input;
using System.Diagnostics;
namespace LearningWPF
{
/// <summary>
/// A command whose sole purpose is to relay its
/// functionality to other objects by invoking
/// delegates. The default return value for the
/// CanExecute method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
//This is a method that will perform the
//requested action.
readonly Action<object> _execute;
//This is a method that will determine
//if _execute SHOULD execute.
readonly Predicate<object> _canExecute;
#endregion Fields
#region Constructors
/// <summary>
/// Creates a new command that can always
/// execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
//This constructor should be used when you know
//absolutely positively that the delegate should
//execute.
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution stats logic.</param>
//This constructor should be used when you know which delegate you want
//to execute and when you have a different delegate to determine if
//your command should execute.
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
//if (execute != null)
//{
// _execute = execute;
//}
//else
//{
// throw new ArgumentNullException("execute");
//}
//The next line is equivalent to the above code.
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
#endregion Constructors
#region ICommand Members
/// <summary>
/// Determines if the command is able to be executed.
/// </summary>
/// <param name="parameters">Input parameters for Predicate.</param>
/// <returns></returns>
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
//if (_canExecute == null)
//{
// return true;
//}
//else
//{
// return _canExecute(parameters);
//}
//The next line is equivalent to the above code.
return _canExecute == null ? true : _canExecute(parameters);
}
/// <summary>
/// This is used to notify that the CanExecute property has changed.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="parameters">Input parameters for Action.</param>
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion ICommand Members
}
}
c# wpf mvvm xaml
So I made a tutorial project to help myself better understand how to use WPF and MVVM, but there are a couple of things I'm unsure about.
This is the tutorial I was using as a reference. I deviated from it a little to make it more interesting for myself.
Is it a good idea to merge MainWindowResources.xaml with MainWindow.xaml or should I merge it with App.xaml? Why or why not? I have tried both and they both run fine (granted the project is extremely basic), but I'm very suspicious about them doing the same thing.
Do you have any recommendations for naming conventions? I would like to know if there are naming conventions that most people would use in the context of MVVM; I'm pretty bad with naming if you leave me to my own devices.- Do you have any other general recommendations/guidelines?
Thanks in advance if you happen to read or answer!
Project Hiearchy:
Enums:
namespace LearningWPF.Enums
{
public enum MotorStatus
{
Off = 0,
On = 1
}
}
.
namespace LearningWPF.Enums
{
public enum RotationalDirection
{
Clockwise = -1,
CounterClockwise = 1
}
}
Models:
using LearningWPF.Enums;
namespace LearningWPF.Models
{
public class Motor : ProtoModel
{
#region Fields
private MotorStatus _status;
private int _id;
private string _motorName;
private RotationalDirection _direction;
#endregion //Fields
#region Properties
public MotorStatus Status
{
get { return _status; }
//This set method will determine if the input value is the
//same as the current value -> if they are not the same, then
//it will change the current value to the input value and
//then it will notify the event handler that its value
//has been changed.
set
{
if (value != _status)
{
_status = value;
OnPropertyChanged("Status");
}
}
}
public int ID
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
OnPropertyChanged("ID");
}
}
}
public string MotorName
{
get { return _motorName; }
set
{
if (value != _motorName)
{
_motorName = value;
OnPropertyChanged("MotorName");
}
}
}
public RotationalDirection Direction
{
get { return _direction; }
set
{
if (value != _direction)
{
_direction = value;
OnPropertyChanged("Direction");
}
}
}
#endregion //Properties
#region Constructors
public Motor(int id)
{
_status = MotorStatus.On;
_id = id;
_motorName = string.Format($"TestMotor{id}");
_direction = RotationalDirection.CounterClockwise;
}
#endregion Constructors
}
}
.
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace LearningWPF.Models
{
//Purpose: To provide base functionality common to all models.
public abstract class ProtoModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
//This property is used to notify that another property has
//changed.
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName"></param>
//This method is used to initiate the PropertyChanged event.
//Whenever a property is changed for whatever reason, you will
//want the class to notify subscribers to the
//PropertyChangedEventHandler that a property on this object
//has just been changed.
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
if (this.PropertyChanged != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
}
#endregion //INotifyPropertyChanged members
#region Debugging aides
/// <summary>
/// Warns the developer if this object does not have a
/// public property with the specified name. This method does not
/// exist in a release build.
/// </summary>
//This method is used during debugging to determine if the
//class has the public property with the name passed into
//the method.
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = String.Format($"Invalid property name: {propertyName}");
if (this.ThrowOnInvalidPropertyName)
{
throw new Exception(msg);
}
else
{
Debug.Fail(msg);
}
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail()
/// when an invalid property name is passed to the VerifyPropertyName
/// method. The default value is false, but subclasses used by unit
/// tests override this property's getter to return true.
/// </summary>
//This is basically used only for unit testing so that an
//exception is thrown instead of a Debug.Fail().
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion //Debugging aides
}
}
ViewModels:
using LearningWPF.Models;
using System.Windows.Input;
using System.Windows;
namespace LearningWPF.ViewModels
{
public class MotorViewModel : ProtoModel
{
#region Fields
private int _motorId;
private string _motorName;
private Motor _currentMotor;
private ICommand _getMotorCommand;
private ICommand _saveMotorCommand;
#endregion //Fields
#region Properties
public int MotorID
{
get { return _motorId; }
set
{
if (value != _motorId)
{
_motorId = value;
OnPropertyChanged("MotorID");
}
}
}
public string MotorName
{
get { return _motorName; }
set
{
if (value != _motorName)
{
_motorName = value;
OnPropertyChanged("MotorName");
}
}
}
public Motor CurrentMotor
{
get { return _currentMotor; }
set
{
if (value != _currentMotor)
{
_currentMotor = value;
OnPropertyChanged("CurrentMotor");
}
}
}
public ICommand SaveMotorCommand
{
get
{
if (_saveMotorCommand == null)
{
_saveMotorCommand = new RelayCommand(
param => SaveMotor(),
param => (CurrentMotor != null)
);
}
return _saveMotorCommand;
}
}
public ICommand GetMotorCommand
{
get
{
if (_getMotorCommand == null)
{
_getMotorCommand = new RelayCommand(
param => GetMotor(),
param => MotorID > 0
);
}
return _getMotorCommand;
}
}
#endregion //Properties
#region Private Helpers
private void GetMotor()
{
_currentMotor = new Motor(_motorId);
_motorName = _currentMotor.MotorName;
string msg = string.Format($"You got {_motorName}.");
MessageBox.Show(msg);
}
private void SaveMotor()
{
string msg = string.Format($"You saved {_motorName}.");
MessageBox.Show(msg);
}
#endregion Private Helpers
}
}
Views (XAML):
<Window x:Class="LearningWPF.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Views/ViewResources/MainWindowResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
Views (C#):
using System.Windows;
namespace LearningWPF.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
ViewResources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:LearningWPF.Models"
xmlns:vm="clr-namespace:LearningWPF.ViewModels">
<DataTemplate DataType="{x:Type m:Motor}">
<Border BorderBrush="Black"
BorderThickness="1"
Padding="20">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="ID"
VerticalAlignment="Center"/>
<TextBox Grid.Column="1"
Grid.Row="0"
Text="{Binding ID}"/>
<TextBlock Grid.Column="0"
Grid.Row="1"
Text="Name"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="1"
Text="{Binding MotorName}" />
<TextBlock Grid.Column="0"
Grid.Row="2"
Text="Status"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="2"
Text="{Binding Status}" />
</Grid>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MotorViewModel}">
<DockPanel Margin="20">
<DockPanel DockPanel.Dock="Top">
<TextBlock Margin="10,2"
DockPanel.Dock="Left"
Text="Enter Motor ID"
VerticalAlignment="Center" />
<TextBox Margin="10,2"
Width="50"
VerticalAlignment="Center"
Text="{Binding Path=MotorID, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Save Motor"
DockPanel.Dock="Right"
Margin="10,2"
VerticalAlignment="Center"
Command="{Binding Path=SaveMotorCommand}"
Width="100" />
<Button Content="Get Motor"
DockPanel.Dock="Right"
Margin="10,2"
VerticalAlignment="Center"
Command="{Binding Path=GetMotorCommand}"
IsDefault="True"
Width="100" />
<ContentControl Margin="10,20"
Content="{Binding Path=CurrentMotor}" />
</DockPanel>
</DockPanel>
</DataTemplate>
</ResourceDictionary>
App (XAML):
<Application x:Class="LearningWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
App (C#):
using System.Windows;
using LearningWPF.ViewModels;
using LearningWPF.Views;
namespace LearningWPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow startApp = new MainWindow();
MotorViewModel viewModel = new MotorViewModel();
startApp.DataContext = viewModel;
startApp.Show();
}
}
}
RelayCommand (idk where to put this):
using System;
using System.Windows.Input;
using System.Diagnostics;
namespace LearningWPF
{
/// <summary>
/// A command whose sole purpose is to relay its
/// functionality to other objects by invoking
/// delegates. The default return value for the
/// CanExecute method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
//This is a method that will perform the
//requested action.
readonly Action<object> _execute;
//This is a method that will determine
//if _execute SHOULD execute.
readonly Predicate<object> _canExecute;
#endregion Fields
#region Constructors
/// <summary>
/// Creates a new command that can always
/// execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
//This constructor should be used when you know
//absolutely positively that the delegate should
//execute.
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution stats logic.</param>
//This constructor should be used when you know which delegate you want
//to execute and when you have a different delegate to determine if
//your command should execute.
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
//if (execute != null)
//{
// _execute = execute;
//}
//else
//{
// throw new ArgumentNullException("execute");
//}
//The next line is equivalent to the above code.
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
#endregion Constructors
#region ICommand Members
/// <summary>
/// Determines if the command is able to be executed.
/// </summary>
/// <param name="parameters">Input parameters for Predicate.</param>
/// <returns></returns>
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
//if (_canExecute == null)
//{
// return true;
//}
//else
//{
// return _canExecute(parameters);
//}
//The next line is equivalent to the above code.
return _canExecute == null ? true : _canExecute(parameters);
}
/// <summary>
/// This is used to notify that the CanExecute property has changed.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="parameters">Input parameters for Action.</param>
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion ICommand Members
}
}
c# wpf mvvm xaml
c# wpf mvvm xaml
asked 2 hours ago
jcrizk
1157
1157
add a comment |
add a comment |
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210624%2fsuggestions-for-this-tutorial-project%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210624%2fsuggestions-for-this-tutorial-project%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown