这几天偶染小恙,今天继续MVVM。
1、创建工程
首先先用VS2010新建一个Silverlight Application,
不要建立一个新的Web站点,我们使用OOB
然后更改工程的属性,添加OOB支持并增加信任权限,
此时在VS中F5运行,不会启动浏览器,而是直接以OOB方式启动应用。
2、设计界面
用Blend打开工程(为什么用Blend,因为MVVM的主要目标就是分离UI和UI逻辑,UI由美工设计,UI逻辑由程序员完成,这样就避免了程序员设计界面或者美工设计程序的苦恼),然后在工程中添加"UserControl with ViewModel",文件名改为FeedsView.xaml,
这个View用于显示每个订阅的名称。同理添加FeedItemsView用于显示每个订阅的新闻标题。
编译工程,打开MainPage.xaml,在Assets页中找到Locations->SilverReader.xap,
可以看见右侧显示了3个UserControl,把FeedsView和FeedsItemsView拖到MainPage中,然后调整一下布局,下面是XAML代码:
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d ="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc ="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local ="clr-namespace:SilverReader" x:Class ="SilverReader.MainPage"
mc:Ignorable ="d"
d:DesignHeight ="300" d:DesignWidth ="400" >
< Grid x:Name ="LayoutRoot" Background ="White" >
< Grid.ColumnDefinitions >
< ColumnDefinition Width ="*" />
< ColumnDefinition Width ="2*" />
</ Grid.ColumnDefinitions >
< local:FeedsView />
< local:FeedItemsView Grid.Column ="1" />
</ Grid >
</ UserControl >
我只是简单的使用了左右布局,MainPage不是主要的UI,充其量算UI的一个容器,真正的UI设计是在其他的两个View中。
3、FeedsView
下面是FeedsView的设计界面,
对象结构如下:
在第一行有一个Button用于显示第二行的UI,第二行包括一个TextBox用于输入RSS的网址和一个Button,第三行只是一个简单的ListBox,显示我们订阅的RSS标题。第二行平时应该是隐藏的,只有点击的第一行的Button后才应该显示,所以更改第二行的Grid的Visibility属性的值为Collapsed。
下一步要添加"添加订阅"Button的行为,当点击Button时,第二行的TextBox和Button能够显示,也就是将上述的Visibility属性改为Visible。以前的方法是直接双击Button,然后在后台代码中编写事件响应函数。现在依赖Blend的Behaviors,不用写一行代码就可以做到了。
首先在Blend的Assets中选择Behaviors->ChangePropertyAction,拖到"添加订阅"Button上,
然后设置Action的属性,TargetObject为第二行的Grid,PropertyName为Visibility,Value为Visible。
4、FeedsViewModel
下面就该ViewModel了,现在的ViewModel中还没有任何东西,先添加一个Feed的集合,还要让ViewModel实现INotifyPropertyChanged接口,代码如下:
{
public ObservableCollection < SyndicationFeed > Feeds { get ; private set ; }
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null )
{
PropertyChanged( this , new PropertyChangedEventArgs(info));
}
}
#endregion
}
5、添加设计数据
在Blend中选择Data页,添加一个新的Sample Data,
选择FeedsViewModel类,在工程中添加FeedsViewModelSampleData.xaml,
6、显示Feed标题
在FeedsView中的ListBox下添加ItemTemplate,用一个TextBox显示Title.Text,代码如下:
< ListBox.Resources >
< DataTemplate x:Key ="ListItemTitleTemplate" >
< StackPanel >
< TextBlock TextWrapping ="Wrap" Text =" {Binding Title.Text, Mode=OneWay} " />
</ StackPanel >
</ DataTemplate >
</ ListBox.Resources >
< ListBox.ItemTemplate >
< StaticResource ResourceKey ="ListItemTitleTemplate" />
</ ListBox.ItemTemplate >
</ ListBox >
然后选择"LayoutRoot"Grid,设置DataContent:
此时在设计界面就可以看见显示的结果了。
7、添加命令
在FeedsView中的"添加"Button下添加InvokeCommandAction,绑定Action的Command属性为AddFeedCommand
绑定Action的CommandParameter属性为TextBox的Text属性
然后在FeedsViewModel中添加如下代码:
{
Feeds = new ObservableCollection < SyndicationFeed > ();
AddFeedCommand = new ActionCommand( delegate ( object o)
{
string url = ( string ) o;
Uri feedUri;
Uri.TryCreate(url, UriKind.Absolute, out feedUri);
if (feedUri == null )
return ;
WebClient request = new WebClient();
request.DownloadStringCompleted +=
delegate ( object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null )
return ;
string xml = e.Result;
if (xml.Length == 0 )
return ;
StringReader stringReader = new StringReader(xml);
XmlReader reader = XmlReader.Create(stringReader);
SyndicationFeed feed = SyndicationFeed.Load(reader);
if (Feeds.Where(f => f.Title.Text == feed.Title.Text).ToList().Count > 0 )
return ;
Feeds.Add(feed);
};
request.DownloadStringAsync(feedUri);
});
}
public ObservableCollection < SyndicationFeed > Feeds { get ; private set ; }
public ICommand AddFeedCommand { get ; private set ; }
最后,看一下运行效果(从VS2010中启动)
8、总结
MVVM模式的主要要点:
- View的设计只是对XAML文件的编辑(通过GUI或者编辑器),没有后台代码,一般是由美工完成;
- ViewModel类直接对应View,View需要显示什么,ViewModel就提供什么,一般由程序员完成;
- ViewModel绑定到View的DataContent属性;
- 用户输入可以通过Blend提供的Behavior绑定到ViewModel上
- 通过d:DataContext和d:DesignData可以绑定设计时间数据,在设计器中就可以预览界面效果
实际上只是试验了MVVM中的V和VM,下一次结合FeedItemsView的实现试验Model。
源码可以从github上下载。