MVVM(Model-View-ViewModel)是xaml开发者常用的装逼模式,WP应用也不例外,除了我们常用的Prism和MVVM Light,现在还有个后起之秀叫做MVVM-Sidekick。用了它,就可以充分利用.NET4.5的async await来提升逼格了,这个轻量级框架也提供了非常便捷的代码模板,同样完成一个功能需要花费的精力比同类框架要少。具体介绍可以看它的官网:https://github.com/waynebaby/mvvM-Sidekick
但是,在DataContext的处理上,这个框架有些特殊。前几天我就爆掉了。
简单的说,我的需求就是在Windows Phone页面上显示一个进度指示器,把后台的Busy和Message属性绑定到ProgressIndicator上面。这个简单的任务在MVVM Light框架中与其他UI控件的绑定完全一致,可以这么写:
<shell:SystemTray.ProgressIndicator> <shell:ProgressIndicator IsIndeterminate="{Binding Busy}" IsVisible="{Binding Busy}" Text="{Binding Message}" /> </shell:SystemTray.ProgressIndicator>
后台代码:
public MainViewModel() { _ShowProgressBar = new RelayCommand(() => ShowProgressBar()); _HideProgressBar = new RelayCommand(() => HideProgressBar()); } private bool _busy; public bool Busy { get { return _busy; } set { _busy = value; RaisePropertyChanged("Busy"); } } private string _Message; public string Message { get { return _Message; } set { _Message = value; RaisePropertyChanged("Message"); } } public RelayCommand _ShowProgressBar { get; set; } private void ShowProgressBar() { Busy = true; Message = "Loading..."; } public RelayCommand _HideProgressBar { get; set; } private void HideProgressBar() { Busy = false; Message = string.Empty; }
然而xaml的这段代码在MVVM-Sidekick下是不起作用的。编译不会报错,运行也不会报错,就是没有效果。很明显是DataContext不对。
稍微看了下发现MVVM-Sidekick的DataContext是在LayoutGrid上的:
<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{StaticResource DesignVm}" > ...
如果你尝试用ElementName去做,也是不行的:
{Binding DataContext.Busy, ElementName=LayoutRoot}
其实思路没错,最关键的一步是要把ProgressIndicator定义在Resources里面(这个是我和框架作者神交之后得知的),虽然我也不知道为什么要这么弄,但觉得很牛逼的样子。
完整的代码:
<mvvm:MVVMPage .... .... <mvvm:MVVMPage.Resources> <vm:MovieListView_Model x:Key="DesignVm" /> <shell:ProgressIndicator x:Key="idk" IsVisible="{Binding DataContext.Busy, ElementName=LayoutRoot}" IsIndeterminate="{Binding DataContext.Busy, ElementName=LayoutRoot}" Text="{Binding DataContext.Message, ElementName=LayoutRoot}" /> </mvvm:MVVMPage.Resources> <shell:SystemTray.ProgressIndicator> <Binding Source="{StaticResource idk}"/> </shell:SystemTray.ProgressIndicator>
这个是100%可以成功的。有图有真相:
如果不定义在Resource里,而把Source指定为StaticResource DesignVm的话,只能在独立页面上work,如果你的页面是被navitgateTo的,还是会爆的。所以不要用下面这种写法。
<shell:SystemTray.ProgressIndicator> <shell:ProgressIndicator IsVisible="{Binding Busy, Source={StaticResource DesignVm}}" IsIndeterminate="{Binding Busy, Source={StaticResource DesignVm}}" Text="{Binding Message, Source={StaticResource DesignVm}}" /> </shell:SystemTray.ProgressIndicator>