Skip to main content
Notice removed Draw attention by RubberDuck
Bounty Ended with Mathieu Guindon's answer chosen by RubberDuck
Tweeted twitter.com/#!/StackCodeReview/status/519880962461925377
deleted 125 characters in body; edited title
Source Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237

Understanding the MVVM Conceptsconcepts and validation of my code

I have been learning MVVM concepts and trying to implement it in my new project. I want to validate my work that I have been doing these past days. I want to know if I follow correctly the mvvmMVVM pattern, I need your criticism / advise / suggestion on my Work. I understand that everyone has their way of working/thinking, but as I mention earlier what I need to know is, am I respecting the CORE of MVVM

  1. Do I follow the MVVM pattern?
  2. Is there a better / alternative approach that I could use (following the MVVM pattern, of course)?

Alright let's dig in! After learning, I came to the conclusion that I need some Helpers class, so I've created some.

The clViewModelBase class which implements the INotifyPropertyChanged interface, this will help me to notify if any of my properties has changed, I. I did this so I don't have to implement the interface on each and every single View Model:

The clDelegateCommand class which implements the ICommand interface, thiswhich will help me to fire up the events on the button  :

The clConverterclConverter which implements the IMultiValueConverterIMultiValueConverter interface, to Updatewill update my values only after the user has clicked on Save, this. This class returns an Arrayarray of the updated values.

Alright those were my helpers, nowHere's my Model that obviously derives the clViewModelBase and also the IDataErrorInfo interface to manage my errors, ID cannot be empty etc...

My View itview consist of 2 forms: my MainWindowMainWindow and the User Control. MainWindow : ContainsMainWindow contains 2 sectionsections:

  1. Left section that has my ListBox, for each part I will add a User Controluser control for it
  2. rightRight section that has all my details displayed.

Main Window Xaml XAML:

Code Behind of my MainWindowMy MainWindow:

My viewModel ViewModel:

and at last my User Control View XamlMy user control view XAML to load in my ListBox ListBox:

Understanding the MVVM Concepts and validation of my code

I have been learning MVVM concepts and trying to implement it in my new project. I want to validate my work that I have been doing these past days. I want to know if I follow correctly the mvvm pattern, I need your criticism / advise / suggestion on my Work. I understand that everyone has their way of working/thinking, but as I mention earlier what I need to know is, am I respecting the CORE of MVVM

  1. Do I follow the MVVM pattern
  2. Is there a better / alternative approach that I could use (following the MVVM pattern, of course)

Alright let's dig in! After learning, I came to the conclusion that I need some Helpers class, so I've created some.

The clViewModelBase class which implements the INotifyPropertyChanged interface, this will help me to notify if any of my properties has changed, I did this so I don't have to implement the interface on each and every single View Model:

The clDelegateCommand class which implements the ICommand interface, this will help me to fire up the events on the button  :

The clConverter which implements the IMultiValueConverter interface, to Update my values only after the user has clicked on Save, this class returns an Array of the updated values

Alright those were my helpers, now my Model that obviously derives the clViewModelBase and also the IDataErrorInfo interface to manage my errors, ID cannot be empty etc...

My View it consist of 2 forms my MainWindow and the User Control. MainWindow : Contains 2 section

  1. Left section that has my ListBox, for each part I will add a User Control for it
  2. right section that has all my details displayed.

Main Window Xaml :

Code Behind of my MainWindow :

My viewModel :

and at last my User Control View Xaml to load in my ListBox :

Understanding the MVVM concepts and validation of my code

I have been learning MVVM concepts and trying to implement it in my new project. I want to validate my work that I have been doing these past days. I want to know if I follow correctly the MVVM pattern. I understand that everyone has their way of working/thinking, but as I mention earlier what I need to know is, am I respecting the CORE of MVVM

  1. Do I follow the MVVM pattern?
  2. Is there a better / alternative approach that I could use (following the MVVM pattern, of course)?

After learning, I came to the conclusion that I need some Helpers class, so I've created some.

The clViewModelBase class which implements the INotifyPropertyChanged interface, will help me to notify if any of my properties has changed. I did this so I don't have to implement the interface on each and every single View Model:

The clDelegateCommand class which implements the ICommand interface, which will help me to fire up the events on the button:

The clConverter which implements the IMultiValueConverter interface, will update my values only after the user has clicked on Save. This class returns an array of the updated values.

Here's my Model that obviously derives the clViewModelBase and also the IDataErrorInfo interface to manage my errors, ID cannot be empty etc...

My view consist of 2 forms: my MainWindow and the User Control. MainWindow contains 2 sections:

  1. Left section that has my ListBox, for each part I will add a user control for it
  2. Right section that has all my details displayed

Main Window XAML:

My MainWindow:

My ViewModel:

My user control view XAML to load in my ListBox:

Notice added Draw attention by RubberDuck
Bounty Started worth 100 reputation by RubberDuck
added language-specification
Source Link
Vogel612
  • 25.3k
  • 7
  • 58
  • 141
using System.ComponentModel;

class clViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}
using System.ComponentModel;

class clViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}
using System;
using System.Windows.Input;

class clDelegateCommand : ICommand
{
    private Action<object> _executionAction;
    private Predicate<object> _canExecutePredicate;

    public clDelegateCommand(Action<object> execute)
        : this(execute, null)
    { }

    public clDelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this._executionAction = execute;
        this._canExecutePredicate = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return this._canExecutePredicate == null ? true : this._canExecutePredicate(parameter);
    }

    public void Execute(object parameter)
    {
        if (!this.CanExecute(parameter))
        {
            throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute.");
        }

        this._executionAction(parameter);
    }
}
using System;
using System.Windows.Input;

class clDelegateCommand : ICommand
{
    private Action<object> _executionAction;
    private Predicate<object> _canExecutePredicate;

    public clDelegateCommand(Action<object> execute)
        : this(execute, null)
    { }

    public clDelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this._executionAction = execute;
        this._canExecutePredicate = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return this._canExecutePredicate == null ? true : this._canExecutePredicate(parameter);
    }

    public void Execute(object parameter)
    {
        if (!this.CanExecute(parameter))
        {
            throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute.");
        }

        this._executionAction(parameter);
    }
}
using System;
using System.Windows.Data;
using System.Collections;
using System.Linq;

class clConverter : IMultiValueConverter
{
    public clConverter() { }

    #region IMultiValueConverter Methods
    public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return values.ToList();
    }

    public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        //throw new System.NotImplementedException();
        return ((IEnumerable)value).Cast<object>().Select(x => x.ToString()).ToArray();
    }
    #endregion
}
the clBaseAddEditDeleteViewModel which derives the clViewModelBase, this is to control my form display

class clBaseAddEditDeleteViewModel : clViewModelBase
{
    #region Variables
    private bool _IsListEnabled;
    private bool _IsDetailEnabled;
    #endregion

    #region Properties
    public bool IsListEnabled
    {
        get { return this._IsListEnabled; }
        set
        {
            this._IsListEnabled = value;
            OnPropertyChanged("IsListEnabled");
        }
    }
    public bool IsDetailEnabled
    {
        get { return this._IsDetailEnabled; }
        set
        {
            this._IsDetailEnabled = value;
            OnPropertyChanged("IsDetailEnabled");
        }
    }
    #endregion

    public void SetNormalUIDisplay()
    {
        this.IsListEnabled = true;

        this.IsAddMode = false;
        this.IsDetailEnabled = false;
    }

    public void SetEditUIDisplay()
    {
        this.IsAddMode = true;
        this.IsDetailEnabled = true;

        this.IsListEnabled = false;
    }
}
using System;
using System.Windows.Data;
using System.Collections;
using System.Linq;

class clConverter : IMultiValueConverter
{
    public clConverter() { }

    #region IMultiValueConverter Methods
    public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return values.ToList();
    }

    public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        //throw new System.NotImplementedException();
        return ((IEnumerable)value).Cast<object>().Select(x => x.ToString()).ToArray();
    }
    #endregion
}
the clBaseAddEditDeleteViewModel which derives the clViewModelBase, this is to control my form display

class clBaseAddEditDeleteViewModel : clViewModelBase
{
    #region Variables
    private bool _IsListEnabled;
    private bool _IsDetailEnabled;
    #endregion

    #region Properties
    public bool IsListEnabled
    {
        get { return this._IsListEnabled; }
        set
        {
            this._IsListEnabled = value;
            OnPropertyChanged("IsListEnabled");
        }
    }
    public bool IsDetailEnabled
    {
        get { return this._IsDetailEnabled; }
        set
        {
            this._IsDetailEnabled = value;
            OnPropertyChanged("IsDetailEnabled");
        }
    }
    #endregion

    public void SetNormalUIDisplay()
    {
        this.IsListEnabled = true;

        this.IsAddMode = false;
        this.IsDetailEnabled = false;
    }

    public void SetEditUIDisplay()
    {
        this.IsAddMode = true;
        this.IsDetailEnabled = true;

        this.IsListEnabled = false;
    }
}
class clPart : clViewModelBase, IDataErrorInfo
{
    #region Variables
    private string _ID;
    #endregion

    #region Properties
    /// <summary>
    /// Gets or Set the ID of this part
    /// </summary>
    public virtual string ID 
    { 
        get { return this._ID; }
        set 
        {
            this._ID = value;
            OnPropertyChanged("ID");
        } 
    }
    #endregion

    #region IDataErrorInfo Members

    string IDataErrorInfo.Error
    {
        get { return null; }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            return GetValidationError(propertyName);
        }
    }

    #endregion

    #region Validation

    /// <summary>
    /// All the properties that has to be validated
    /// </summary>
    static readonly string[] ValidatedProperties = 
    {
        "ID"
    };

    public bool IsValid
    {
        get
        {
            foreach (string property in ValidatedProperties)
                if (GetValidationError(property) != null)
                    return false;

            return true;
        }
    }

    private string GetValidationError(String propertyName)
    {
        string error = null;

        switch (propertyName)
        {
            case "ID":
                error = ValidatePartID();
                break;
        }

        return error;
    }

    private string ValidatePartID()
    {
        if (String.IsNullOrWhiteSpace(this.ID))
        {
            return "The Part ID cannot be empty.";
        }

        return null;
    }

    #endregion
}
class clPart : clViewModelBase, IDataErrorInfo
{
    #region Variables
    private string _ID;
    #endregion

    #region Properties
    /// <summary>
    /// Gets or Set the ID of this part
    /// </summary>
    public virtual string ID 
    { 
        get { return this._ID; }
        set 
        {
            this._ID = value;
            OnPropertyChanged("ID");
        } 
    }
    #endregion

    #region IDataErrorInfo Members

    string IDataErrorInfo.Error
    {
        get { return null; }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            return GetValidationError(propertyName);
        }
    }

    #endregion

    #region Validation

    /// <summary>
    /// All the properties that has to be validated
    /// </summary>
    static readonly string[] ValidatedProperties = 
    {
        "ID"
    };

    public bool IsValid
    {
        get
        {
            foreach (string property in ValidatedProperties)
                if (GetValidationError(property) != null)
                    return false;

            return true;
        }
    }

    private string GetValidationError(String propertyName)
    {
        string error = null;

        switch (propertyName)
        {
            case "ID":
                error = ValidatePartID();
                break;
        }

        return error;
    }

    private string ValidatePartID()
    {
        if (String.IsNullOrWhiteSpace(this.ID))
        {
            return "The Part ID cannot be empty.";
        }

        return null;
    }

    #endregion
}
<Window x:Class="Mvvm.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Helpers="clr-namespace:Mvvm.ViewModel.Helpers"
    xmlns:vm="clr-namespace:Mvvm.View"
    Title="MainWindow" Height="483" Width="922.725" WindowStartupLocation="CenterScreen">

<Window.Resources>
    <Helpers:clConverter x:Key="PartConverter" />
</Window.Resources>

<Grid x:Name="grdMain" RenderTransformOrigin="0.449,0.707">
    <Label x:Name="lblPartID" Content="Part ID" HorizontalAlignment="Left" Margin="382,86,0,0" VerticalAlignment="Top" Width="109"/>
    <TextBox x:Name="txtPartID" 
             IsEnabled="{Binding IsDetailEnabled}" 
             Text="{Binding Path=CurrentSelectedPart.ID, Mode=OneWay, ValidatesOnDataErrors=True}" 
             HorizontalAlignment="Left" Height="23" Margin="498,89,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="195"/>

    <ToolBar x:Name="tbSubMenu" HorizontalAlignment="Left" Margin="367,35,0,0" VerticalAlignment="Top" Width="548" Height="28">
        <Button x:Name="btnSaveDetails" IsEnabled="{Binding Path=IsDetailEnabled}" Command="{Binding SaveCommand}">
            <Image Source="Images/Save_6530.ico"/>

            <Button.CommandParameter>
                <MultiBinding Converter="{StaticResource PartConverter}" Mode="TwoWay">
                    <Binding ElementName="txtPartID" Path="Text"/>
                </MultiBinding>
            </Button.CommandParameter>
        </Button>

        <Separator/>
        <Button x:Name="btnEditDetail" Command="{Binding EditCommand}">
            <Image Source="Images/PencilTool_206.png"/>
        </Button>
        <Separator/>
        <Button x:Name="btnCancelDetail" IsEnabled="{Binding Path=IsDetailEnabled}" Command="{Binding CancelCommand}">
            <Image Source="Images/Cancel(build)_194_32.bmp"/>
        </Button>
    </ToolBar>

    <ListBox Name="listParts" HorizontalAlignment="Left" Height="402" Margin="10,40,0,0" IsEnabled="{Binding Path=IsListEnabled}"
             VerticalAlignment="Top" Width="352" SelectedItem="{Binding Path=CurrentSelectedPart}"
             ItemsSource="{Binding Path=CollectionPart}" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <vm:ucPart />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>
</Window>
<Window x:Class="Mvvm.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Helpers="clr-namespace:Mvvm.ViewModel.Helpers"
    xmlns:vm="clr-namespace:Mvvm.View"
    Title="MainWindow" Height="483" Width="922.725" WindowStartupLocation="CenterScreen">

<Window.Resources>
    <Helpers:clConverter x:Key="PartConverter" />
</Window.Resources>

<Grid x:Name="grdMain" RenderTransformOrigin="0.449,0.707">
    <Label x:Name="lblPartID" Content="Part ID" HorizontalAlignment="Left" Margin="382,86,0,0" VerticalAlignment="Top" Width="109"/>
    <TextBox x:Name="txtPartID" 
             IsEnabled="{Binding IsDetailEnabled}" 
             Text="{Binding Path=CurrentSelectedPart.ID, Mode=OneWay, ValidatesOnDataErrors=True}" 
             HorizontalAlignment="Left" Height="23" Margin="498,89,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="195"/>

    <ToolBar x:Name="tbSubMenu" HorizontalAlignment="Left" Margin="367,35,0,0" VerticalAlignment="Top" Width="548" Height="28">
        <Button x:Name="btnSaveDetails" IsEnabled="{Binding Path=IsDetailEnabled}" Command="{Binding SaveCommand}">
            <Image Source="Images/Save_6530.ico"/>

            <Button.CommandParameter>
                <MultiBinding Converter="{StaticResource PartConverter}" Mode="TwoWay">
                    <Binding ElementName="txtPartID" Path="Text"/>
                </MultiBinding>
            </Button.CommandParameter>
        </Button>

        <Separator/>
        <Button x:Name="btnEditDetail" Command="{Binding EditCommand}">
            <Image Source="Images/PencilTool_206.png"/>
        </Button>
        <Separator/>
        <Button x:Name="btnCancelDetail" IsEnabled="{Binding Path=IsDetailEnabled}" Command="{Binding CancelCommand}">
            <Image Source="Images/Cancel(build)_194_32.bmp"/>
        </Button>
    </ToolBar>

    <ListBox Name="listParts" HorizontalAlignment="Left" Height="402" Margin="10,40,0,0" IsEnabled="{Binding Path=IsListEnabled}"
             VerticalAlignment="Top" Width="352" SelectedItem="{Binding Path=CurrentSelectedPart}"
             ItemsSource="{Binding Path=CollectionPart}" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <vm:ucPart />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>
</Window>
base.DataContext = new clPartViewModel();
base.DataContext = new clPartViewModel();
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using Mvvm.Model;
using Mvvm.ViewModel.Helpers;

/// <summary>
/// 
/// </summary>
class clPartViewModel : clBaseAddEditDeleteViewModel
{
    #region Variables
    private ObservableCollection<clPart> _CollectionPart;
    private clPart _CurrentSelectedPart;

    private clDelegateCommand _EditCommand;
    private clDelegateCommand _SaveCommand;
    private clDelegateCommand _CancelCommand;
    #endregion

    #region Properties
    public ObservableCollection<clPart> CollectionPart
    {
        get { return this._CollectionPart; }
        set
        {
            if (this._CollectionPart == value)
                return;

            this._CollectionPart = value;
            OnPropertyChanged("CollectionPart");
        }
    }

    public clPart CurrentSelectedPart
    {
        get { return this._CurrentSelectedPart; }
        set
        {
            if (this._CurrentSelectedPart == value)
                return;

            this._CurrentSelectedPart = value;
            OnPropertyChanged("CurrentSelectedPart");
        }
    }

    public ICommand EditCommand
    {
        get 
        {
            if (this._EditCommand == null)
                this._EditCommand = new clDelegateCommand(new Action<object>(EditPart), null);

            return this._EditCommand; 
        }
    }

    public ICommand CancelCommand
    {
        get
        {
            if (this._CancelCommand == null)
                this._CancelCommand = new clDelegateCommand(new Action<object>(CancelPart), null);

            return this._CancelCommand;
        }
    }

    public ICommand SaveCommand
    {
        get
        {
            if (this._SaveCommand == null)
                this._SaveCommand = new clDelegateCommand(new Action<object>(SaveChanges), null);

            return this._SaveCommand;
        }
    }

    #endregion

    /// <summary>
    /// Cunstructor of the clPartViewModel class
    /// </summary>
    public clPartViewModel()
    {
        this.CollectionPart = new ObservableCollection<clPart>
        {
            new clPart { ID = "[0000001]" },
            new clPart { ID = "[0000002]" },
            new clPart { ID = "[0000003]" },
            new clPart { ID = "[0000004]" },
            new clPart { ID = "[0000005]" }
        };

        this.SetNormalUIDisplay();
    }


    private void EditPart(object obj)
    {
        this.SetEditUIDisplay();
    }

    private void CancelPart(object obj)
    {
        // TODO : revert changes
        this.SetNormalUIDisplay();
    }

    private void SearchByPartID(string ID)
    {
        this.CollectionPart.Where(x => x.ID == ID);
    }

    private void SaveChanges(object obj)
    {
        string[] Part = ((IEnumerable)obj).Cast<object>().Select(x => x.ToString()).ToArray();

        if (IsPartIdDuplicate(Convert.ToString(Part[0])))
            return;

        this._CurrentSelectedPart.ID = Convert.ToString(Part[0]);

        this.SetNormalUIDisplay();
    }

    /// <summary>
    /// Validate if we have duplicates ID's
    /// </summary>
    /// <returns>True, if we have duplicates</returns>
    public bool IsPartIdDuplicate(string IDToValidate)
    {
        foreach (clPart Parts in this.CollectionPart)
        {
            if (Parts.ID == IDToValidate && Parts.ID != this.CurrentSelectedPart.ID)
                return true;
        }

        return false;
    }
}
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using Mvvm.Model;
using Mvvm.ViewModel.Helpers;

/// <summary>
/// 
/// </summary>
class clPartViewModel : clBaseAddEditDeleteViewModel
{
    #region Variables
    private ObservableCollection<clPart> _CollectionPart;
    private clPart _CurrentSelectedPart;

    private clDelegateCommand _EditCommand;
    private clDelegateCommand _SaveCommand;
    private clDelegateCommand _CancelCommand;
    #endregion

    #region Properties
    public ObservableCollection<clPart> CollectionPart
    {
        get { return this._CollectionPart; }
        set
        {
            if (this._CollectionPart == value)
                return;

            this._CollectionPart = value;
            OnPropertyChanged("CollectionPart");
        }
    }

    public clPart CurrentSelectedPart
    {
        get { return this._CurrentSelectedPart; }
        set
        {
            if (this._CurrentSelectedPart == value)
                return;

            this._CurrentSelectedPart = value;
            OnPropertyChanged("CurrentSelectedPart");
        }
    }

    public ICommand EditCommand
    {
        get 
        {
            if (this._EditCommand == null)
                this._EditCommand = new clDelegateCommand(new Action<object>(EditPart), null);

            return this._EditCommand; 
        }
    }

    public ICommand CancelCommand
    {
        get
        {
            if (this._CancelCommand == null)
                this._CancelCommand = new clDelegateCommand(new Action<object>(CancelPart), null);

            return this._CancelCommand;
        }
    }

    public ICommand SaveCommand
    {
        get
        {
            if (this._SaveCommand == null)
                this._SaveCommand = new clDelegateCommand(new Action<object>(SaveChanges), null);

            return this._SaveCommand;
        }
    }

    #endregion

    /// <summary>
    /// Cunstructor of the clPartViewModel class
    /// </summary>
    public clPartViewModel()
    {
        this.CollectionPart = new ObservableCollection<clPart>
        {
            new clPart { ID = "[0000001]" },
            new clPart { ID = "[0000002]" },
            new clPart { ID = "[0000003]" },
            new clPart { ID = "[0000004]" },
            new clPart { ID = "[0000005]" }
        };

        this.SetNormalUIDisplay();
    }


    private void EditPart(object obj)
    {
        this.SetEditUIDisplay();
    }

    private void CancelPart(object obj)
    {
        // TODO : revert changes
        this.SetNormalUIDisplay();
    }

    private void SearchByPartID(string ID)
    {
        this.CollectionPart.Where(x => x.ID == ID);
    }

    private void SaveChanges(object obj)
    {
        string[] Part = ((IEnumerable)obj).Cast<object>().Select(x => x.ToString()).ToArray();

        if (IsPartIdDuplicate(Convert.ToString(Part[0])))
            return;

        this._CurrentSelectedPart.ID = Convert.ToString(Part[0]);

        this.SetNormalUIDisplay();
    }

    /// <summary>
    /// Validate if we have duplicates ID's
    /// </summary>
    /// <returns>True, if we have duplicates</returns>
    public bool IsPartIdDuplicate(string IDToValidate)
    {
        foreach (clPart Parts in this.CollectionPart)
        {
            if (Parts.ID == IDToValidate && Parts.ID != this.CurrentSelectedPart.ID)
                return true;
        }

        return false;
    }
}
<UserControl x:Class="Mvvm.View.ucPart"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="60" Width="230">

<Grid Name="DetailsGruid" HorizontalAlignment="Left" VerticalAlignment="Center" Width="230">
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="75" />
        <ColumnDefinition Width="150" />
    </Grid.ColumnDefinitions>

    <Border Grid.RowSpan="2" Grid.ColumnSpan="3" BorderBrush="DarkRed" 
            BorderThickness="1,1,1,1" CornerRadius="8,8,8,8" />

    <Image Name="ImgPart" Grid.RowSpan="2" Grid.Column="0" Margin="5" Source="/Mvvm;component/Images/Save_6530.ico" />

    <TextBox Grid.Row="0" Grid.Column="1" Name="txtID" Width="Auto" Margin="5"
                HorizontalAlignment="Stretch" VerticalAlignment="Center" IsEnabled="False"
                Text="{Binding Path=ID}" />
</Grid>
<UserControl x:Class="Mvvm.View.ucPart"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="60" Width="230">

<Grid Name="DetailsGruid" HorizontalAlignment="Left" VerticalAlignment="Center" Width="230">
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="75" />
        <ColumnDefinition Width="150" />
    </Grid.ColumnDefinitions>

    <Border Grid.RowSpan="2" Grid.ColumnSpan="3" BorderBrush="DarkRed" 
            BorderThickness="1,1,1,1" CornerRadius="8,8,8,8" />

    <Image Name="ImgPart" Grid.RowSpan="2" Grid.Column="0" Margin="5" Source="/Mvvm;component/Images/Save_6530.ico" />

    <TextBox Grid.Row="0" Grid.Column="1" Name="txtID" Width="Auto" Margin="5"
                HorizontalAlignment="Stretch" VerticalAlignment="Center" IsEnabled="False"
                Text="{Binding Path=ID}" />
</Grid>
using System.ComponentModel;

class clViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}
using System;
using System.Windows.Input;

class clDelegateCommand : ICommand
{
    private Action<object> _executionAction;
    private Predicate<object> _canExecutePredicate;

    public clDelegateCommand(Action<object> execute)
        : this(execute, null)
    { }

    public clDelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this._executionAction = execute;
        this._canExecutePredicate = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return this._canExecutePredicate == null ? true : this._canExecutePredicate(parameter);
    }

    public void Execute(object parameter)
    {
        if (!this.CanExecute(parameter))
        {
            throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute.");
        }

        this._executionAction(parameter);
    }
}
using System;
using System.Windows.Data;
using System.Collections;
using System.Linq;

class clConverter : IMultiValueConverter
{
    public clConverter() { }

    #region IMultiValueConverter Methods
    public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return values.ToList();
    }

    public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        //throw new System.NotImplementedException();
        return ((IEnumerable)value).Cast<object>().Select(x => x.ToString()).ToArray();
    }
    #endregion
}
the clBaseAddEditDeleteViewModel which derives the clViewModelBase, this is to control my form display

class clBaseAddEditDeleteViewModel : clViewModelBase
{
    #region Variables
    private bool _IsListEnabled;
    private bool _IsDetailEnabled;
    #endregion

    #region Properties
    public bool IsListEnabled
    {
        get { return this._IsListEnabled; }
        set
        {
            this._IsListEnabled = value;
            OnPropertyChanged("IsListEnabled");
        }
    }
    public bool IsDetailEnabled
    {
        get { return this._IsDetailEnabled; }
        set
        {
            this._IsDetailEnabled = value;
            OnPropertyChanged("IsDetailEnabled");
        }
    }
    #endregion

    public void SetNormalUIDisplay()
    {
        this.IsListEnabled = true;

        this.IsAddMode = false;
        this.IsDetailEnabled = false;
    }

    public void SetEditUIDisplay()
    {
        this.IsAddMode = true;
        this.IsDetailEnabled = true;

        this.IsListEnabled = false;
    }
}
class clPart : clViewModelBase, IDataErrorInfo
{
    #region Variables
    private string _ID;
    #endregion

    #region Properties
    /// <summary>
    /// Gets or Set the ID of this part
    /// </summary>
    public virtual string ID 
    { 
        get { return this._ID; }
        set 
        {
            this._ID = value;
            OnPropertyChanged("ID");
        } 
    }
    #endregion

    #region IDataErrorInfo Members

    string IDataErrorInfo.Error
    {
        get { return null; }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            return GetValidationError(propertyName);
        }
    }

    #endregion

    #region Validation

    /// <summary>
    /// All the properties that has to be validated
    /// </summary>
    static readonly string[] ValidatedProperties = 
    {
        "ID"
    };

    public bool IsValid
    {
        get
        {
            foreach (string property in ValidatedProperties)
                if (GetValidationError(property) != null)
                    return false;

            return true;
        }
    }

    private string GetValidationError(String propertyName)
    {
        string error = null;

        switch (propertyName)
        {
            case "ID":
                error = ValidatePartID();
                break;
        }

        return error;
    }

    private string ValidatePartID()
    {
        if (String.IsNullOrWhiteSpace(this.ID))
        {
            return "The Part ID cannot be empty.";
        }

        return null;
    }

    #endregion
}
<Window x:Class="Mvvm.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Helpers="clr-namespace:Mvvm.ViewModel.Helpers"
    xmlns:vm="clr-namespace:Mvvm.View"
    Title="MainWindow" Height="483" Width="922.725" WindowStartupLocation="CenterScreen">

<Window.Resources>
    <Helpers:clConverter x:Key="PartConverter" />
</Window.Resources>

<Grid x:Name="grdMain" RenderTransformOrigin="0.449,0.707">
    <Label x:Name="lblPartID" Content="Part ID" HorizontalAlignment="Left" Margin="382,86,0,0" VerticalAlignment="Top" Width="109"/>
    <TextBox x:Name="txtPartID" 
             IsEnabled="{Binding IsDetailEnabled}" 
             Text="{Binding Path=CurrentSelectedPart.ID, Mode=OneWay, ValidatesOnDataErrors=True}" 
             HorizontalAlignment="Left" Height="23" Margin="498,89,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="195"/>

    <ToolBar x:Name="tbSubMenu" HorizontalAlignment="Left" Margin="367,35,0,0" VerticalAlignment="Top" Width="548" Height="28">
        <Button x:Name="btnSaveDetails" IsEnabled="{Binding Path=IsDetailEnabled}" Command="{Binding SaveCommand}">
            <Image Source="Images/Save_6530.ico"/>

            <Button.CommandParameter>
                <MultiBinding Converter="{StaticResource PartConverter}" Mode="TwoWay">
                    <Binding ElementName="txtPartID" Path="Text"/>
                </MultiBinding>
            </Button.CommandParameter>
        </Button>

        <Separator/>
        <Button x:Name="btnEditDetail" Command="{Binding EditCommand}">
            <Image Source="Images/PencilTool_206.png"/>
        </Button>
        <Separator/>
        <Button x:Name="btnCancelDetail" IsEnabled="{Binding Path=IsDetailEnabled}" Command="{Binding CancelCommand}">
            <Image Source="Images/Cancel(build)_194_32.bmp"/>
        </Button>
    </ToolBar>

    <ListBox Name="listParts" HorizontalAlignment="Left" Height="402" Margin="10,40,0,0" IsEnabled="{Binding Path=IsListEnabled}"
             VerticalAlignment="Top" Width="352" SelectedItem="{Binding Path=CurrentSelectedPart}"
             ItemsSource="{Binding Path=CollectionPart}" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <vm:ucPart />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>
</Window>
base.DataContext = new clPartViewModel();
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using Mvvm.Model;
using Mvvm.ViewModel.Helpers;

/// <summary>
/// 
/// </summary>
class clPartViewModel : clBaseAddEditDeleteViewModel
{
    #region Variables
    private ObservableCollection<clPart> _CollectionPart;
    private clPart _CurrentSelectedPart;

    private clDelegateCommand _EditCommand;
    private clDelegateCommand _SaveCommand;
    private clDelegateCommand _CancelCommand;
    #endregion

    #region Properties
    public ObservableCollection<clPart> CollectionPart
    {
        get { return this._CollectionPart; }
        set
        {
            if (this._CollectionPart == value)
                return;

            this._CollectionPart = value;
            OnPropertyChanged("CollectionPart");
        }
    }

    public clPart CurrentSelectedPart
    {
        get { return this._CurrentSelectedPart; }
        set
        {
            if (this._CurrentSelectedPart == value)
                return;

            this._CurrentSelectedPart = value;
            OnPropertyChanged("CurrentSelectedPart");
        }
    }

    public ICommand EditCommand
    {
        get 
        {
            if (this._EditCommand == null)
                this._EditCommand = new clDelegateCommand(new Action<object>(EditPart), null);

            return this._EditCommand; 
        }
    }

    public ICommand CancelCommand
    {
        get
        {
            if (this._CancelCommand == null)
                this._CancelCommand = new clDelegateCommand(new Action<object>(CancelPart), null);

            return this._CancelCommand;
        }
    }

    public ICommand SaveCommand
    {
        get
        {
            if (this._SaveCommand == null)
                this._SaveCommand = new clDelegateCommand(new Action<object>(SaveChanges), null);

            return this._SaveCommand;
        }
    }

    #endregion

    /// <summary>
    /// Cunstructor of the clPartViewModel class
    /// </summary>
    public clPartViewModel()
    {
        this.CollectionPart = new ObservableCollection<clPart>
        {
            new clPart { ID = "[0000001]" },
            new clPart { ID = "[0000002]" },
            new clPart { ID = "[0000003]" },
            new clPart { ID = "[0000004]" },
            new clPart { ID = "[0000005]" }
        };

        this.SetNormalUIDisplay();
    }


    private void EditPart(object obj)
    {
        this.SetEditUIDisplay();
    }

    private void CancelPart(object obj)
    {
        // TODO : revert changes
        this.SetNormalUIDisplay();
    }

    private void SearchByPartID(string ID)
    {
        this.CollectionPart.Where(x => x.ID == ID);
    }

    private void SaveChanges(object obj)
    {
        string[] Part = ((IEnumerable)obj).Cast<object>().Select(x => x.ToString()).ToArray();

        if (IsPartIdDuplicate(Convert.ToString(Part[0])))
            return;

        this._CurrentSelectedPart.ID = Convert.ToString(Part[0]);

        this.SetNormalUIDisplay();
    }

    /// <summary>
    /// Validate if we have duplicates ID's
    /// </summary>
    /// <returns>True, if we have duplicates</returns>
    public bool IsPartIdDuplicate(string IDToValidate)
    {
        foreach (clPart Parts in this.CollectionPart)
        {
            if (Parts.ID == IDToValidate && Parts.ID != this.CurrentSelectedPart.ID)
                return true;
        }

        return false;
    }
}
<UserControl x:Class="Mvvm.View.ucPart"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="60" Width="230">

<Grid Name="DetailsGruid" HorizontalAlignment="Left" VerticalAlignment="Center" Width="230">
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="75" />
        <ColumnDefinition Width="150" />
    </Grid.ColumnDefinitions>

    <Border Grid.RowSpan="2" Grid.ColumnSpan="3" BorderBrush="DarkRed" 
            BorderThickness="1,1,1,1" CornerRadius="8,8,8,8" />

    <Image Name="ImgPart" Grid.RowSpan="2" Grid.Column="0" Margin="5" Source="/Mvvm;component/Images/Save_6530.ico" />

    <TextBox Grid.Row="0" Grid.Column="1" Name="txtID" Width="Auto" Margin="5"
                HorizontalAlignment="Stretch" VerticalAlignment="Center" IsEnabled="False"
                Text="{Binding Path=ID}" />
</Grid>
using System.ComponentModel;

class clViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}
using System;
using System.Windows.Input;

class clDelegateCommand : ICommand
{
    private Action<object> _executionAction;
    private Predicate<object> _canExecutePredicate;

    public clDelegateCommand(Action<object> execute)
        : this(execute, null)
    { }

    public clDelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this._executionAction = execute;
        this._canExecutePredicate = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return this._canExecutePredicate == null ? true : this._canExecutePredicate(parameter);
    }

    public void Execute(object parameter)
    {
        if (!this.CanExecute(parameter))
        {
            throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute.");
        }

        this._executionAction(parameter);
    }
}
using System;
using System.Windows.Data;
using System.Collections;
using System.Linq;

class clConverter : IMultiValueConverter
{
    public clConverter() { }

    #region IMultiValueConverter Methods
    public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return values.ToList();
    }

    public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        //throw new System.NotImplementedException();
        return ((IEnumerable)value).Cast<object>().Select(x => x.ToString()).ToArray();
    }
    #endregion
}
the clBaseAddEditDeleteViewModel which derives the clViewModelBase, this is to control my form display

class clBaseAddEditDeleteViewModel : clViewModelBase
{
    #region Variables
    private bool _IsListEnabled;
    private bool _IsDetailEnabled;
    #endregion

    #region Properties
    public bool IsListEnabled
    {
        get { return this._IsListEnabled; }
        set
        {
            this._IsListEnabled = value;
            OnPropertyChanged("IsListEnabled");
        }
    }
    public bool IsDetailEnabled
    {
        get { return this._IsDetailEnabled; }
        set
        {
            this._IsDetailEnabled = value;
            OnPropertyChanged("IsDetailEnabled");
        }
    }
    #endregion

    public void SetNormalUIDisplay()
    {
        this.IsListEnabled = true;

        this.IsAddMode = false;
        this.IsDetailEnabled = false;
    }

    public void SetEditUIDisplay()
    {
        this.IsAddMode = true;
        this.IsDetailEnabled = true;

        this.IsListEnabled = false;
    }
}
class clPart : clViewModelBase, IDataErrorInfo
{
    #region Variables
    private string _ID;
    #endregion

    #region Properties
    /// <summary>
    /// Gets or Set the ID of this part
    /// </summary>
    public virtual string ID 
    { 
        get { return this._ID; }
        set 
        {
            this._ID = value;
            OnPropertyChanged("ID");
        } 
    }
    #endregion

    #region IDataErrorInfo Members

    string IDataErrorInfo.Error
    {
        get { return null; }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            return GetValidationError(propertyName);
        }
    }

    #endregion

    #region Validation

    /// <summary>
    /// All the properties that has to be validated
    /// </summary>
    static readonly string[] ValidatedProperties = 
    {
        "ID"
    };

    public bool IsValid
    {
        get
        {
            foreach (string property in ValidatedProperties)
                if (GetValidationError(property) != null)
                    return false;

            return true;
        }
    }

    private string GetValidationError(String propertyName)
    {
        string error = null;

        switch (propertyName)
        {
            case "ID":
                error = ValidatePartID();
                break;
        }

        return error;
    }

    private string ValidatePartID()
    {
        if (String.IsNullOrWhiteSpace(this.ID))
        {
            return "The Part ID cannot be empty.";
        }

        return null;
    }

    #endregion
}
<Window x:Class="Mvvm.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Helpers="clr-namespace:Mvvm.ViewModel.Helpers"
    xmlns:vm="clr-namespace:Mvvm.View"
    Title="MainWindow" Height="483" Width="922.725" WindowStartupLocation="CenterScreen">

<Window.Resources>
    <Helpers:clConverter x:Key="PartConverter" />
</Window.Resources>

<Grid x:Name="grdMain" RenderTransformOrigin="0.449,0.707">
    <Label x:Name="lblPartID" Content="Part ID" HorizontalAlignment="Left" Margin="382,86,0,0" VerticalAlignment="Top" Width="109"/>
    <TextBox x:Name="txtPartID" 
             IsEnabled="{Binding IsDetailEnabled}" 
             Text="{Binding Path=CurrentSelectedPart.ID, Mode=OneWay, ValidatesOnDataErrors=True}" 
             HorizontalAlignment="Left" Height="23" Margin="498,89,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="195"/>

    <ToolBar x:Name="tbSubMenu" HorizontalAlignment="Left" Margin="367,35,0,0" VerticalAlignment="Top" Width="548" Height="28">
        <Button x:Name="btnSaveDetails" IsEnabled="{Binding Path=IsDetailEnabled}" Command="{Binding SaveCommand}">
            <Image Source="Images/Save_6530.ico"/>

            <Button.CommandParameter>
                <MultiBinding Converter="{StaticResource PartConverter}" Mode="TwoWay">
                    <Binding ElementName="txtPartID" Path="Text"/>
                </MultiBinding>
            </Button.CommandParameter>
        </Button>

        <Separator/>
        <Button x:Name="btnEditDetail" Command="{Binding EditCommand}">
            <Image Source="Images/PencilTool_206.png"/>
        </Button>
        <Separator/>
        <Button x:Name="btnCancelDetail" IsEnabled="{Binding Path=IsDetailEnabled}" Command="{Binding CancelCommand}">
            <Image Source="Images/Cancel(build)_194_32.bmp"/>
        </Button>
    </ToolBar>

    <ListBox Name="listParts" HorizontalAlignment="Left" Height="402" Margin="10,40,0,0" IsEnabled="{Binding Path=IsListEnabled}"
             VerticalAlignment="Top" Width="352" SelectedItem="{Binding Path=CurrentSelectedPart}"
             ItemsSource="{Binding Path=CollectionPart}" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <vm:ucPart />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>
</Window>
base.DataContext = new clPartViewModel();
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using Mvvm.Model;
using Mvvm.ViewModel.Helpers;

/// <summary>
/// 
/// </summary>
class clPartViewModel : clBaseAddEditDeleteViewModel
{
    #region Variables
    private ObservableCollection<clPart> _CollectionPart;
    private clPart _CurrentSelectedPart;

    private clDelegateCommand _EditCommand;
    private clDelegateCommand _SaveCommand;
    private clDelegateCommand _CancelCommand;
    #endregion

    #region Properties
    public ObservableCollection<clPart> CollectionPart
    {
        get { return this._CollectionPart; }
        set
        {
            if (this._CollectionPart == value)
                return;

            this._CollectionPart = value;
            OnPropertyChanged("CollectionPart");
        }
    }

    public clPart CurrentSelectedPart
    {
        get { return this._CurrentSelectedPart; }
        set
        {
            if (this._CurrentSelectedPart == value)
                return;

            this._CurrentSelectedPart = value;
            OnPropertyChanged("CurrentSelectedPart");
        }
    }

    public ICommand EditCommand
    {
        get 
        {
            if (this._EditCommand == null)
                this._EditCommand = new clDelegateCommand(new Action<object>(EditPart), null);

            return this._EditCommand; 
        }
    }

    public ICommand CancelCommand
    {
        get
        {
            if (this._CancelCommand == null)
                this._CancelCommand = new clDelegateCommand(new Action<object>(CancelPart), null);

            return this._CancelCommand;
        }
    }

    public ICommand SaveCommand
    {
        get
        {
            if (this._SaveCommand == null)
                this._SaveCommand = new clDelegateCommand(new Action<object>(SaveChanges), null);

            return this._SaveCommand;
        }
    }

    #endregion

    /// <summary>
    /// Cunstructor of the clPartViewModel class
    /// </summary>
    public clPartViewModel()
    {
        this.CollectionPart = new ObservableCollection<clPart>
        {
            new clPart { ID = "[0000001]" },
            new clPart { ID = "[0000002]" },
            new clPart { ID = "[0000003]" },
            new clPart { ID = "[0000004]" },
            new clPart { ID = "[0000005]" }
        };

        this.SetNormalUIDisplay();
    }


    private void EditPart(object obj)
    {
        this.SetEditUIDisplay();
    }

    private void CancelPart(object obj)
    {
        // TODO : revert changes
        this.SetNormalUIDisplay();
    }

    private void SearchByPartID(string ID)
    {
        this.CollectionPart.Where(x => x.ID == ID);
    }

    private void SaveChanges(object obj)
    {
        string[] Part = ((IEnumerable)obj).Cast<object>().Select(x => x.ToString()).ToArray();

        if (IsPartIdDuplicate(Convert.ToString(Part[0])))
            return;

        this._CurrentSelectedPart.ID = Convert.ToString(Part[0]);

        this.SetNormalUIDisplay();
    }

    /// <summary>
    /// Validate if we have duplicates ID's
    /// </summary>
    /// <returns>True, if we have duplicates</returns>
    public bool IsPartIdDuplicate(string IDToValidate)
    {
        foreach (clPart Parts in this.CollectionPart)
        {
            if (Parts.ID == IDToValidate && Parts.ID != this.CurrentSelectedPart.ID)
                return true;
        }

        return false;
    }
}
<UserControl x:Class="Mvvm.View.ucPart"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="60" Width="230">

<Grid Name="DetailsGruid" HorizontalAlignment="Left" VerticalAlignment="Center" Width="230">
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="75" />
        <ColumnDefinition Width="150" />
    </Grid.ColumnDefinitions>

    <Border Grid.RowSpan="2" Grid.ColumnSpan="3" BorderBrush="DarkRed" 
            BorderThickness="1,1,1,1" CornerRadius="8,8,8,8" />

    <Image Name="ImgPart" Grid.RowSpan="2" Grid.Column="0" Margin="5" Source="/Mvvm;component/Images/Save_6530.ico" />

    <TextBox Grid.Row="0" Grid.Column="1" Name="txtID" Width="Auto" Margin="5"
                HorizontalAlignment="Stretch" VerticalAlignment="Center" IsEnabled="False"
                Text="{Binding Path=ID}" />
</Grid>
deleted 160 characters in body; edited tags
Source Link
RubberDuck
  • 30.9k
  • 6
  • 71
  • 174

I do not know if this is a valid question to ask here, if not I apologize, let me know and I will delete this Post.

What I want to know :What I want to know:

Alright let's dig In : after learning I came to the conclusion that I need some Helpers class, so I've created some:

The clViewModelBase class which implements the INotifyPropertyChanged interface, this will help me to notify if any of my properties has changedAlright let's dig in! After learning, I did this so I don't havecame to implement the interface on each and every single ViewModel:conclusion that I need some Helpers class, so I've created some.

using System.ComponentModel;The clViewModelBase class which implements the INotifyPropertyChanged interface, this will help me to notify if any of my properties has changed, I did this so I don't have to implement the interface on each and every single View Model:

using System.ComponentModel;

class clViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

The clDelegateCommandclDelegateCommand class which implements the ICommandICommand interface, this will help me to fire up the events on the button :

Alright those were my helpers, now my Model that obviously derives the clViewModelBaseclViewModelBase and also the IDataErrorInfoIDataErrorInfo interface to manage my errors, ID cannot be empty etc...

Thanks for the feedbacks guys hope I'm on the right track

I do not know if this is a valid question to ask here, if not I apologize, let me know and I will delete this Post.

What I want to know :

Alright let's dig In : after learning I came to the conclusion that I need some Helpers class, so I've created some:

The clViewModelBase class which implements the INotifyPropertyChanged interface, this will help me to notify if any of my properties has changed, I did this so I don't have to implement the interface on each and every single ViewModel:

using System.ComponentModel;

class clViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

The clDelegateCommand class which implements the ICommand interface, this will help me to fire up the events on the button :

Alright those were my helpers, now my Model that obviously derives the clViewModelBase and also the IDataErrorInfo interface to manage my errors, ID cannot be empty etc...

Thanks for the feedbacks guys hope I'm on the right track

What I want to know:

Alright let's dig in! After learning, I came to the conclusion that I need some Helpers class, so I've created some.

The clViewModelBase class which implements the INotifyPropertyChanged interface, this will help me to notify if any of my properties has changed, I did this so I don't have to implement the interface on each and every single View Model:

using System.ComponentModel;

class clViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

The clDelegateCommand class which implements the ICommand interface, this will help me to fire up the events on the button :

Alright those were my helpers, now my Model that obviously derives the clViewModelBase and also the IDataErrorInfo interface to manage my errors, ID cannot be empty etc...

Source Link
Sam
  • 171
  • 1
  • 3
Loading