代码不仅仅是一种技术,更是一种艺术,在艺术的领域中有了自己的设计模式,有了自己的框架……,任何东西都不是与生俱来的,而是随着人们的认识慢慢发现和总结,大脑的思维就在于此,可以发现规律和总结规律。
MVC和MVVM这两种模式对于NET下的开发者应该不会陌生,关于这两个我在以前的博客中介绍过,今天心血来潮,自己一个个的模拟下,纸上得来终觉浅,绝知此事要躬行,就我这破脑子,看过的东西,很快就被忘记,愚人自有愚人的办法。
刚开始的时候,我们写程序,对于NET的开发人员来说,都是在一个事件下面完成一个一个任务的,比如下面的操作:
private void Button_Click(object sender, RoutedEventArgs e) {
string pName = Convert.ToDouble(textBox1.Text);
Double B = Convert.ToDouble(textBox2.Text);
// 省略
//在这里对A和B进行操作,如果对Name,B换一种操作,那么必须进入视图这里操作
}
这样有一个好处就是代码很容易阅读,逻辑也很清楚,但是对于大的项目来说,这个缺点就很明显,业务和视图耦合的太紧密,我们知道写程序有一个目的就是解耦,所以上面这个必须改动,那么这个时候我们需要将业务处理逻辑和视图分开来写,在视图中声明一个事件或者委托变量,然后在需要的进行处理的类中进行绑定,如下:
public event ClickEventHandler SendClick;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (SendClick != null)
{
MyArgs pArgs = new MyArgs();
pArgs.Name= textBox1.Text;
pArgs.B = Convert.ToDouble(textBox2.Text);
//A和B的操作在另外的类中,这样的好处,对于A,B可以有不同的操作,SendClick预先不知道自己如何对Name,B操作,如果要换一种操作,直接在绑定的类中进行修改,而不是在视图中修改,相比上面的,这里的优势不言而喻。
SendClick(this, pArgs);
}
}
如果是视图是程序的外观或者外衣,那么数据就是这个程序的血液,没有血液的程序还能有什么用么?视图的目的就是为了显示数据,而如何显示数据,这个就好比人的神经中枢,控制着人的一切,通着类似,程序中应该有一个控制器,所以我们将程序中所使用的数据完完全全的放在一个类中,称之为数据模型,好比下面这个一样:
class Persons
{
List<Person> persList = new List<Person>();
public Persons()
{
persList.Add(new Person("刘宇", 25));
persList.Add(new Person("刘宇1", 25));
persList.Add(new Person("刘宇2", 25));
}
public List<Person> GetPersons()
{
return persList;
}
}
数据和视图都有了,控制器将这两者联系起来:
public controller()
{
if (m_MVC == null)
{
m_MVC = new MVC(); //这是我们的视图
m_MVC.SendClick += new ClickEventHandler(m_MVC_SendClick);
}
datamodel = new Persons().GetPersons();//这是我们的数据
}
void m_MVC_SendClick(object sender, MyArgs e)
{
string sName=e.Name;
//这里对数据进行操作,这里我做了一个查找
List<Person> pPersonsTest;
if (!string.IsNullOrEmpty(sName))
{
pPersonsTest = new List<Person>();
foreach (var p in pPersons)
{
if (p.Name == sName)
pPersonsTest.Add(p);
//有了结果,可以在视图上显示,省略。
}
}
}
上面这个就算是一个MVC结构的小程序,数据和视图分开了,控制器用来对请求进行处理。
在MVVM中,这个可以进一步通过ICommand和绑定简化。
在视图中,我们也不需要对事件进行处理,也就是没有了Button_Click这样的方法,取之而来的是:
<Button Command="{Binding pCmd}" CommandParameter="{Binding ElementName=txtName, Path=Text}" Height="64" Grid.ColumnSpan="2" Grid.Row="1" Content="查找"></Button>
public partial class View : UserControl
{
ViewModel PVM= new ViewModel();
public View()
{
InitializeComponent();
this.DataContext = PVM;
}
}
这里出现了ViewModel,这个ViewModel实现了INotifyPropertyChanged接口,要不然怎么实现变更通知,这里有一个疑惑我没搞清楚,就是这个PropertyChanged事件是在什么时候赋值的,这个可能是系统实现的。
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;//
private string _str;
// private View _View;
public string Str
{
get { return _str; }
set
{
_str = value;
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(Str));
}//封装在一个函数里面
}
}
//View pView 在MVC中传入视图可以用来更新数据,但是在MVVM中通过绑定。
public ViewModel()
{
pPersons = new Persons().GetPersons();
pCmd = new MyCommand(search) { ISbool = true };
}
public ICommand pCmd { get; set; }
public List<Person> pPersons;
void search(string sName)
{
List<Person> pPersonsTest;
if (!string.IsNullOrEmpty(sName))
{
pPersonsTest = new List<Person>();
foreach (var p in pPersons)
{
if (p.Name == sName)
pPersonsTest.Add(p);
}
}
}
}
在ViewModel中有一个pCmd这个参数,这个是被绑定到按钮上的,当按钮接受到这个命令后,就会执行查询操作,代码如下:
class MyCommand:ICommand
{
public delegate void handler(string sName) ;
handler _handler;
public bool ISbool{get;set;}
public MyCommand(handler pHandler)
{
this._handler = pHandler;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (!String.IsNullOrEmpty(parameter.ToString()))
{
if (CanExecuteChanged!=null)
{
_handler(parameter.ToString());
}
}
}
}
对于这个CanExecuteChanged这个事件是什么时候有值的,我也不清楚,这个先放到这里暂且不管,在命令里其实也是通过回调函数来执行ViewModel中的方法,委托在这里着实起了很大的作用。在写程序的时候,主界面我们经常是布局,然后将试图作为用户控件,最后添加到主窗体中,在MVVM中,只需要在主窗体中声明试图,然后放到指定的位置即可:
xmlns:local="clr-namespace:WpfMVVM"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<local:View Grid.Column="1" Grid.Row="0"></local:View>
</Grid>
</StackPanel>
</Grid>
总算将这个写完了,但是对于这两个事件变量的值,我只是能跟踪到其有值,但是却不知道在哪里给赋值的?还望大家告知答案。
技术这个玩意,要深刻的体会,要深入的应用,这样才可能有出奇制胜的可能,在工作中也就有了事半功倍这个说法,其实上面讨论的很大程度上都是依靠委托,委托允许声明和实现的分离(其实接口也是可以的)。