Click here to Skip to main content
15,886,038 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: , +
Hi folks,

in effect, I need to have a dictionary that user can edit and that's stored in app.config.

app.config doesn't like generics, it seems. At least I couldn't create a setting with a generic as its type. Not even by typing the fully qualified name instead of searching through the available types.
So I created a non-generic class that implements IXmlSerializable (modified this) to store the data.

For the user to change settings, I just wanted to throw Properties.Settings.Default at a PropertyGrid and let it handle all the editing. My class therefore has TypeConverter and Editor attributes set so that an instance of that class finally is assigned to a DataGridView.DataSource. Since setting a BindingList<> as DataSource works, I also implemented IBindingList on my class, but the DataGridView stays empty.

I can see in the debugger that the instance contains data, but DataGridView just doesn't show anything except its DarkGrey background.

What is a class supposed to look like to be able to work as DataGridView's DataSource?

My class is attached. It may seem like a code dump, but I just can't figure out what part may be the wrong (or missing) one.

C#
[System.ComponentModel.TypeConverter(typeof(DictionaryConverter))]
[System.ComponentModel.Editor(typeof(Pulsotronic.Model.DictionaryEditor), typeof(System.Drawing.Design.UITypeEditor))]
[XmlRoot("dictionary")]
public class SerializableDictionary : System.ComponentModel.IBindingList, IXmlSerializable
{
    #region Declarations

    private class LutEntry : System.ComponentModel.INotifyPropertyChanged
    {
        #region Declarations

        public ulong Key { get; private set; }
        private int _hashCode;
        private ulong _value;

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        #endregion


        #region Init

        public LutEntry(ulong key, ulong value)
        {
            this.Key = key;
            this.Value = value;

            this._hashCode = (int)(key % int.MaxValue);
        }

        #endregion


        #region Information Disclosure

        public ulong Value
        {
            get { return (_value); }
            set
            {
                _value = value;
                System.ComponentModel.PropertyChangedEventHandler eh = PropertyChanged;
                if (eh != null)
                {
                    eh(this, new System.ComponentModel.PropertyChangedEventArgs("Value"));
                }
            }
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(this, obj))
            {
                return (true);
            }

            LutEntry input = obj as LutEntry;
            if (obj == null)
            {
                return (false);
            }

            return (this.Key.Equals(input.Key));
        }

        // Todo: resolve Stack Overflow
        public static bool operator ==(LutEntry first, LutEntry second)
        {
            if (object.ReferenceEquals(first, second))
            {
                return (true);
            }

            if (
                (object)first == null
                || (object)second == null
            )
            {
                return (false);
            }

            return (first.Key == second.Key);
        }

        public static bool operator !=(LutEntry first, LutEntry second)
        {
            return (!(first == second));
        }

        public override int GetHashCode()
        {
            return (_hashCode);
        }

        #endregion
    }

    private System.ComponentModel.BindingList<LutEntry> _innerList = new System.ComponentModel.BindingList<LutEntry>();

    public event System.ComponentModel.ListChangedEventHandler ListChanged;

    #endregion


    #region Init

    public SerializableDictionary()
    {
        this.Add(123456789, 10001000);
        this.Add(123456798, 10011000);
        this.Add(123456879, 10021001);
        this.Add(123456897, 10031002);
    }

    #endregion


    #region Grow And Shrink

    private int Add(LutEntry value)
    {
        if (value == null)
        {
            throw new ArgumentNullException("value");
        }

        if (this.ContainsKey(value.Key))
        {
            throw new ArgumentException("Entry \"" + value.Key.ToString() + "\" is already present.");
        }

        _innerList.Add(value);
        int index = _innerList.Count - 1;

        System.ComponentModel.ListChangedEventHandler eh = ListChanged;
        if (eh != null)
        {
            eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.Reset, index));
        }

        return (index);
    }


    public int Add(object value)
    {
        LutEntry input = value as LutEntry;
        if (input == null)
        {
            throw new ArgumentException("\"" + value.ToString() + "\" is not of type \"LutEntry\"");
        }

        return (this.Add(input));
    }


    public int Add(ulong key, ulong value)
    {
        return (this.Add(new LutEntry(key, value)));
    }


    public object AddNew()
    {
        LutEntry newOne = _innerList.AddNew();

        System.ComponentModel.ListChangedEventHandler eh = ListChanged;
        if (eh != null)
        {
            eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.Reset, 0));
        }

        return (newOne);
    }

    public void Insert(int index, object value)
    {
        _innerList.Insert(index, (LutEntry)value);

        System.ComponentModel.ListChangedEventHandler eh = ListChanged;
        if (eh != null)
        {
            eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.ItemAdded, index));
        }
    }

    public void Remove(object value)
    {
        // Could have been passed a LutEntry object
        LutEntry input = value as LutEntry;

        // Or an ulong as well, in which case try
        //   to find the corresponding LutEntry.
        if (value is ulong)
        {
            foreach (LutEntry entry in _innerList)
            {
                if (entry.Key == (ulong)value)
                {
                    input = entry;
                    break;
                }
            }
        }

        // Delete the LutEntry in question.
        if (input != null)
        {
            _innerList.Remove(input);
        }

        System.ComponentModel.ListChangedEventHandler eh = ListChanged;
        if (eh != null)
        {
            eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.Reset, 0));
        }
    }

    public void RemoveAt(int index)
    {
        _innerList.RemoveAt(index);

        System.ComponentModel.ListChangedEventHandler eh = ListChanged;
        if (eh != null)
        {
            eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.ItemDeleted, index));
        }
    }

    public void Clear()
    {
        _innerList.Clear();

        System.ComponentModel.ListChangedEventHandler eh = ListChanged;
        if (eh != null)
        {
            eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.Reset, 0));
        }
    }

    #endregion


    #region Sort

    public void ApplySort(System.ComponentModel.PropertyDescriptor property, System.ComponentModel.ListSortDirection direction)
    {
        ((System.ComponentModel.IBindingList)_innerList).ApplySort(property, direction);

        System.ComponentModel.ListChangedEventHandler eh = ListChanged;
        if (eh != null)
        {
            eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.Reset, 0));
        }
    }

    public void RemoveSort()
    {
        ((System.ComponentModel.IBindingList)_innerList).RemoveSort();

        System.ComponentModel.ListChangedEventHandler eh = ListChanged;
        if (eh != null)
        {
            eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.Reset, 0));
        }
    }

    public void AddIndex(System.ComponentModel.PropertyDescriptor property)
    // http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.addindex.aspx
    // "The list must support this method. However, support for this method can be a nonoperation."
    {
        ((System.ComponentModel.IBindingList)_innerList).AddIndex(property);
    }

    public void RemoveIndex(System.ComponentModel.PropertyDescriptor property)
    // http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.removeindex.aspx
    // "The list must support this method. However, support for this method can be a nonoperation."
    {
        ((System.ComponentModel.IBindingList)_innerList).RemoveIndex(property);
    }

    public bool IsSorted
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).IsSorted); }
    }

    public System.ComponentModel.ListSortDirection SortDirection
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).SortDirection); }
    }

    public System.ComponentModel.PropertyDescriptor SortProperty
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).SortProperty); }
    }

    public bool SupportsSorting
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).SupportsSorting); }
    }

    #endregion


    #region Value Handling

    public ulong this[ulong key]
    {
        get
        {
            foreach (LutEntry entry in _innerList)
            {
                if (key == entry.Key)
                {
                    return (entry.Value);
                }
            }

            throw new ArgumentOutOfRangeException("Entry with key \"" + key.ToString() + "\" not found in list");
        }
    }


    public object this[int index]
    {
        get
        {
            return (_innerList[index]);
        }
        set
        {
            LutEntry newEntry = value as LutEntry;
            if (newEntry == null)
            {
                throw new ArgumentException("\"" + value.ToString() + "\" is null or not of type \"LutEntry\".");
            }
            if (this.ContainsKey(newEntry.Key))
            {
                throw new ArgumentException("Entry with key \"" + newEntry.Key + "\" already found in list");
            }

            _innerList[index] = newEntry;

            System.ComponentModel.ListChangedEventHandler eh = ListChanged;
            if (eh != null)
            {
                eh(this, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.ItemChanged, index));
            }
        }
    }


    public bool Contains(object value)
    {
        LutEntry input = value as LutEntry;
        if (input != null)
        {
            return (_innerList.Contains(input));
        }

        if (value is ulong)
        {
            foreach (LutEntry match in _innerList)
            {
                if (match.Key == (ulong)value)
                {
                    return (true);
                }
            }
        }

        return (false);
    }


    public bool ContainsKey(ulong key)
    {
        foreach (LutEntry entry in _innerList)
        {
            if (key == entry.Key)
            {
                return (true);
            }
        }

        return (false);
    }


    public int IndexOf(object value)
    {
        LutEntry input = value as LutEntry;
        if (input != null)
        {
            return (_innerList.IndexOf(input));
        }

        if (value is ulong)
        {
            for (int i = 0; i < _innerList.Count; i++)
            {
                if (_innerList[i].Key == (ulong)value)
                {
                    return (i);
                }
            }
        }

        return (-1);
    }

    #endregion


    #region XMLSerialize

    public void ReadXml(System.Xml.XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(ulong));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(ulong));

        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();

        if (wasEmpty)
            return;

        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");

            reader.ReadStartElement("key");
            ulong key = (ulong)keySerializer.Deserialize(reader);
            reader.ReadEndElement();

            reader.ReadStartElement("value");
            ulong value = (ulong)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();

            this.Add(key, value);

            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(ulong));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(ulong));

        foreach (LutEntry match in this._innerList)
        {
            writer.WriteStartElement("item");

            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, match.Key);
            writer.WriteEndElement();

            writer.WriteStartElement("value");
            //ulong value = this[key];
            valueSerializer.Serialize(writer, match.Value);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }

    public System.Xml.Schema.XmlSchema GetSchema()
    // http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.getschema.aspx
    // "This method is reserved and should not be used. When implementing the IXmlSerializable interface,
    //  you should return null (Nothing in Visual Basic) from this method, and instead, if specifying
    //  a custom schema is required, apply the XmlSchemaProviderAttribute to the class"
    {
        return null;
    }

    #endregion


    #region Information Disclosure

    public bool AllowEdit
    {
        get { return (_innerList.AllowEdit); }
    }

    public bool AllowNew
    {
        get { return (_innerList.AllowNew); }
    }

    public bool AllowRemove
    {
        get { return (_innerList.AllowRemove); }
    }

    public void CopyTo(Array array, int index)
    {
        _innerList.CopyTo((LutEntry[])array, index);
    }

    public int Count
    {
        get { return (_innerList.Count); }
    }

    public int Find(System.ComponentModel.PropertyDescriptor property, object key)
    {
        return (((System.ComponentModel.IBindingList)_innerList).Find(property, key));
    }

    public System.Collections.IEnumerator GetEnumerator()
    {
        return (_innerList.GetEnumerator());
    }

    public bool IsFixedSize
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).IsFixedSize); }
    }

    public bool IsReadOnly
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).IsReadOnly); }
    }

    public bool IsSynchronized
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).IsSynchronized); }
    }

    public bool SupportsChangeNotification
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).SupportsChangeNotification); }
    }

    public bool SupportsSearching
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).SupportsSearching); }
    }

    public object SyncRoot
    {
        get { return (((System.ComponentModel.IBindingList)_innerList).SyncRoot); }
    }


    public override string ToString()
    {
        if (_innerList.Count <= 0)
        {
            return ("[Empty]");
        }

        StringBuilder outputBuilder = new StringBuilder();
        foreach (LutEntry match in _innerList)
        {
            outputBuilder.AppendFormat("{0}{1}{2}->{3}", Environment.NewLine, match.Key.ToString(), "\t", match.Value.ToString());
        }
        outputBuilder.Remove(0, 1);

        return (outputBuilder.ToString());
    }

    #endregion
}
Posted
Updated 5-Jul-20 12:32pm

Hello,

How about simple solution to store key/value as string collection? App settings gives possibility to store array of string in collection of type:

C#
System.Collections.Specialized.StringCollection


As I see you need to store dictionary of <ulong,ulong> so, why not to create own simple custom class like this:

C#
public class KeyValueClass
{
    public ulong Key { get; set; }
    public ulong Value { get; set; }
}


Then store values in string array in this format:

Key,Value
123541098054,498902112342
423442223,2344842904

C#
public partial class Form1 : Form
{
    private List<keyvalueclass> _values;
    private BindingSource _bindingSource;

    public Form1()
    {
        InitializeComponent();
        _bindingSource = new BindingSource();
    }

    private void btnLoad_Click(object sender, EventArgs e)
    {
        _values = new List<keyvalueclass>();
        if (Properties.Settings.Default.Values != null)
        {
            foreach (var stringValue in Properties.Settings.Default.Values)
            {
                var stringItems = stringValue.Split(",".ToCharArray());
                var item = new KeyValueClass();
                item.Key = ulong.Parse(stringItems[0]);
                item.Value = ulong.Parse(stringItems[1]);
                _values.Add(item);
            }
        }

        _bindingSource.DataSource = _values;
        dataGridView1.DataSource = _bindingSource;
    }

    private void btnSave_Click(object sender, EventArgs e)
    {
        Properties.Settings.Default.Values = new System.Collections.Specialized.StringCollection();
        foreach (var item in _values)
        {
            Properties.Settings.Default.Values.Add(string.Join(",", item.Key, item.Value));
        }
        Properties.Settings.Default.Save();
    }
}</keyvalueclass></keyvalueclass>


Remember to check values are correct while reading from app settings and don't forget to implement INotifyPropertyChanged in KeyValueClass.

I hope it help you.
 
Share this answer
 
v2
a million years later... I'm a vb.net developer so if Ctype/Trycast have different implementations in C# just ignore me!I wonder if it had anything to do with your use of "as" casting (trycast in vb) which obviously returns null and is a better production method as you have it but for debugging you (or future reader) may want to try direct casting (CType or directcase in vb) just to find any potential bugs.
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900