首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > 移动开发 >

【WP开发】再谈View与ViewModel其间的解耦实现方式

2012-11-18 
【WP开发】再谈View与ViewModel之间的解耦实现方式MVVM模式的View与ViewModel的三大通讯方式:Binding Data(

【WP开发】再谈View与ViewModel之间的解耦实现方式

MVVM模式的View与ViewModel的三大通讯方式:Binding Data(实现数据的传递)、Command(实现操作的调用)和Attached Behavior(实现控件加载过程中的操作)。

这里再谈一下textbox双向绑定的问题以及绑定行为的一些东西。

?

一、textbox双向绑定取值异常问题:

就比如说这个textbox:

?

<TextBox Text="{Binding Mobile,  Mode=TwoWay}"                         BorderBrush="{StaticResource PhoneAccentBrush}"                          Background="{StaticResource PhoneBackgroundBrush}"/>

?

?很明显是个双向绑定的数据模式,我们在看后端vm代码

?

public void SubmitAction()        {            if (!this.ValidateData())                return;            App.LwApi.GetInternalService().BindMobile(this.Mobile, this.Code, (o, ev) =>            {                if (ev.Error != null)                {                    MessageHandleHelper.HandleError(ev.Error);                    return;                }                MessageBox.Show("手机号绑定成功!");                NavigationController.GoBack();            });        }

?

上述代码在提交命令触发时调用,这里有一个很奇怪的现象:当我们直接触发点击事件时,是无法同步的享有Mobile属性的值的,只有在用户再对textbox失焦后才能正常的。

?

这里的原因在于wp系统只在当前textbox触发LostFocus事件后才会对绑定的数据赋值。现在我们就需要将事件触发提前到每一次的TextChanged事件里进行触发。

?

将代码改成:

?

<TextBox Text="{Binding Mobile,  Mode=TwoWay, UpdateSourceTrigger=Explicit}" TextChanged="OnPhoneTextBoxTextChanged"                         BorderBrush="{StaticResource PhoneAccentBrush}"                          Background="{StaticResource PhoneBackgroundBrush}"/>

?

?注意上面的?UpdateSourceTrigger 属性:TwoWay是由绑定目标到绑定源方向,若实现绑定目标的值更改影响绑定源的值方式,只需要设置相应控件绑定时的UpdateSourceTrigger的值,其值有三种:1、PropertyChanged:当绑定目标属性更改时,立即更新绑定源。2、 LostFocus:当绑定目标元素失去焦点时,更新绑定源。3、 Explicit:仅在调用 UpdateSource 方法时更新绑定源。?多数依赖项属性的UpdateSourceTrigger 值的默认值为 PropertyChanged,而 TextBox 属性的默认值为 LostFocus。

?

我们现在把它设置为Explicit的意思就是要在cs文件里手动调用UpdateSource 方法才会更新绑定源数据。cs代码如下:

?

private void OnPhoneTextBoxTextChanged(object sender, TextChangedEventArgs e) {            TextBox ptb = sender as TextBox;            BindingExpression be = ptb.GetBindingExpression(TextBox.TextProperty);            be.UpdateSource(); }

?

?上面的代码就是手动调用更新绑定源的操作。

现在我们可以看到viewmodel层已经可以正确的操作属性了。

?

下面我们再看一下附加行为方式的解耦:

?

二、LongListSelector的回到顶部功能的Attached?Behavior方式实现

回到顶部的功能其实在codebehind代码里写起来异常的简单:只需一句ScrollTo就能轻松搞定,但我们的业务代码全部写在Viewmodel中,比如我们执行了refreshData的操作,要求list回到顶部,如果ScrollTo写在cb代码里,我们就必须要用notification方式去实现,然而这种方式既会搞得代码很复杂而且后期维护的成本也很大。(不要问我那为什么要把业务代码写vm里。。。vm里写业务代码能使得前端UI修改方便,而且移植起来也方便,甚至我们的win8 app就直接能套用wp的业务vm代码)

?

为了使这里我们要用到第三种解耦模式:Attached?Behavior方式也就是行为依赖的方式。我们会写一个ToTop的Behavior类。这里先看它的调用方式:

?

?

<toolkit:LongListSelector x:Name="StoryListBox" Background="Transparent"  ShowListHeader="False" ShowListFooter="False" IsFlatList="True" BufferSize="5.0"                              lwcontrols:ToTopBehavior.GoTop="{Binding GoTopFlag, Mode=TwoWay}"  ItemsSource="{Binding StoryList}" ItemTemplate="{StaticResource StoryTemplate}"  />

?看到这一句: lwcontrols:ToTopBehavior.GoTop="{Binding GoTopFlag, Mode=TwoWay}"这个就是Behavior的xml绑定方式;

再来看vm:

?

?

private bool goTopFlag;public bool GoTopFlag {            get { return goTopFlag; }            set            {                goTopFlag = value;                this.RaisePropertyChanged("GoTopFlag");            }}        public override void RefreshData(Action<object> callback = null){            base.RefreshData((o) =>            {                GoTopFlag = true;                callback.Invoke(o);            }); }
?

是的,我们需要一个双向绑定的属性,在vm中直接调用 GoTopFlag = true;就能使list直接回到顶部;看是如何做到的(ToTopBehavior.cs):

?

namespace Laiwang.Controls{    static public class ToTopBehavior    {        public static readonly DependencyProperty GoTopProperty = DependencyProperty.RegisterAttached(            "GoTop",            typeof(bool),            typeof(ToTopBehavior),            new PropertyMetadata(new PropertyChangedCallback(OnGoTopChanged)));        public static bool GetGoTop(DependencyObject obj)        {            return (bool)obj.GetValue(GoTopProperty);        }        public static void SetGoTop(DependencyObject obj, bool value)        {            obj.SetValue(GoTopProperty, value);        }        private static void OnGoTopChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)        {            LongListSelector control = obj as LongListSelector;            if (control != null)            {                if ((bool)args.NewValue)                {                    ScrollViewer scrollViewer = CommonHelper.FindChildOfType<ScrollViewer>(control);                    if (scrollViewer.VerticalOffset > 1)                    {                        object o = null;                        foreach (var item in control.ItemsSource)                        {                            if (item == null)                                return;                            o = item;                            break;                        }                        control.ScrollTo(o);                    }                }            }            SetGoTop(control, false);        }    }}

?像给依赖对象附加依赖属性一样,我们这里只是对一个已有的依赖对象附加一个新的依赖属性:GoTop,我们所有的操作都是在OnGoTopChanged事件做的,事实上我们这个方法是对双向模式中的PropertyChangedCallback,还记得一般的双向的UpdateSourceTrigger都是PropertyChanged吗,对于依赖属性也是这样的。我们只是简单的判断了 if ((bool)args.NewValue)并作出回滚的实现,注意后面我们又调用了SetGoTop(control, false),这样做很明显就是让它下一次的赋值为true时能执行刚才的代理事件。

?

vm和view的解耦虽说感觉是简单的事情复杂化了,但不管怎么说,这样做的好处也显而易见的,维护的成本降低了,wp和win8的移植也变的如此的简单美妙,只需对view层更改及简单的vm更改就能轻松搞定,ms这一套wpf的东西还是非常值得借鉴的。嗯,wp8我还是非常期待啊。。。

?

另外,近些天一直在做的ios开发对于事件的绑定及回调都是用delegate去做,感觉非常的麻烦。试图改变这种情况,貌似ios中KVO以及block的使用会改进这些,待我研究研究。。。

热点排行