public abstract class ObservableObject : INotifyPropertyChanged
{
#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>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "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() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raises the PropertyChange event for the property specified
/// </summary>
/// <param name="propertyName">Property name to update. Is case-sensitive.</param>
public virtual void RaisePropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
OnPropertyChanged(propertyName);
}
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
}
2..
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _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>
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 status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion // ICommand Members
}
App.Xaml
<Application x:Class="SimpleMVVMExample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ProductView.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
APP.XAML.CS
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow app = new MainWindow();
ProductViewModel context = new ProductViewModel();
app.DataContext = context;
app.Show();
}
}
Main Window File
<Window x:Class="SimpleMVVMExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Simple MVVM Example" Height="350" Width="525">
<Grid>
<ContentControl Content="{Binding }" HorizontalAlignment="Center" Margin="10" />
</Grid>
</Window>
Product Model Class
public class ProductModel : ObservableObject
{
#region Fields
private int _productId;
private string _productName;
private decimal _unitPrice;
#endregion // Fields
#region Properties
public int ProductId
{
get { return _productId; }
set
{
if (value != _productId)
{
_productId = value;
OnPropertyChanged("ProductId");
}
}
}
public string ProductName
{
get { return _productName; }
set
{
if (value != _productName)
{
_productName = value;
OnPropertyChanged("ProductName");
}
}
}
public decimal UnitPrice
{
get { return _unitPrice; }
set
{
if (value != _unitPrice)
{
_unitPrice = value;
OnPropertyChanged("UnitPrice");
}
}
}
#endregion // Properties
}
ProductView Xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SimpleMVVMExample">
<!--
The View can be defined in a UserControl, a DataTemplate, or a combination of the two.
It can exist as it's own file, or it can be simply added to Window.Resources.
My View is a ResourceDictionary that defines DataTemplates. This ResourceDictionary
needs to be added to the application's ResourceDictionaries at runtime, so it is getting
added it in the MainWindow's Window.Resources XAML.
-->
<!-- DataTemplate for Product Model -->
<DataTemplate DataType="{x:Type local:ProductModel}">
<Border BorderBrush="Black" BorderThickness="1" Padding="20">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="ID" VerticalAlignment="Center" Margin="5" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding ProductId}" Margin="5" Width="150" />
<TextBlock Grid.Column="0" Grid.Row="1" Text="Name" VerticalAlignment="Center" Margin="5" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding ProductName}" Margin="5" Width="150" />
<TextBlock Grid.Column="0" Grid.Row="2" Text="Unit Price" VerticalAlignment="Center" Margin="5" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding UnitPrice}" Margin="5" Width="150" />
</Grid>
</Border>
</DataTemplate>
<!-- DataTemplate for Product ViewModel -->
<DataTemplate DataType="{x:Type local:ProductViewModel}">
<DockPanel Margin="20">
<DockPanel DockPanel.Dock="Top">
<TextBlock Margin="10,2" DockPanel.Dock="Left" Text="Enter Product Id" VerticalAlignment="Center" />
<TextBox Margin="10,2" Width="50" VerticalAlignment="Center" Text="{Binding Path=ProductId, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Save Product" DockPanel.Dock="Right" Margin="10,2" VerticalAlignment="Center"
Command="{Binding Path=SaveProductCommand}" Width="100" />
<Button Content="Get Product" DockPanel.Dock="Right" Margin="10,2" VerticalAlignment="Center"
Command="{Binding Path=GetProductCommand}" IsDefault="True" Width="100" />
</DockPanel>
<ContentControl Margin="10" Content="{Binding Path=CurrentProduct}" />
</DockPanel>
</DataTemplate>
</ResourceDictionary>
4... Product View Model
using System.Windows.Input;
namespace SimpleMVVMExample
{
public class ProductViewModel : ObservableObject
{
#region Fields
private int _productId;
private ProductModel _currentProduct;
private ICommand _getProductCommand;
private ICommand _saveProductCommand;
#endregion
#region Public Properties/Commands
public int ProductId
{
get { return _productId; }
set
{
if (value != _productId)
{
_productId = value;
OnPropertyChanged("ProductId");
}
}
}
public ProductModel CurrentProduct
{
get { return _currentProduct; }
set
{
if (value != _currentProduct)
{
_currentProduct = value;
OnPropertyChanged("CurrentProduct");
}
}
}
public ICommand GetProductCommand
{
get
{
if (_getProductCommand == null)
{
_getProductCommand = new RelayCommand(
param => GetProduct(),
param => ProductId > 0
);
}
return _getProductCommand;
}
}
public ICommand SaveProductCommand
{
get
{
if (_saveProductCommand == null)
{
_saveProductCommand = new RelayCommand(
param => SaveProduct(),
param => (CurrentProduct != null)
);
}
return _saveProductCommand;
}
}
#endregion
#region Private Helpers
private void GetProduct()
{
// Usually you'd get your Product from your datastore,
// but for now we'll just return a new object
ProductModel p = new ProductModel();
p.ProductId = ProductId;
p.ProductName = "Test Product";
p.UnitPrice = 10;
CurrentProduct = p;
}
private void SaveProduct()
{
// You would implement your Product save here
}
#endregion
}
}
No comments:
Post a Comment