人间风景开发日志 – 2 <DataModel,ViewModel>

Data Model

数据模型基本上算是一个人间API数据模型的简单封装,使用人间API的XML版本,并使用LINQ to XML读取数据。

namespace RenjianImageViewer.DataModel
{
    class UserModel
    {
        private long id;

        public string NickName { get; private set; }
        public string UserName { get; private set; }
        public string Gender { get; private set; }
        public string Avater { get; private set; }

        public UserModel(string xmlString) :
            this(XElement.Parse(xmlString))
        {
        }

        public UserModel(XElement userElement)
        {
            id = (long)userElement.Element("id");

            NickName = userElement.Element("name").Value;
            UserName = userElement.Element("screen_name").Value;
            Avater = userElement.Element("profile_image_url").Value;
            Gender = userElement.Element("gender").Value == "1" ? "DeepSkyBlue" : "Pink";
        }
    }
}

Gender这个地方采取了特殊处理,因为User的GUI是用DataModel直接绑定的,所以这里我先偷了个懒,直接放上了界面颜色数据,此处比较好的做法是做一个ValueConverter来处理。这算是一个Todo Item吧。

View Model

View Model层有两个重要的用来和UI绑定的类,一个是RenjianViewModel,另一个是ImageViewModel。

RenjianViewModel是一个General的ViewModel,包含了对其他各个DataModel,ViewModel以及Command的引用,它是用来和整个窗体的DataContext绑定的。

ImageViewModel是图片的ViewModel。因为严格来说,图片并不是一个数据模型,而我又希望能有一个business object来对图片进行统一管理,其中还包含了很多显示上的tricks,所以这边在ViewModel层单独创建了一个ImageViewModel。

另外一个辅助的ViewModel类是SavingStateViewModel,它用来和UI的Save状态绑定。当用户选择保存喜欢的图片到本地时,会显示一个正在save的层,这个层上面的数据就是从这个ViewModel绑定中得到的。

下面分别就两个比较重要的ViewModel类谈一下一些实现上的tricks。

RenjianViewModel

这个类没什么好说的,一堆对其他ViewModel和DataModel的引用而已,只是有一个方法要解释一下:

public void UpdateImagesIsLocalStatus()
{
    long temp = 0;
    var conversitionIDs = from imageModel in ConversitionImages
                          select imageModel.StatusID;

    var idList = conversitionIDs.ToList();

    foreach (var image in Images)
    {
        if (idList.Contains(image.StatusID))
        {
            image.IsLocal = true;
            idList.Remove(image.StatusID);
            if (idList.Count == 0)
            {
                break;
            }
        }
    }
}

这个方法是用来更新文件的本地存储状态的。

当你喜欢的图片被保存到本地之后,图片显示会使用本地的图片,从而提高速度,而图片列表中的thumbnail下方也会显示一个indicator来提示用户。这个方法就是来更新这个状态的。

为什么需要这个方法呢,因为在保存整个conversation的图片的时候,要在下面的列表中更新相关图片的状态,但是conversation图片列表的ImageViewModel全部是新建的,跟下面完整列表的ImageViewModel没有关系,所以需要在这里做一个check,保证所有在main list的ImageViewModel的状态都得到更新。为什么不直接在conversation图片列表中引用main list的ImageViewModel呢?因为在conversation图片列表出来的时候,下面的图片有可能还没有加载,与其部分同步,还不如直接分开,长痛不如短痛嘛。

ImageViewModel

这个ViewModel很复杂,甚至超过了我预期的庞大。

因为Windows Vista之后Microsoft采用了WIC来显示图片,WPF也是如此。而这个组件是具有设备无关特性的,说白了就是DPI相关的,也就是平时在Windows XP上看很正常的图片,如果DPI设置不对的话,在WPF的图片组件上显示就是变型失真。所以里面采用了一系列的tricky来fix这个问题。

System.Windows.Controls.Image imageControl = new System.Windows.Controls.Image();
BitmapImage image = new BitmapImage();
image.DownloadCompleted += (s, e) =>
{
    imageControl.MaxWidth = image.PixelWidth;
    imageControl.MaxHeight = image.PixelHeight;
};
image.BeginInit();
image.UriCachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.CacheIfAvailable);
image.CacheOption = BitmapCacheOption.OnDemand;
image.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
image.DownloadFailed += new EventHandler<System.Windows.Media.ExceptionEventArgs>(image_DownloadFailed);
image.UriSource = new Uri(ImageUrl);
image.EndInit();
if (!image.IsDownloading)
{
    imageControl.MaxWidth = image.PixelWidth;
    imageControl.MaxHeight = image.PixelHeight;
}
try
{
    imageControl.Stretch = System.Windows.Media.Stretch.Uniform;
    imageControl.StretchDirection = StretchDirection.Both;
    
    imageControl.Source = image;
    result = imageControl;
}
catch
{
    return null;
}

还有一个问题是WPF至今没有办法处理gif动画,所以在ImageViewModel里面,我又用了一系列的tricky来处理gif动画的渲染。

if (string.Compare(".gif", Path.GetExtension(ImageUrl), true) == 0)
{
    GIFImageControl g = new GIFImageControl();
    g.Stretch = System.Windows.Media.Stretch.Uniform;
    g.StretchDirection = StretchDirection.Both;
    g.AnimatedImageControl(ImageUrl);
    result = g;
}

GIFImageControl这个第三方的类处理了gif的动画问题,不过这里又引入了两个还没有解决的问题。

  • 第一个是因为GIFImageControl这个类并不是标准的WPF Image类,所以它失去了stretch的功能,那就是说,所有gif的图片都不能在RIV上拉伸,而只能以原始大小显示。
  • 第二个是,这个类本来只需要用来处理包含动画的gif,但是目前只采用了文件后缀判断,所以不包含动画的gif也会用这个类来渲染,而某些被强制改了后缀的gif动画却不能得到显示。

好了,DataModel和ViewModel的大致介绍就这么多,下一篇是关于GUI的实现的描述。

本篇对应代码下载:#53865

posted @ 2010-02-03 16:26  redjackwong  阅读(847)  评论(2编辑  收藏  举报