代码改变世界

[Silverlight]MVVM+MEF框架Jounce练习(1)

2012-12-01 09:55  slmk  阅读(1474)  评论(1编辑  收藏  举报

光学不练,对一些概念的理解就只能停留在表面了。这个系列主要记录自己对Jounce框架的理解和测试代码,以加深对MVVM和Silverlight的一些概念的理解。本例主要测试Jounce框架中View和ViewModel的通信。

MVVM的优点

我认为Model、View和ViewModel的设计原则:模型、视图和视图模型分离,主要有两方面的优点:

1. 便于团队协作和单元测试。开发人员开发出适合业务逻辑的Model和ViewModel,并在没有View的情况下就可以做单元测试;美工人员根据ViewModel设计好View;测试人员可以只根据View、ViewModel或者Model其中之一的情况下写测试代码。还实现了更好的模块化,尽管有时我们都是一个人在做这些事情。

2.XAML系的特点,采用MVVM的模式,可以减少很多繁琐的界面更新的代码,例如对象属性改变后,在ViewModel中调用适当的方法,就可以完成界面的更新,这样我们可以更多的关心业务逻辑。相对于传统的Winforms,更适合采用这种设计模式。

思维方式的改变

我以前直接在View的后台代码里,想怎么控制界面都可以。现在MVVM了,给我整分离了,ViewModel和View不能相互引用对方,两者如何通讯呢?例如我点击Save按钮,想要显示一个等待界面给用户:以前多方便,直接调用一个等待界面代码即可。现在呢?点击事件抽象成Command,绑定由ViewModel来处理,而ViewModel不能引用View,又如何改变View的状态呢?Jounce又是如何做到的呢?我们想达到的是这种效果:

可以有两种方法:

1. 利用View和ViewModel的契约

这种契约其实就是绑定,View界面元素的属性绑定到ViewModel的属性上,这样就形成了一种约定:ViewModel的属性会影响元素显示,反过来也一样(如果是双向绑定)。

View代码可以这样写:

<Grid x:Name="LayoutRoot" Background="White">
        <StackPanel x:Name="Sp">
            <TextBlock FontSize="36" Text="{Binding Welcome}"/>
            <Button Width="100" Margin="4" Content="Save" Command="{Binding SaveCommand}"/>
        </StackPanel>
        <TextBlock Text="正在保存..." Visibility="{Binding IsSaving}" Foreground="Blue" Height="50" x:Name="tbSaving" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

ViewModel这样写:

[ExportAsViewModel(typeof (MainViewModel))]
    public class MainViewModel : BaseViewModel
    {
        public MainViewModel()
        {
            SaveCommand = new ActionCommand<object>(o =>
            {
                IsSaving = Visibility.Visible;
            });
        }

        public string Welcome
        {
            get { return InDesigner ? "Jounce Design-time View" : "Welcome to Jounce."; }
        }

        public IActionCommand<object> SaveCommand { get; set; }

        Visibility _isSaving=Visibility.Collapsed;
        public Visibility IsSaving
        {
            get
            {
                return _isSaving;
            }
            set
            {
                _isSaving = value;
                RaisePropertyChanged("IsSaving");
            }
        }
    }

Jounce的BaseViewModel实现了INotifyPropertyChanged接口,通过调用RaisePropertyChanged方法可以在属性改变时,通知界面更新。

为了简单,这里实现的很简陋,比较优雅的实现可以使用BusyIndicator控件。

2.利用Jounce的GoToVisualState

 Jounce的BaseViewModel提供了GoToVisualState方法,可以在不引用View的情况下改变View的状态。

View代码:

<Grid x:Name="LayoutRoot" Background="White">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="Commstates">
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="Saving">
                    <Storyboard>
                        <DoubleAnimation Duration="0" Storyboard.TargetName="tbSaving" Storyboard.TargetProperty="Opacity" To="1"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <StackPanel x:Name="Sp">
            <TextBlock FontSize="24" Text="{Binding Welcome}"/>
            <Button Width="100" Margin="4" Content="Save" Command="{Binding SaveCommand}"/>
        </StackPanel>
        <TextBlock Text="正在保存..." Foreground="Blue" Height="50" x:Name="tbSaving" Opacity="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

ViewModel代码:

public MainViewModel()
{
     SaveCommand = new ActionCommand<object>(o =>{
           GoToVisualState("Saving", true);
      }); 
}

参考:

[Silverlight]MVVM+MEF框架Jounce学习(2):标记和绑定

更深刻的理解Jounce,可以参考Jounce的源代码。