文/zhouyinhui  出处/博客园

1,    避免在逻辑中引用界面元素,别把后台数据强加给UI

一个糟糕的案例

比如说主界面上有一个显示当前任务状态的标签label_TaskState,我们会时常更新该标签以便及时地将任务状态通知用户。那么很糟糕的一种假设是我们的代码中会到处充斥着这样的语句段this.label_TaskState .Content = this.GetStateDescription(TaskStates.Busy);(GetStateDescription方法会返回一段比较友好的描述信息)

当用户点击“暂停”按钮后,我们可能要这样来这样更新标签:

void btn_Pause_Clicked(object sender, RoutedEventArgs e)

{

                //do something to pause the task

    //update our lab

    this.label_TaskState .Content = this.GetStateDescription(TaskStates.Pause);

}

当由于某种原因我们的任务发生了错误时,我们可能会这样:

try

{

                //do something dangerous

}

catch(MyException e)

{

                this.label_TaskState .Content = this.GetStateDescription(TaskStates.Error);

}

finally

{

                //…

}

这样一来,我们的逻辑代码无数地方将引用label_TaskState这个UI元素。

现在有一些变化来了:(1)我们觉得使用一段文本来描述任务状态还是不够直观,所以我们决定使用美工提供的一系列漂亮图标来显示当前状态(图标中也可能含有文字,不过我们不关心)。(2)另外一个面板上(myPanel2)也要放置一个显示任务当前任务状态的标签label_TaskState2,只不过其仅仅显示文字描述就可以了。

那么我们在这么糟糕的环境下是不是应该像这样来修改我们的代码呢?

首先找出所有引用了label_TaskState的地方(比如有20个)。

然后将Lable类型的label_TaskState控件修改为Image类型的image_TaskState控件。

然后重复地将this.label_TaskState .Content = this.GetStateDescription(TaskStates.Busy);语句替换为this.image_TaskState.Source = this.GetStateImage(TaskStates.Busy);

别忘了每次都要在该语句后追加一条:this.label_TaskState2.Content = this.GetStateDescription(TaskStates.Busy);因为我们增加了一个标签。

多么令人上火的编程工作啊。

原因是,我们频繁地引用不稳定的界面元素(label_TaskState),严重地将界面和逻辑耦合在了一起,我们采用赋值的方式将后台数据(当前状态信息)强加给了UI元素。

解决方案:使用Binding,然UI元素从后台“拿”数据

一个简单的描述是:后台逻辑对前台UI说“要如何展现由前台决定,数据就在这里,要用就自己来拿吧”

“数据就在这里”

我们的数据是当前任务的状态信息,为了提供给UI元素和后台逻辑使用,我们决定提供一个TheTaskState属性来跟踪当前状态:

public TaskStates TheTaskState

{

    get

    {

        return (TaskStates)GetValue(TheTaskStateProperty);

    }

    set

    {

        SetValue(TheTaskStateProperty, value);

    }

}

public static readonly DependencyProperty TheTaskStateProperty =

            DependencyProperty.Register("TheTaskState", typeof(TaskStates),

                  typeof(Window1), new UIPropertyMetadata(TaskStates.Idle));

这样后台逻辑中要改变任务状态时只需要修改TheTaskState属性就可以了。

“要用就自己来拿吧”

当前台需要向用户展现该任务状态时只需要读取该属性,要实时跟踪就绑定吧。

<Label x:Name="label_TaskState"

          C />

“要如何展现由前台决定”

不对,我要展现给用户的可不是一些枚举值,而应该是图片或文本。

的确如此,所以我们要在绑定中加入转换器(或者数据模板,这里我们使用转换器):

    public class TaskStatesImageConverter:IValueConverter

    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            TaskStates state = (TaskStates)value;

            return GetImageFromTaskState(state);

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            throw new NotImplementedException();

        }

        private Image GetImageFromTaskState(TaskStates state)

        {

            Image image = new Image();

            image.Source = new BitmapImage(new Uri((int)state+".png", UriKind.Relative));

            return image;

        }

}

<Label x:Name="label_TaskState"

          C />

这样一来,我们的后台逻辑没有去引用UI元素并把数据强加给它,后台关注于如何任务状态及其更新,前台专注于如何向用户展现这些信息。当我们要更换其他展示方式时,只需更换一下转换器就可以了。
TOP