MVVM之ViewModel

一、观察者模式(一对多的对象归属,当一个对象发生了改变,它的所属对象自动通知和改变)

    System.ComponentModel.INotifyPropertyChanged,该接口是观察者模式的部分,当属性的值发生改变后去通知订阅者(使用者).

    该接口仅包含一个事件event PropertyChangedEventHandler PropertyChanged,当类继承了该接口,并实现该事件即可完成通知,如下:

   public class Product : INotifyPropertyChanged
{
private string price;

public string Price
{
get { return price; }
set
{
price = value;
OnPropertyChanged("Price");
}
}


public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
If(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}


可以看到在属性的Set中调用OnPropertyChanged方法,然后触发PropertyChanged事件通知。

 

Systems.Collections.Specialized.INotifyCollectionChanged,和INotifyPropertyChanged类似,不过他是针对集合而不是一个对象,包含事件:

event NotifyCollectionChangedEventHandler CollectionChanged,接收一个委托事件

public delegate void NotifyCollectionChangedEventHandler(
Object sender,
NotifyCollectionChangedEventArgs e
)

事件参数NotifyCollectionChangedEventArgs的Action是一个类型为NotifyCollectionChangedAction的枚举类型,值包括如下:

值      含义
Add   当一个新项添加到集合
Remove 当一个项从集合移除
Replace 当一个项被另一个对象替换
Move    当一个项在集合中移动(WPF支持,Silverlight不支持)
Reset   当集合内容发生显著改变


  System.Collections.ObjectModel.ObservableCollection<T>,表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知.

  ReadOnlyObservableCollection<T>,表示一个只读的集合。

 

  CollectionViewSource为CollectionView 的XAML代理,表示用于分组、排序、筛选和导航数据集合的视图。

  Grouping(分组):
  通过CollectionViewSource的GroupDescriptions属性添加PropertyGroupDescription对象(参数为分组的依据)

CollectionViewSource appointmentsByDate = new CollectionViewSource();
appointmentsByDate.Source = appointments;
appointmentsByDate.GroupDescriptions.Add(new
PropertyGroupDescription("Date"));

  Sorting(排序):
  通过CollectionViewSource的SortDescriptions属性添加SortDescription对象(参数为排序依据和排序方向)

CollectionViewSource appointmentsByDate = new CollectionViewSource();
appointmentsByDate.Source = appointments;
appointmentsByDate.SortDescriptions.Add(new SortDescription("Location",
ListSortDirection.Ascending));
appointmentsByDate.SortDescriptions.Add(new
SortDescription("AttendeeSurname", ListSortDirection.Descending));

Filtering(过滤):

Filter属性是一个FilterEventHandler事件

    CollectionViewSource souece = new CollectionViewSource();
souece.Filter += new FilterEventHandler(souece_Filter);

e.Item每次为一个对象,Appointment为实体类型,然后通过e.Accepted(为true保留)来决定是否过滤掉

private void AppointmentsWithinTheNextMonth(object sender, FilterEventArgs e)
{
Appointment appointment = e.Item as Appointment;
if(appointment)
{
e.Accepted = DateTime.Now.AddMonths(1) > appointment.Date;
}
}


二、Thread处理

看个例子:

IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
int portNumber = 1500;
TcpListener server = new TcpListener(ipAddress, portNumber);
try
{
server.Start();
// This call blocks until a client is received
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Client connected!");
client.Close();
}
catch(SocketException ex)
{
Console.WriteLine("Socket Exception caught: {0}", ex);
}
finally
{
server.Stop();
}
Console.ReadKey();

上述代码去监听1500端口,通过AcceptTcpClient得到返回的结果,但是,如果这句代码不执行完毕,以下的过程是不会进行的,就容易造成线程锁死,面对这样的线程问题,可以使用异步来完成,当调用完方法之后立马返回,优化后如下:

 

static void Main(string[] args)
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
int portNumber = 1500;
TcpListener server = new TcpListener(ipAddress, portNumber);
try
{
server.Start();
// This call returns immediately, but there is no guarantee when a client
connects
server.BeginAcceptTcpClient(new AsyncCallback(ClientConnected), server);
}
catch(SocketException ex)
{
Console.WriteLine("Socket Exception caught: {0}", ex);
server.Stop();
}
Console.ReadKey();
}
private static void ClientConnected(IAsyncResult asyncResult)
{
TcpListener server = asyncResult.AsyncState as TcpListener;
TcpClient client = server.EndAcceptTcpClient(asyncResult);
Console.WriteLine("Client connected!");
server.Stop();
}


两者的区别在于

server.AcceptTcpClient()换成了
server.BeginAcceptTcpClient(new AsyncCallback(ClientConnected), server)
你猜的没错,前者的操作是同步的,后者的操作是异步的,同时给你起绑定了回调函数,在执行完毕后自动调用,也不会造成代码堵塞。


再看下面的例子:
public class ViewModel
{
public ViewModel()
{
Messages = new ObservableCollection<string>();
_timer = new Timer(5000D);
_timer.Elapsed += new ElapsedEventHandler(TimerElapsed);
_timer.Enabled = true;
_timer.Start();
}
public ObservableCollection<string> Messages
{
get;
private set;
}
void TimerElapsed(object sender, ElapsedEventArgs e)
{
Messages.Add("Timer fired!");
}
private Timer _timer;
}

在该类中,使用Timer来执行操作,我们把集合绑定到ListView(ListBox)上去,当Message属性改变后可以通知UI来更新,可是运行后会得到错误,NotSupportedException,因为跨线程了。

在Silverlight或者WPF中通过非UI线程来更新UI元素使用Dispather.Invoke(Delegate,object())或者Dispather.BeginInvoke(Delegate,object())来实现UI的更新,如下:
void TimerElapsed(object sender, ElapsedEventArgs e)
{
_uiDispatcher.Invoke(
(
Action)delegate
{
Messages.Add("Timer fired!");
}
);
}

但是上述方法的后果是需要给ViewModel传递UI的Dispather过去,然后才能使用。

除了这个方法,还可以使用接口来实现:
public ViewModel(IView view)
{
_view = view;
}
void TimerElapsed(object sender, ElapsedEventArgs e)
{
_view.AddMessage("Timer fired!");
}
IView为接口,包含方法AddMessage,修改ViewModel的构造函数传递Iview对象,同时让我们的Ui窗口实现于该接口,实现AddMessage方法,然后就可以正常工作。
public partial class MainWindow : Window, IView
{
public MainWindow()
{
InitializeComponent();
}
public void AddMessage(string message)
{
ViewModel viewModel = DataContext as ViewModel;
Dispatcher.Invoke(
(Action)delegate
{
viewModel.Messages.Add(message);
}
);
}
}




以上只是一些对ViewModel处理上的部分见解。
    

posted @ 2012-03-30 21:42  wangyafei_it  阅读(854)  评论(0编辑  收藏  举报