使用自定义Settings Provider序列化自定义类

时间:2017-12-28 18:53:33

标签: c# serialization xml-serialization

我正在尝试使用自定义设置提供程序保存以下类,但不断收到空引用错误(错误详情如下)。

[Serializable]
public class SoundClips
{

    [System.Xml.Serialization.XmlElementAttribute("Items")]
    public List<SoundKeyBind> Items { get; set; }

    public SoundClips()
    {
        Items = new List<SoundKeyBind>();
    }

}

[Serializable]
public class SoundKeyBind
{
    public string FilePath { get; set; }
    public string FileName { get; set; }
    public string KeyBindText { get; set; }
    public KeyPressedEventArgs KeyBind { get; set; }
}

保存者:

        dgvSoundBoard.DataSource = keyBinds.Items;
        Properties.Settings.Default.SoundBinds = keyBinds;
        Properties.Settings.Default.Save();

我在GitHub上的某个地方有以下设置提供程序,但找不到引用它的链接,抱歉。

public sealed class MySettingsProvider : SettingsProvider, IApplicationSettingsProvider
{
    private const string _rootNodeName = "settings";
    private const string _localSettingsNodeName = "localSettings";
    private const string _globalSettingsNodeName = "globalSettings";
    private const string _className = "MySettingsProvider";
    private XmlDocument _xmlDocument;

    private string _filePath
    {
        get
        {
            return Path.Combine(Path.GetDirectoryName(Application.ExecutablePath),
               string.Format("{0}.settings", ApplicationName));
        }
    }

    private XmlNode _localSettingsNode
    {
        get
        {
            XmlNode settingsNode = GetSettingsNode(_localSettingsNodeName);
            XmlNode machineNode = settingsNode.SelectSingleNode(Environment.MachineName.ToLowerInvariant());

            if (machineNode == null)
            {
                machineNode = _rootDocument.CreateElement(Environment.MachineName.ToLowerInvariant());
                settingsNode.AppendChild(machineNode);
            }

            return machineNode;
        }
    }

    private XmlNode _globalSettingsNode
    {
        get { return GetSettingsNode(_globalSettingsNodeName); }
    }

    private XmlNode _rootNode
    {
        get { return _rootDocument.SelectSingleNode(_rootNodeName); }
    }

    private XmlDocument _rootDocument
    {
        get
        {
            if (_xmlDocument == null)
            {
                try
                {
                    _xmlDocument = new XmlDocument();
                    _xmlDocument.Load(_filePath);
                }
                catch (Exception)
                {

                }

                if (_xmlDocument.SelectSingleNode(_rootNodeName) != null)
                    return _xmlDocument;

                _xmlDocument = GetBlankXmlDocument();
            }

            return _xmlDocument;
        }
    }

    public override string ApplicationName
    {
        get { return Path.GetFileNameWithoutExtension(Application.ExecutablePath); }
        set { }
    }

    public override string Name
    {
        get { return _className; }
    }

    public override void Initialize(string name, NameValueCollection config)
    {
        base.Initialize(Name, config);
    }

    public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
    {
        foreach (SettingsPropertyValue propertyValue in collection)
            SetValue(propertyValue);

        try
        {
            _rootDocument.Save(_filePath);
        }
        catch (Exception)
        {
            /* 
             * If this is a portable application and the device has been 
             * removed then this will fail, so don't do anything. It's 
             * probably better for the application to stop saving settings 
             * rather than just crashing outright. Probably.
             */
        }
    }

    public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
    {
        SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();

        foreach (SettingsProperty property in collection)
        {
            values.Add(new SettingsPropertyValue(property)
            {
                SerializedValue = GetValue(property)
            });
        }

        return values;
    }

    private void SetValue(SettingsPropertyValue propertyValue)
    {
        XmlNode targetNode = IsGlobal(propertyValue.Property)
           ? _globalSettingsNode
           : _localSettingsNode;

        XmlNode settingNode = targetNode.SelectSingleNode(string.Format("setting[@name='{0}']", propertyValue.Name));

        if (settingNode != null)
            settingNode.InnerText = propertyValue.SerializedValue.ToString();
        else
        {
            settingNode = _rootDocument.CreateElement("setting");

            XmlAttribute nameAttribute = _rootDocument.CreateAttribute("name");
            nameAttribute.Value = propertyValue.Name;

            settingNode.Attributes.Append(nameAttribute);

            // ######### ERROR OCCURS HERE #########
            settingNode.InnerText = propertyValue.SerializedValue.ToString();
            // ######### ERROR OCCURS HERE #########

            targetNode.AppendChild(settingNode);
        }
    }

    private string GetValue(SettingsProperty property)
    {
        XmlNode targetNode = IsGlobal(property) ? _globalSettingsNode : _localSettingsNode;
        XmlNode settingNode = targetNode.SelectSingleNode(string.Format("setting[@name='{0}']", property.Name));

        if (settingNode == null)
            return property.DefaultValue != null ? property.DefaultValue.ToString() : string.Empty;

        return settingNode.InnerText;
    }

    private bool IsGlobal(SettingsProperty property)
    {
        foreach (DictionaryEntry attribute in property.Attributes)
        {
            if ((Attribute)attribute.Value is SettingsManageabilityAttribute)
                return true;
        }

        return false;
    }

    private XmlNode GetSettingsNode(string name)
    {
        XmlNode settingsNode = _rootNode.SelectSingleNode(name);

        if (settingsNode == null)
        {
            settingsNode = _rootDocument.CreateElement(name);
            _rootNode.AppendChild(settingsNode);
        }

        return settingsNode;
    }

    public XmlDocument GetBlankXmlDocument()
    {
        XmlDocument blankXmlDocument = new XmlDocument();
        blankXmlDocument.AppendChild(blankXmlDocument.CreateXmlDeclaration("1.0", "utf-8", string.Empty));
        blankXmlDocument.AppendChild(blankXmlDocument.CreateElement(_rootNodeName));

        return blankXmlDocument;
    }

    public void Reset(SettingsContext context)
    {
        _localSettingsNode.RemoveAll();
        _globalSettingsNode.RemoveAll();

        _xmlDocument.Save(_filePath);
    }

    public SettingsPropertyValue GetPreviousVersion(SettingsContext context, SettingsProperty property)
    {
        // do nothing
        return new SettingsPropertyValue(property);
    }

    public void Upgrade(SettingsContext context, SettingsPropertyCollection properties)
    {
    }
}

设置提供程序在private void SetValue(SettingsPropertyValue propertyValue)尝试访问为空的propertyValue.SerializedValue.ToString()时发生错误。我在上面的代码中对它进行了评论,以帮助突出显示位置。错误是:

An unhandled exception of type 'System.NullReferenceException' occurred in SoundBoard.exe Object reference not set to an instance of an object.

在设置设计器中,我将设置的提供商设置为MySettingsProvider,并将漫游设置为True。我猜我的类的序列化声明有问题,但我尝试过一些东西,例如:

[Serializable]
[SettingsSerializeAs(SettingsSerializeAs.Xml)]
public class SoundClips

并且无法理解。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:0)

我设法通过打破课程并一次保存每个位来解决问题。问题是,SoundKeyBind类和在其中创建的KeyPressedEventArgs类/对象需要一个不带参数的构造函数。

即。我需要将以下内容添加到各自的类中:

public SoundKeyBind() { }

public KeyPressedEventArgs() { }