using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Melphi.Mvvm.Base
{
/// <summary>
/// ViewModel模型基类(属性更改通知)
/// A base class for objects of which the properties must be observable.
/// </summary>
public class ViewModelBase : INotifyPropertyChanged
{
public ViewModelBase() { }
/// <summary>
/// 当任何子属性更改其值时激发的事件
/// </summary>
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
/// <summary>
/// 调用此函数以触发 <see cref="PropertyChanged"/> 事件
/// </summary>
/// <param name="name"></param>
//public void RaisePropertyChanged(string name)
//{
// PropertyChanged(this, new PropertyChangedEventArgs(name));
//}
/// <summary>
/// 调用此函数以触发 <see cref="PropertyChanged"/> 事件
/// </summary>
/// <param name="propertyName">(optional) The name of the property that changed.</param>
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// 更新属性,如果属性值没有发生变化,则不进行通知
/// </summary>
/// <typeparam name="T">属性类型【The type of the property that changed.】</typeparam>
/// <param name="field">返回的属性值【The field storing the property's value.】</param>
/// <param name="newValue">新的属性值【The property's value after the change occurred.】</param>
/// <param name="properName">属性名【(optional) The name of the property that changed.】</param>
protected void RaisePropertyChanged<T>(ref T field, T newValue, [CallerMemberName] string properName = null)
{
if (object.Equals(field, newValue))
return;
field = newValue;
RaisePropertyChanged(properName);
}
}
}
using System;
using System.Windows.Input;
namespace Melphi.Mvvm.Base
{
// 言论:
// If you are using this class in WPF4.5 or above, you need to use the GalaSoft.MvvmLight.CommandWpf
// namespace (instead of GalaSoft.MvvmLight.Command). This will enable (or restore)
// the CommandManager class which handles automatic enabling/disabling of controls
// based on the CanExecute delegate.
/// <summary>
/// A generic 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'. This class allows you to accept command parameters in the Execute and
/// CanExecute callback methods.
/// </summary>
/// <typeparam name="T">The type of the command parameter.</typeparam>
public class RelayCommand<T> : ICommand
{
/// <summary>
/// The execution logic.
/// </summary>
private Action<T> mExecute;
/// <summary>
/// The execution status logic.
/// </summary>
private Func<T, bool> mCanExecute;
/// <summary>
/// Initializes a new instance of the RelayCommand class that can always execute.
///
/// Exception:
/// T:System.ArgumentNullException:
/// If the execute argument is null.
/// </summary>
/// <param name="execute">The execution logic. IMPORTANT: If the action causes a closure, you must set keepTargetAlive to true to avoid side effects.</param>
/// <param name="keepTargetAlive">If true, the target of the Action will be kept as a hard reference, which might cause a memory leak. You should only set this parameter to true if the action is causing a closure. See http://galasoft.ch/s/mvvmweakaction.</param>
public RelayCommand(Action<T> execute, bool keepTargetAlive = false)
{
// TODO: Check the T Type
this.mExecute = execute;
}
/// <summary>
/// Initializes a new instance of the RelayCommand class.
///
/// Exception:
/// T:System.ArgumentNullException:
/// If the execute argument is null.
/// </summary>
/// <param name="execute">The execution logic. IMPORTANT: If the action causes a closure, you must set keepTargetAlive to true to avoid side effects.</param>
/// <param name="canExecute">The execution status logic. IMPORTANT: If the func causes a closure, you must set keepTargetAlive to true to avoid side effects.</param>
/// <param name="keepTargetAlive">If true, the target of the Action will be kept as a hard reference, which might cause a memory leak. You should only set this parameter to true if the action is causing a closure.</param>
public RelayCommand(Action<T> execute, Func<T, bool> canExecute, bool keepTargetAlive = false)
{
// TODO: Check the T Type
this.mExecute = execute;
this.mCanExecute = canExecute;
}
/// <summary>
/// Occurs when changes occur that affect whether the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged = (sender, e) => { };
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed,this object can be set to a null reference</param>
/// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter)
{
if (mCanExecute != null)
return mCanExecute.Invoke((T)parameter);
else
return true;
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed,this object can be set to a null reference</param>
public virtual void Execute(object parameter)
{
mExecute.Invoke((T)parameter);
}
/// <summary>
/// Raises the CanExecuteChanged event.
/// </summary>
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, null);
}
}
// 言论:
// If you are using this class in WPF4.5 or above, you need to use the GalaSoft.MvvmLight.CommandWpf
// namespace (instead of GalaSoft.MvvmLight.Command). This will enable (or restore)
// the CommandManager class which handles automatic enabling/disabling of controls
// based on the CanExecute delegate.
/// <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'.This class does not allow you to accept command parameters in the Execute and CanExecute callback methods.
/// </summary>
public class RelayCommand : ICommand
{
/// <summary>
/// The execution logic.
/// </summary>
private Action mExecute;
/// <summary>
/// The execution status logic.
/// </summary>
private Func<bool> mCanExecute;
/// <summary>
/// Initializes a new instance of the RelayCommand class that can always execute.
///
/// Exception:
/// T:System.ArgumentNullException:
/// If the execute argument is null.
/// </summary>
/// <param name="execute">The execution logic. IMPORTANT: If the action causes a closure, you must set keepTargetAlive to true to avoid side effects.</param>
/// <param name="keepTargetAlive">If true, the target of the Action will be kept as a hard reference, which might cause a memory leak. You should only set this parameter to true if the action is causing a closure.</param>
public RelayCommand(Action execute, bool keepTargetAlive = false)
{
this.mExecute = execute;
}
/// <summary>
/// Initializes a new instance of the RelayCommand class.
///
/// Exception:
/// T:System.ArgumentNullException:
/// If the execute argument is null.
/// </summary>
/// <param name="execute">The execution logic. IMPORTANT: If the action causes a closure, you must set keepTargetAlive to true to avoid side effects.</param>
/// <param name="canExecute">The execution status logic. IMPORTANT: If the func causes a closure, you must set keepTargetAlive to true to avoid side effects.</param>
/// <param name="keepTargetAlive">If true, the target of the Action will be kept as a hard reference, which might cause a memory leak. You should only set this parameter to true if the action is causing a closures.</param>
public RelayCommand(Action execute, Func<bool> canExecute, bool keepTargetAlive = false)
{
this.mExecute = execute;
this.mCanExecute = canExecute;
}
/// <summary>
/// Occurs when changes occur that affect whether the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged = (sender, e) => { };
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <param name="parameter">This parameter will always be ignored.</param>
/// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter)
{
if (mCanExecute != null)
return this.mCanExecute.Invoke();
else
return true;
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">This parameter will always be ignored.</param>
public virtual void Execute(object parameter)
{
this.mExecute.Invoke();
}
/// <summary>
/// Raises the GalaSoft.MvvmLight.Command.RelayCommand.CanExecuteChanged event.
/// </summary>
public void RaiseCanExecuteChanged()
{
}
}
}
My Model
public class Student : ViewModelBase
{
private int id;
public int Id
{
get { return id; }
set { id = value; RaisePropertyChanged(); }
}
private string name;
public string Name
{
get { return name; }
set { name = value; RaisePropertyChanged(); }
}
}
My ViewModel
public class DataGridViewModel : ViewModelBase
{
private readonly LocalDb mLocalDb;
public DataGridViewModel()
{
mLocalDb = new LocalDb();
QueryCommand = new RelayCommand(Query);
ResetCommand = new RelayCommand(() =>
{
SearchText = string.Empty;
Query();
});
EditCommand = new RelayCommand<int>(t => Edit(t));
DeleteCommand = new RelayCommand<int>(t => Delete(t));
AddCommand = new RelayCommand(Add);
Query();
}
private ObservableCollection<Student> students;
public ObservableCollection<Student> Students
{
get { return students; }
set { students = value; RaisePropertyChanged(); }
}
private string searchText = string.Empty;
public string SearchText
{
get { return searchText; }
set { searchText = value; RaisePropertyChanged(); }
}
public RelayCommand QueryCommand { get; set; }
public RelayCommand ResetCommand { get; set; }
public RelayCommand<int> EditCommand { get; set; }
public RelayCommand<int> DeleteCommand { get; set; }
public RelayCommand AddCommand { get; set; }
/// <summary>
/// Query
/// </summary>
private void Query()
{
var models = mLocalDb.GetStudentsByName(SearchText);
Students = new ObservableCollection<Student>();
if (models != null)
{
models.ForEach(arg =>
{
Students.Add(arg);
});
}
}
private void Edit(int id)
{
var model = mLocalDb.GetStudentsById(id);
if (model != null)
{
UserWindow window = new UserWindow(model);
var r = window.ShowDialog();
if (r.Value)
{
var stu = Students.FirstOrDefault(t => t.Id == id);
if (stu != null)
{
stu.Name = model.Name;
}
}
}
}
private void Delete(int id)
{
var model = mLocalDb.GetStudentsById(id);
if (model != null)
{
var r = MessageBox.Show($"确认删除当前学生信息{model.Name}?", "操作提示", MessageBoxButton.OK, MessageBoxImage.Question);
if (r == MessageBoxResult.OK)
{
mLocalDb.DeleteStudent(id);
this.Query();
}
}
}
private void Add()
{
Student student = new Student();
UserWindow window = new UserWindow(student);
var r = window.ShowDialog();
if (r.Value)
{
student.Id = Students.Max(t => t.Id) + 1;
mLocalDb.AddStudent(student);
this.Query();
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Deamon.View.PERSONALIZATION.DataGridUsage
{
/// <summary>
/// UserWindow.xaml 的交互逻辑
/// </summary>
public partial class UserWindow : Window
{
public UserWindow(Student student)
{
InitializeComponent();
this.DataContext = new
{
Model = student
};
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
}
private void OK_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
}
}
My Service
public class LocalDb
{
public LocalDb()
{
Init();
}
private void Init()
{
mStudents = new List<Student>();
for (int i = 1; i < 30; i++)
{
mStudents.Add(new Student() { Id = i, Name = $"Melphi" + i });
}
}
private List<Student> mStudents;
public List<Student> GetStudents()
{
return mStudents;
}
public void AddStudent(Student student)
{
mStudents.Add(student);
}
public void DeleteStudent(int id)
{
var model = mStudents.FirstOrDefault(t => t.Id == id);
if (model != null)
{
mStudents.Remove(model);
}
}
public List<Student> GetStudentsByName(string name)
{
return mStudents.Where(q => q.Name.Contains(name)).ToList();
}
public Student GetStudentsById(int id)
{
var model = mStudents.FirstOrDefault(q => q.Id == id);
if (model != null)
{
return new Student()
{
Id = model.Id,
Name = model.Name
};
}
else
return null;
}
}