Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 反序列化到UI中_C#_User Interface_Deserialization - Fatal编程技术网

C# 反序列化到UI中

C# 反序列化到UI中,c#,user-interface,deserialization,C#,User Interface,Deserialization,我有一个程序集,其中包含几个UserControl对象,我希望能够通过应用程序UI保存/加载这些对象。为此,每个控件都实现了ISerializable接口,以自定义需要保存的字段 以下是该库的简化版本: namespace LibraryProject { using System; using System.Runtime.Serialization; using System.Windows.Forms; [Serializable] public

我有一个程序集,其中包含几个
UserControl
对象,我希望能够通过应用程序UI保存/加载这些对象。为此,每个控件都实现了
ISerializable
接口,以自定义需要保存的字段

以下是该库的简化版本:

namespace LibraryProject
{
    using System;
    using System.Runtime.Serialization;
    using System.Windows.Forms;

    [Serializable]
    public partial class UserControl1 : UserControl, ISerializable
    {
        public UserControl1()
        {
            InitializeComponent();
        }

        public UserControl1(SerializationInfo info, StreamingContext ctxt)
            : this()
        {
            this.checkBox1.Checked = info.GetBoolean("Checked");
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Checked", this.checkBox1.Checked);
        }
    }
}
客户端应用程序实例化了其中几个控件,并允许用户保存/加载各种
UserControl
配置。以下是该应用程序的简化版本:

namespace ApplicationProject
{
    using System;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Soap;
    using System.Windows.Forms;
    using LibraryProject;

    public partial class Form1 : Form
    {
        private const string filename = @"test.xml";

        //int hash1;
        //int hash2;

        public Form1()
        {
            InitializeComponent();

            //hash1 = this.ctrl1.GetHashCode();
        }

        private void SaveClick(object sender, EventArgs e)
        {
            using (var stream = File.Open(filename, FileMode.Create))
            {
                var formatter = new SoapFormatter();

                formatter.Serialize(stream, this.ctrl1);
            }
        }

        private void LoadClick(object sender, EventArgs e)
        {
            using (var stream = File.Open(filename, FileMode.Open))
            {
                var formatter = new SoapFormatter();

                this.ctrl1= (UserControl1)formatter.Deserialize(stream);
            }

            //hash2 = this.ctrl1.GetHashCode();
        }
    }
}
保存单击
,将值正确保存到文件中。 在
LoadClick
上,在调试器监视列表中正确更新了
复选框.Checked
,但UI不反映新值

我已尝试将呼叫添加到,但似乎没有任何效果

正如所料,
hash1
hash2
是不同的,但是
Form1
使用了正确的实例

我做错了什么,如何修复UI以显示正确的(更新的)值


编辑:另外,请注意,我需要处理多个配置文件,用户必须能够保存/加载到她选择的路径中,但我不确定,但我猜这是因为
InitializeComponent
没有被调用

但要解决您的问题,最好序列化代理。只需创建标记为[Serializable]的小代理类,并在序列化之前将属性从UserControl复制到代理。这样,您就不必乱搞
GetObjectData
——序列化过程只是假设代理的每个属性都应该序列化

反序列化过程将返回一个代理。代理只需要知道如何实例化UserControl并将属性映射回它

如果您定义了一个公共接口,则不必知道要反序列化的用户控件的具体类型:

var surrogate = formatter.Deserialize(stream) as ISerializationSurrogate;
UserControl uc = surrogate.Create();
this.Controls.Add(uc);
下面是一个代理的外观示例:

[Serializable]
public class MySurrogate: ISerializationSurrogate
{
    public MySurrogate() {}

    public MySurrogate(MyControl control)
    {
        CB1Checked = control.checkBox1.Checked;
    }

    public bool CB1Checked { get; set; }

    public Control Create()
    {
        var control = new MyControl();
        control.checkBox1.Checked = CB1Checked;
        return control;
    }
}
更新

实际上,我敢打赌问题在于您只是重新分配了
this.ctrl
,它不会改变表单上的控件。你实际上需要做的是这样的事情:

this.Controls.Remove(existingControl); // if it exists
this.Controls.Add(newControl);

但是您仍然应该使用序列化代理。他们让这类东西变得容易多了。

我暗自怀疑UI没有更新,因为它还不可见。我发现visible属性设置为false的控件有时会出现问题

您最好将数据序列化为某种结构化格式,然后使用它来实例化控件并填充控件。类似这样(未经测试):

公共类UserControlData
{
公共字符串类型{get;set;}//或程序集限定类型
公共列表控制值{get;set;}
}
公共类控制值
{
公共字符串名称{get;set;}
公共对象值{get;set;}
}
公共接口IControlPersistence
{
列出GetControlValues();
作废SetControlValues(列出controlValues);
}
然后序列化/反序列化用户控件数据,并根据定义实例化和设置值。在实例化并添加用户控件后,它可以独立于实例化更新其控件值,甚至只在它可见时更新(如果最终出现问题的话)

我还建议包装XmlSerializer并将其用于序列化(可能类似于
IObjectSerializer
/
XmlObjectSerializer:IObjectSerializer


希望这是有意义的。

最后,我添加了一个接口方法,将序列化数据推送到现有组件中,这是一个非常小的代码更改。为了在上面提供的模型中加以说明,以下是我所做的:

添加用于加载设置的界面。在我的实际项目中,这只是我的库中控件支持的界面上的一个新方法,由于应用程序仍在开发中,这不是一个突破性的变化。对于这个模型,我添加了接口
ICopyFrom

public interface ICopyFrom
{
    void CopyFrom(UserControl control);
}
更改所有可序列化组件以实现此接口。在模型中,它只是
UserControl1
。每个类上的实现将输入控件强制转换为自己的类型,然后复制所需的属性:

public void CopyFrom(UserControl control)
{
    using (var source = control as UserControl1)
    {
        if (source == null)
        {
            return;
        }

        // copy properties
        this.checkBox1.Checked= source.checkBox1.Checked;
    }
}
最后,更改
LoadClick
事件处理程序以使用此机制更新现有控件:

private void LoadClick(object sender, EventArgs e)
{
    using (var stream = File.Open(filename, FileMode.Open))
    {
        var formatter = new SoapFormatter();

        this.ctrl11.CopyFrom((UserControl1)formatter.Deserialize(stream));
    }
}

虽然这样做有效,但并不十分优雅。还有人建议将UI与数据分离。但是,进行此更改需要的时间比我现在花在这方面的时间要多,因此我将其用于下一次修订。

我个人建议序列化应用程序的数据,而不是UI状态。信息,也;SoapFormatter已过时,这是一个快照。使用dispatcher进行UI更新,而不是直接进行更新。this.Dispatcher.Invoke((操作)委托{this.checkBox1.Checked=info.GetBoolean(“Checked”);},null)@马克·格雷威尔:这是一个很好的关于数据处理的建议。谢谢,但仍然不能解决问题。@Anand:不幸的是,两种方法都没有运气。@Gustavo-“但仍然不能解决问题”,这就是为什么我没有将其作为答案发布;但是从根本上说,UI状态不应该被序列化,所以(主观上)您试图解决错误的问题。原因:平台和版本之间的UI变化;数据没有。也许更重要的是,UI API通常不被设计成这样的可序列化。这样做是可行的,但它会省去许多需要从原始控件克隆的其他默认设置。如果它们是默认设置,则不需要序列化-只需实例化控件即可获得它们。但是,如果有许多非默认设置是Windows窗体设计器的结果,那么您是对的(在这种情况下,您的答案处理起来不错)是的,这是一个错误
private void LoadClick(object sender, EventArgs e)
{
    using (var stream = File.Open(filename, FileMode.Open))
    {
        var formatter = new SoapFormatter();

        this.ctrl11.CopyFrom((UserControl1)formatter.Deserialize(stream));
    }
}