Object reference not set to an instance of an object

I’m working with C# using Microsoft Visual Studios in .NET 4.8.1 on WinForms and have been trying to save & load Form settings using .XML files.

My goal would be to read the .XML attributes into an array. Then I can step through that array and load their values onto form objects.

For example:

(.XML file)
<settings>
  <setting txb="1" />
  <setting txb="2" />
  <setting txb="3" />
</settings>

(WinForm code)
private void xml(string input)
{
    string[] settings = new string[3];
    int count = 0;

    XmlDocument xmlDoc = new XmlDocument();

    if (input == "Load")
    {
        xmlDoc.Load("./xml/settings.xml");
        XmlNodeList functionNodes = xmlDoc.SelectNodes("//settings/setting");

        foreach (XmlNode functionNode in functionNodes)
        {
            settings[count] = functionNode.Attributes["txb"].Value;
            count++;
        }

        count = 0;

        foreach (Control txt in this.Controls)
        {
            if (txt is TextBox)
            {
                txt.Text = settings[count];
                count++;
            }
        }
    }
}

I don’t know for certain if the second foreach loop works but I haven’t found an alternative way to address every textbox on the form.

The code stops execution here with the before-mentioned error:
settings[count] = functionNode.Attributes["txb"].Value;

If I instead point it to a Form Object it runs fine:
textBox1.Text = functionNode.Attributes["txb"].Value;

I don’t understand what it wants. Some of the error details talk about XML returning a null value:
System.Xml.XmlAttributeCollection.this[string].get returned null.

There is a method I’ve used up to now to make it work but I hate it:

foreach (XmlNode functionNode in functionNodes)
{
    settings[count] = functionNode.Attributes["txb"].Value;

    if (count == 0) { textBox1.Text = functionNode.Attributes["txb"].Value; }
    else if (count == 1) { textBox2.Text = functionNode.Attributes["txb"].Value; }
    else if (count == 2) { textBox3.Text = functionNode.Attributes["txb"].Value; }

    count++
}

I would really like a method that is cleaner to look at, steps through an array, and is objectively more efficient. I’ve read that apparently .JSON files are more popular in this application but I’ve never worked with them.

I’m not married to .XML, I’m open to other solutions so long as I can understand them.

I just came up with this as a potential solution:

private void xml(string input)
{
    string[] settings = new string[45];
    int count = 0;

    XmlDocument xmlDoc = new XmlDocument();

    if (input == "Load")
    {
        xmlDoc.Load("./xml/settings.xml");
        XmlNodeList functionNodes = xmlDoc.SelectNodes("//settings/setting");

        foreach (XmlNode functionNode in functionNodes)
        {
            settings[count] = functionNode.Attributes["object"].Value;
            count++;
        }

        count = 0;

        foreach (Control obj in Controls)
        {
            if (obj is TextBox)
            {
                obj.Text = settings[count];
                count++;
            }
        }
    }

    else if (input == "Save")
    {
        XmlNode rootNode = xmlDoc.CreateElement("settings");
        xmlDoc.AppendChild(rootNode);

        foreach (Control obj in Controls)
        {
            if (obj is TextBox)
            {
                XmlNode functionNode = xmlDoc.CreateElement("setting");
                XmlAttribute attribute = xmlDoc.CreateAttribute("object");

                attribute.Value = obj.Text;

                functionNode.Attributes.Append(attribute);
                rootNode.AppendChild(functionNode);
            }
        }

        xmlDoc.Save("./xml/settings.xml");
    }

I see pros and cons to this. It does work but it doesn’t offer as much control. Thoughts? Criticism?

That private function…

Can you extract another smaller function out of it that is easier to handle and hopefully takes string as input and gives just one object something like string as output? Like no side effects, just one thing…

It would make it much easier to test without having to load files.

1 Like

I’m struggling to understand what you’re asking. Do you mean like a function that returns a value?

This function already accepts a string input.
Output depends on what the string input was.

Could you elaborate on what you’re thinking of testing?

What generates this files?
Does the winforms application generate the xml for itself?
What edits them?
I’m thinking if this is strictly private settings to your application, it is probably better to use something like appsettings?
does the user need to be able to manually edit them?

is it an existing application or something completely new?
if it is new,
consider using dotnet 8 (:

I’ll try to answer these.

  1. XmlDocument or XmlNode. Not sure which but it generates the .XML file every time the code executes “Save”.
  2. Each time the file is Saved the .XML file is edited from the foreach loop under Save.
  3. I’ve seen the words appsettings but have no familiarity with it.
  4. From the WinForm the user needs to be able to edit every textbox and have those values loaded and saved depending on the user starting or quitting the application. Think of it like “saved settings”.
  5. The program is existing and functions sorta in BETA prototyping phase but it was written from scratch up to this point.
  6. The program is based off of .NET 4.8.1. I could have based it off .NET 6 but all the clients I need to run it on only support 4.8.1 and I can’t install newer frameworks. Didn’t know .Net 8 was out. Not that I understand how that would help.

here is the quick and dirty way to do this…

add your settings in the settings file and you won’t have to parse xml documents.
here is what I have

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            textBox1.Text = Properties.Settings.Default.HandoffMessage;
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            if (sender != null && !string.IsNullOrEmpty(textBox1.Text))
            {
                Properties.Settings.Default.HandoffMessage = textBox1.Text;
                Properties.Settings.Default.Save();
            }
        }

but ideally you should use the latest and greatest stuff unless you have a reason to support really old machines.

You can use dotnet 8 even when there is no framework installed on the machine.
You can bring your own framework and include it with your binaries.
Your code will work on macOS and Linux too – well not if you need winforms I guess. Maui won’t work in Linux :frowning:

https://avaloniaui.net/ will work on Linux but I guess this should be good enough for now.

No need to Install anything you can publish .net 8 self contained.

If youre feeling adventerous you could also try aot. Though not sure If winforms works with it at all. And even If it does its very likely your code would not work immidiately.

1 Like

In some of my applications it’s quite helpful and useful to have the save data external because of changes in the environment requiring on the fly alterations. I’ve dealt with this before.

It’s a heck of a lot faster and easier to tweak an external file & relaunch the application vs baking the settings in and having to come back to the program a different day, modify, and re-compile.

1 Like

Which does not mean you have to parse XML by hand. Especially since .net core / 5+ the built in configuration system is quite exhaustive yet really simple imo. You can have multiple layered configuration sources and dont have to parse any of them by hand.

In .net 4.8 you can either use ConfigurationManager or manually wire up .net core/5+ configuration system (it exists as nuget package). A lot of the new features have Backwards compatible nuget packages. Very useful for slowely migrating old projects.

1 Like

If you can point me to some resources that aren’t too complicated to read I can investigate it. I just went with .XML because it was the first promising thing I found. Though I could just read & write line by line a .TXT file. It’d probably get the job done too.

Probably a bit off base as what you are doing does not actually look like app configuration but rather what your program does. Seems more like you are using XML in place of a database. For simple things thats fine.

For the most part I guess so. Does not really mean it´s unsuitable for your use case. It´s mostly more popular because of javascript (at least initially). It´s a little smaller for data transfer and a lot less “expressive”.

What I mean with “expressive” is that this

<settings>
  <setting txb="1" />
  <setting txb="2" />
  <setting txb="3" />
</settings>

and this

<settings>
  <setting>
     <txb>1</txb>
  </setting>
  <setting>
     <txb>2</txb>
  </setting>
  <setting>
     <txb>3</txb>
  </setting>
</settings>

is the same thing just slightly different. If you are choosing a format for exchanging data the above is nonsence you´d rather not deal with.

Right now I’m only using .XML to act as a “save settings” sort of feature that includes textbox.Text info and checkbox state (checked/not checked). I may extend this into populating listboxes with selections but I found I can use .TXT files for that.

Your code works just fine for me.

public static void Main()
{
    xml();
}
public static void xml()
{
    string[] settings = new string[3];
    int count = 0;

    XmlDocument xmlDoc = new XmlDocument();

    xmlDoc.Load("F:\\textOne.xml");
    XmlNodeList functionNodes = xmlDoc.SelectNodes("//settings/setting");

    foreach (XmlNode functionNode in functionNodes)
    {
        settings[count] = functionNode.Attributes["txb"].Value;
        Console.WriteLine(functionNode.Attributes["txb"].Value);
        count++;
    }
}

Look at this error, this is a null error - which means when it looked, it could not find.

You could try adding an if null check before trying to attribute to your array.

if (functionNode.Attributes["txb"] != null)
{
    settings[count] = functionNode.Attributes["txb"].Value;
    Console.WriteLine(functionNode.Attributes["txb"].Value);
    count++;
}

I changed the underlying XML to this:

<settings>
  <setting />
  <setting txb="B" />
  <setting txb="C" />
</settings>

So that the first settings does not have a txb - this threw the same null error.

When I added the null check above, it skipped the first and reported on the next 2.

Somewhere, either in your file, or when you are reading it - you are removing an attribute that exists - hence the null value - when you tested it on the textbox - it is likely that the error was either corrected or the file was read correctly or something along those lines.

Hard to say without access to the documents.

Or you could just change it to a Linq function and not iterate it at all.

var query = functionNodes.Cast<XmlNode>()
    .Where(node => node.Name == "setting" && node.Attributes["txb"] != null)
    .Select(node => node.Attributes["txb"].Value).ToList();

for (int i = 0; i < query.Count; i++)
{
    Console.WriteLine(query[i]);
}

This creates a List that you could use as easily as an array in your second loop.

Eliminating iteration would be great if I can basically import it to an array all at once instead of one XML element at a time.

I appreciate the detail to my use case you showed here but where would I point to the .XML file here? Same as I did before?:

xmlDoc.Load("./xml/settings.xml");
        XmlNodeList functionNodes = xmlDoc.SelectNodes("//settings/setting");

var query = functionNodes.Cast<XmlNode>()
    .Where(node => node.Name == "setting" && node.Attributes["txb"] != null)
    .Select(node => node.Attributes["txb"].Value).ToList();

for (int i = 0; i < query.Count; i++)
{
    Console.WriteLine(query[i]);
}

What I would need though is a way to step through and populate WinForm textboxes:

var query = functionNodes.Cast<XmlNode>()
    .Where(node => node.Name == "setting" && node.Attributes["txb"] != null)
    .Select(node => node.Attributes["txb"].Value).ToList();

for (int i = 0; i < query.Count; i++)
{
    foreach (Control obj in Controls)
    {
        if (obj is TextBox)
        {
            obj.Text = query[i];
            count++;
        }
    }
}

I understand what I typed would not work and would be terribly inefficient stepping through every form object for every element in the .XML but hopefully you can see where I’m coming from.

Linq for the most part is not about efficiency though, don´t worry too much about that part and if you do best to benchmark it unless you are absolutely certain (and even then you might be wrong this changes constantly with every .net release).

You are not eliminating iteration here.

This code

var query = functionNodes.Cast<XmlNode>()
    .Where(node => node.Name == "setting" && node.Attributes["txb"] != null)
    .Select(node => node.Attributes["txb"].Value).ToList();

is about equivalent to this

var results = new List<string>();

foreach(var node in functionNodes) {
  if(node is not XmlNode xmlNode) continue;
  if(xmlNode.Name != "setting" || xmlNode.Attributes["txb"] == null) continue;

  results.Add(xmlNode.Attributes["txb"].Value);
}

If anything the second code will most likely be ever so slightly faster. There are sometimes cases where linq can be faster. Like Min/Max functions are using AVX vector instructions when available now (remember changes constantly, this wasn´t the case one or two .net versions ago). But that also does not mean linq is actually faster it just means their implementation of Min/Max is far more sophisticated than you would probably care to implement yourself. All be it not very significant calling functions with function arguments and captured scope variables does cost you performance. If you are not optimizing a hot path of a program that you know needs to be as fast as humanly possible. Don´t worry about it. Readability is far more important in most cases.

What linq is is often less to write and less code to look at, which is good. But if you go to crazy with it it becomes hard to debug as you can´t step through it easiely. You´ll develop your own opinion about this eventually when to and when not to use linq. I tend to use linq when it´s clean and simple, but don´t when I feel the loop is easier to comprehend. It (can) become complicated quickly. Writing code thats too clever makes it hard to decipher later and that is very easy to accomplish with linq too.


When using JSON this would probably look something like this

// Put somewhere
class Setting {
  [JsonPropertyName("txb")]
  public string Txb { get; set; }
}

// Read
var fileContents = File.ReadAllText("/path/file.json");
var settings = JsonSerializer.Deserialize<List<Settings>>(fileContents);

// Write
var newContent = JsonSerilizer.Serialize(settings);
File.WriteAllText("/path/file.json", newContent);

This would use System.Text.Json you will need to add it as nuget package.

If you want to have an eclosing setting(s) object you could also add that and serialize that class intead of a list of setting.

class Settings {
  [JsonPropertyName("settings"]
  public List<Setting> Settings { get; set; }
}

I would probably prefer this over XML if only because it´s easier to map to a strongly typed object(s) and I don´t have to deal with XmlNodes, but ultimately XML works also.

I suppose then the best solution I’m looking for is something that is easy to read without being horribly inefficient. I want a program that is small and responsive but no I don’t need bleeding edge speed. I just want something that is objectively good for the the application.

For example this is what I have running and working right now for loading in the elements and assigning their values to their corrisponding textboxes.

foreach (XmlNode functionNode in functionNodes)
        {
            settings[count] = functionNode.Attributes["object"].Value;
            count++;
        }

        count = 0;

foreach (Control obj in Controls)
    {
        if (obj is TextBox)
        {
            obj.Text = settings[count];
            count++;
        }
    }

Is it bleeding edge efficient? My limited experience says no.
Is the code easy to read/understand & debug? I feel yes.
Is it objectively the best solution for the application?..I don’t know but I feel like there could be better solutions. I just don’t know how.

I’m constantly learning new code, researching methods of doing things, I just don’t want to run myself down a path of sloppy craftsmanship “just because it works” if there’s an objectively better solution.

I can try substituting in your .JSON example if not to just build an understanding of that way to do it. It would take me some trial & error to figure it out though.

I still wonder if it’s possible to create an array of WinForm objects because the second foreach loop steps through every object on the form and doesn’t allow me control to step through them in a specific order so .XML values can wind up assigned to the wrong boxes…

Yeah, that´s good ofc.


Your textboxes should have a Name property you assign in the designer or can assign when programatically creating text boxes. When order is an issue you should also store the name with the value.

If you don´t need an arbitrarily long list of textboxes for your settings it´s probably cleaner to just store an object that has a property for every setting as opposed to an array/list of arbitrary lenght.


If you wanted to stick with an arbitrary long list of textboxes you could do something like this.

// Store this once on window creation
private Dictionary<string, TextBox> settingTbDict = Controls
                    .OfType<TextBox>()
                    .Cast<TextBox>()
                    .ToDictionary(x => x.Name, x => x);

Make a class like this

public class Setting {
     public string Name { get; set; }
     public string Value { get; set; }
}

Write the code that loads the xml file and spits out an list of setting. If you where using JSON look at the last awnswer otherwise youd do something along these lines

public static List<Setting> LoadSettingsFromXml(string path) {
  var document = new XmlDocument();
  document.Load(path);

  var results = new List<Setting>();

  foreach(var current in document.SelectNodes("//settings/setting").Cast<XmlNode>()) {
    var name = node.Attributes["name"].Value;
    var value = node.Attributes["value"].Value;

    results.Add(new Setting() { Name = name, Value = value });
  }

  return results;
}

If you wanted to change to another data store method like JSON all you should need to change is how the data is loaded / saved not all the rest. Best to not entangle storage with logic as much as you can.

And then your setting the values code would be more like the following.

public void LoadFormValues() {
  var settings = LoadSettingsFromXml("somepath");

  foreach(var setting in settings) {
     if(!this.settingTbDict.TryGetValue(setting.Name, out var tb) continue;
     tb.Text = setting.Value;
  }
}

So youd store an XML and is more similar to this

<settings>
  <setting name="tb1" value"A" />
  <setting name="tb2" value"B" />
</settings>

Note there might be some typos here and there. Im literally just typing this code straight in the forum here. So no syntax checking is going on.


If you wanted to you could also use yield return here and return an IEnumerable instead

public static IEnumerable<Setting> LoadSettingsFromXml(string path) {
  var document = new XmlDocument();
  document.Load(path);

  foreach(var current in document.SelectNodes("//settings/setting").Cast<XmlNode>()) {
    var name = node.Attributes["name"].Value;
    var value = node.Attributes["value"].Value;

    yield return new Setting() { Name = name, Value = value };
  }
}

Then you save one list creation it just pulls the data one by one in the foreach. Not gonna break the bank especially because loading the XmlDocument likely uses up far more memory than your list ever could.

Note when returnning IEnumerable you should only iterate over the result once. When more iterations are required either change your approach of iterating so you can make it one loop still or ToList the result and work with the list instead. Sometimes it technically does not matter as the IEnumerable may be backed by a list or array that does not mind. But other times you might end up doing an expensive database query that takes 1 second to complete every time. So you doing a ToList then saves you an entier second.


If you wanted to really try to improve performance characteristics of this you’d be looking to replace this

var document = new XmlDocument();
document.Load(path);

And the same thing for the json solution you don´t want this

var fileContents = File.ReadAllText("/path/file.json");  // <= this line
var settings = JsonSerializer.Deserialize<List<Settings>>(fileContents);
using var fs = File.OpenRead("/path/file.json"); // <= this would do much better on larger files
var settings = JsonSerializer.Deserialize<List<Settings>>(fs);

But it´s simple and it works just fine until it does not and that´s when you should solve it.

I´ve had to deal with basically this exact thing before in an android app I was making. I wanted to get all the music I have in my library and the API would return thouthands of lines of XML (I have 4136 music files so id, name, path, artist, release year, composer, artist, filetype, etc. for all of them). Parsing this all at once to something similar as XmlDocument was dog slow (> 10 seconds don´t remember exactly) and also adroid eventuelly straight up tells you “no won´t do it” as you have a limit of how large a string is allowed to get. I used XmlPullParser (which is an android thing) to solve that. It ended up being easiely 100x lines the code, but was also more than 10x faster because I was parsing only what I need and put it straight into the object I want while parsing.

It´s likely overkill to try and stream in your settings one by one efficiently. I would not have done this for that android app either if it wasn´t an issue.

1 Like