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>