在MVVM中进行序列化有很多麻烦

时间:2013-05-30 23:16:27

标签: c# serialization mvvm

我正在WPF中开发一个基于文本的游戏,我正在探索MVVM。目前我的项目中有2个模型,Adventurer和GameDate(我不关心这个应该或不应该是什么模型。我稍后会解决这个问题)。我有一个视图模型MainViewModel和一个视图MainViewMainView有按钮绑定保存/加载命令......而这就是我被卡住的地方。我非常想实现一种二进制序列化的形式;我有一个类ObjectSerializer,其功能及其相应部分位于MainViewModel中的“保存”和“加载”命令中,但我不知道如何“获取”对类的实例的访问权限需要序列化(在这种情况下是模型)因为我从来没有手动实例化它们中的任何一个。此外,我想找到一种方法将它们全部序列化在一个文件中(游戏的典型“保存”文件)。

在MVVM中处理过序列化的人是否应该引导我完成这个过程?我一直坚持这一切,但没有取得任何进展,这让我发疯。如果有人可以提供某种例子,我将永远负债累累。先感谢您;一个能让我超越这个驼峰的答案不会得不到重视。我真的在这里尝试......

ObjectSerializer.cs

    protected IFormatter iformatter;

    public ObjectSerializer()
    {
        this.iformatter = new BinaryFormatter();
    }

    public T GetSerializedObject(string filename)
    {
        if (File.Exists(filename))
        {
            Stream inStream = new FileStream(
                filename,
                FileMode.Open,
                FileAccess.Read,
                FileShare.Read);
            T obj = (T)this.iformatter.Deserialize(inStream);
            inStream.Close();
            return obj;
        }
        return default(T);
    }

    public void SaveSerializedObject(T obj, string filename)
    {
        Stream outStream = new FileStream(
            filename,
            FileMode.Create,
            FileAccess.Write,
            FileShare.None);
        this.iformatter.Serialize(outStream, obj);
        outStream.Close();
    }

1 个答案:

答案 0 :(得分:7)

在处理MVVM时,您的模型(M)将封装在ViewModel(VM)中,并且仅通过您在ViewModel上明确公开的方法和属性公开给View(V)。您的ViewModel将主要用作模型和View之间的适配器。您与应用程序层交互的所有逻辑(例如您可能需要的任何序列化)也将存放在ViewModel中,并与任何特定于UI的代码分开。这样可以更轻松地测试核心应用程序代码,而不会陷入您不一定关心的事情,例如TextBoxLabel中是否显示某些内容。这比在xaml.cs文件中发生类似对象序列化更为可取。

例如:

请考虑您的Adventurer类看起来像这样:

public class Adventurer { 
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Rank { get; set; } //Knight, Warlock, Whatever
}

您的MainViewModel可能如下所示:

(不要担心ViewModelBase,只是假设出于本示例的目的,它包含一些代码,允许您MainViewModel实施INotifyPropertyChanged,这是要求与WPF的绑定子系统配合使用)

public class MainViewModel : ViewModelBase {

    // When the ViewModel is created, populate _selectedAdventurer
    // with an empty Adventurer so that your form has something to  
    // bind to (and it can also be used as a "New" adventurer)
    private Adventurer _selectedAdventurer = new Adventurer();

    public string FirstName {
        get {
            return _selectedAdventurer.FirstName;
        }
        set {
            _selectedAdventurer.FirstName = value;
            // The following is implemented in our fictional
            // ViewModelBase, and essentially raises a notification
            // event to WPF letting it know that FirstName has changed
            OnPropertyChanged("FirstName");
        }
    }

    /*
       The remaining properties are implemented in a similar fashion, and in 
       this simple case are mainly acting as passthroughs to the view plus 
       a little bit of binding code
    */

    // These methods will house your save/load logic. I will assume
    // for simplicity that you already know how to wrap this logic in a
    // Command that can be bound to the view
    public void SaveAdventurer() {
       if(_selectedAdventurer != null) {
           SerializeToFile(_selectedAdventurer);
       }
    }

    public void LoadAdventurer() {
       _selectedAdventurer = LoadFromFile();
    }

    private void SerializeToFile(Adventurer adventurer) {
       // Use your serializer and save to file
    }

    private Adventurer LoadFromFile() {
       // Load from file and deserialize into Adventurer
    }

}

现在您已经有一个基本的ViewModel包装模型,一旦将UI控件设置为视图的DataContext,就可以轻松地将UI控件绑定到VM上的属性。

<TextBox Text="{Binding FirstName}" />
<TextBox Text="{Binding LastName}" />
<TextBox Text="{Binding Rank}" />
<Button Value="Save" Command="{Binding SaveCommand}" />
<Button Value="Load" Command="{Binding LoadCommand}" />

由于您已设置ViewModel来包装模型,并且已将ViewModel属性正确绑定到View,因此当用户在绑定到FirstName的文本框中输入值时,{{1}中的值将直接使用该输入更新。实质上,底层模型的状态将始终与UI中显示的值同步。然后,当用户点击标有_selectedAdventurer.FirstName的按钮时,您的Save将会执行,并且会触发代码将基础SaveCommand序列化为文件,数据库或其他内容。

这当然是一个非常简单的例子,主要用作数据输入表格,但希望它能帮助你掌握这个概念。为了更好地封装Adventurer绑定逻辑,您可以选择创建将向View公开的子Adventurer,而不是将属性直接放在AdventurerViewModel上。也许您会想要添加一个可以绑定到MainViewModel的属性IEnumerable<SaveGameFile> SavegameFiles,并允许用户选择要加载的文件。