Davide Bombarda
Davide Bombarda

Reputation: 3

C# error CS0103 when using methods in different classes

I'm a newbie in C# and not native English (sorry for that); I am using Visual Studio 2017 using WinForm application.

To test some features to be added in an already existing project, I have created a new Winform application. This simple software writes and reads strings to and from a file. So, in the form I have 2 textboxes and 3 buttons: Save to file, Read from file and Update. To reduce the confusion in the bigger project, I've decided to split the methods in different classes: each one does one job and the form script has the minimum quantity of code possible. The 3 .cs files (clases) are:

All the 3 classes have been created and added to the project ProvaSalvataggioFile2.

So, the form class is (take note only of the methods' name, I have written all the code for completeness, if someone wants to test the code)

using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace ProvaSalvataggioFile2
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            string inputString = tbInputText.Text;
            if (inputString.IsValidString())
            {
                inputString.SaveToFile();
            }
            else
            {
                MessageBox.Show("The input string is not valid: please insert a valid string",
                    "Empty or null input string",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);
                tbInputText.Focus();
            }
        }

        private void btnOpenFile_Click(object sender, EventArgs e)
        {
            List<string> textFileContent = new List<string>();
            textFileContent = OpenTextFile();
            tbFileText.Text = string.Join(Environment.NewLine, textFileContent);
        }

        private void btnUpdate_Click(object sender, EventArgs e)
        {
            if (File.Exists(fileName))
            {
                List<string> textReadFromFile = new List<string>();
                textReadFromFile = File.ReadAllLines(fileName).ToList();
                tbFileText.Text = string.Join(Environment.NewLine, textReadFromFile);
            }
        }
    }
}

The SaveFile class is

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace ProvaSalvataggioFile2
{
    public static class SaveFile
    {
        public static bool IsValidString(this string stringToValidate)
        {
            bool result = true;
            if (string.IsNullOrEmpty(stringToValidate))
            {
                result = false;
            }
            return result;
        }

        public static bool SaveToFile(this string stringToSave)
        {
            bool result = true;
            //bool savedfile;
            DialogResult messageBoxResult;

            SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            //saveFileDialog1.InitialDirectory = @"C:\";
            saveFileDialog1.Title = "Save text Files";
            saveFileDialog1.CheckFileExists = false;
            saveFileDialog1.OverwritePrompt = false;
            saveFileDialog1.CheckPathExists = true;
            saveFileDialog1.DefaultExt = "txt";
            saveFileDialog1.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            saveFileDialog1.FilterIndex = 1;
            saveFileDialog1.RestoreDirectory = true;
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                if (File.Exists(saveFileDialog1.FileName))
                {
                    messageBoxResult = MessageBox.Show("The file is already existing: do you want:\n\t\u22c5OVERWRITE the file [YES]\n\t\u22c5APPEND data in the file [NO]\n\t\u22c5Use another file [CANCEL]",
                        "Overwrite file",
                        MessageBoxButtons.YesNoCancel,
                        MessageBoxIcon.Asterisk,
                        MessageBoxDefaultButton.Button3);
                    if (messageBoxResult == DialogResult.Yes)
                    {
                        messageBoxResult = MessageBox.Show(("Are you sure to overwrite the file in\n" + saveFileDialog1.FileName),
                            "Sure to overwrite file?",
                            MessageBoxButtons.OKCancel);
                        if (messageBoxResult == DialogResult.OK)
                        {
                            try
                            {
                                File.WriteAllText(saveFileDialog1.FileName, stringToSave);
                                result = true;
                            }
                            catch
                            {
                                result = false;
                            }
                        }
                    }
                    else if (messageBoxResult == DialogResult.No)
                    {
                        //MessageBox.Show(("Message to save: \"" + stringToSave + "\"\nin \"" + saveFileDialog1.FileName));
                        try
                        {
                            File.AppendAllText(saveFileDialog1.FileName, (Environment.NewLine + stringToSave));
                            result = true;
                        }
                        catch
                        {
                            result = false;
                        }
                    }
                    else
                    {
                        messageBoxResult = MessageBox.Show("Please enter a new filename",
                            "Save in a new file",
                            MessageBoxButtons.OKCancel);
                        if (messageBoxResult == DialogResult.OK)
                        {
                            stringToSave.SaveToFile();
                        }
                    }
                }
                else
                {
                    File.WriteAllText(saveFileDialog1.FileName, stringToSave);
                    result = true;
                }
            }

            return result;
        }
    }
}

And the OpenFile class is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace ProvaSalvataggioFile2
{
    public class OpenFile
    {
        public string fileName { get; set; }

        public List<string> OpenTextFile()
        {
            List<string> textReadFromFile = new List<string>();
            //textReadFromFile = new List<string>();
            OpenFileDialog openFileDialog1 = new OpenFileDialog();
            openFileDialog1.CheckPathExists = true;
            openFileDialog1.CheckFileExists = true;
            openFileDialog1.RestoreDirectory = true;
            openFileDialog1.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            openFileDialog1.FilterIndex = 1;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                fileName = openFileDialog1.FileName.ToString();
                textReadFromFile = File.ReadAllLines(openFileDialog1.FileName).ToList();
            }
            return textReadFromFile;
        }
    }
}

Now, if I put all the methods in the form class, all works just fine, with no problems (the application is stupid, only made to test the logic behind). But if I split the code in the 3 classes, I have:

  • Error CS0103 The name 'OpenTextFile' does not exists in the current context ProvaSalvataggioFile2 Form1.cs 41
  • Error CS0103 The name 'fileName' does not exists in the current context ProvaSalvataggioFile2 Form1.cs 47
  • Error CS0103 The name 'fileName' does not exists in the current context ProvaSalvataggioFile2 Form1.cs 50

So there must be some problem related with the splitting in classes. I have tried googling the error, but it seems that this error comes out in very different occasions and for different reasons (nothing in common with mine). I argue that I have missed something in the process of adding the new class or in the code defining the class.

I repeat myself, if I copy and paste the methods in the form class, the application works perfectly, but the same methods put in a separate class (but in the same file of the form class) doesn't.

Upvotes: 0

Views: 943

Answers (2)

ColinM
ColinM

Reputation: 2681

The problem here is that you are trying to access members of the OpenFile class within the MainForm class. Initialize an instance of OpenFile and keep it in a variable within your MainClass to reuse

using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace ProvaSalvataggioFile2
{
    public partial class MainForm : Form
    {
        // Initialize OpenFile
        private readonly OpenFile openFile = new OpenFile();
        public MainForm()
        {
            InitializeComponent();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            string inputString = tbInputText.Text;
            if (inputString.IsValidString())
            {
                inputString.SaveToFile();
            }
            else
            {
                MessageBox.Show("The input string is not valid: please insert a valid string",
                    "Empty or null input string",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);
                tbInputText.Focus();
            }
        }

        private void btnOpenFile_Click(object sender, EventArgs e)
        {
            List<string> textFileContent = new List<string>();
            textFileContent = openFile.OpenTextFile(); // Open Text File via openFile variable
            tbFileText.Text = string.Join(Environment.NewLine, textFileContent);
        }

        private void btnUpdate_Click(object sender, EventArgs e)
        {
            if (File.Exists(openFile.fileName)) // Validate file exists via openFile.fileName property
            {
                List<string> textReadFromFile = new List<string>();
                textReadFromFile = File.ReadAllLines(fileName).ToList();
                tbFileText.Text = string.Join(Environment.NewLine, textReadFromFile);
            }
        }
    }
}

You can also reduce your code by changing your IsValidString method to an extension method

public static bool IsValidString(this string stringToValidate) => !string.IsNullOrEmpty(stringToValidate)

I would recommend somewhat redesigning the code because there are potential bugs waiting to be found by using one class, I.E OpenFile, for opening and checking file contents. What if the file isn't opened and fileName is an empty string for example.

See the following MSDN article on classes to understand them more in-depth, and how the interact with each other.

Refactored Code

I have refactored the code to make it a bit more manageable and readable, please see below.

Here's a new class called TextFile which you can store the filename and contents in.

namespace ProvaSalvataggioFile2
{
    public class TextFile
    {
        public TextFile(string fileName, string contents)
        {
            FileName = fileName;
            Contents = contents;
        }

        public string FileName { get; set; }
        public string Contents { get; set; }
    }
}

Your OpenFile class is actually named pretty well, it has quite a good separation of concerns - meaning that on first glance I can see that it's a class which should only be used to open files, though you could go a step further and abstract the UI code away which couples you to Win Forms. OpenTextFile now returns a TextFile object

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace ProvaSalvataggioFile2
{
    public class OpenFile
    {
        public TextFile OpenTextFile()
        {
            TextFile textFile;
            OpenFileDialog openFileDialog1 = new OpenFileDialog();
            openFileDialog1.CheckPathExists = true;
            openFileDialog1.CheckFileExists = true;
            openFileDialog1.RestoreDirectory = true;
            openFileDialog1.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            openFileDialog1.FilterIndex = 1;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string fileName = openFileDialog1.FileName.ToString();
                string textReadFromFile = File.ReadAllText(openFileDialog1.FileName);

                textFile = new TextFile(fileName, textReadFromFile);
            }
            return textFile;
        }
    }
}

And of course, MainForm has to be updated to take the new object into consideration, I've removed duplicated code, reused your OpenFile class and introduced a RefreshTextFile method for setting the label text - this time you don't have to worry about file names being valid.

using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace ProvaSalvataggioFile2
{
    public partial class MainForm : Form
    {
        private readonly OpenFile openFile = new OpenFile();
        public MainForm()
        {
            InitializeComponent();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            string inputString = tbInputText.Text;
            if (inputString.IsValidString())
            {
                inputString.SaveToFile();
            }
            else
            {
                MessageBox.Show("The input string is not valid: please insert a valid string",
                    "Empty or null input string",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);
                tbInputText.Focus();
            }
        }

        private void btnOpenFile_Click(object sender, EventArgs e)
        {
            RefreshTextFile();
        }

        private void btnUpdate_Click(object sender, EventArgs e)
        {
            RefreshTextFile();
        }

        private void RefreshTextFile()
        {
            TextFile textFile = openFile.OpenTextFile();
            tbFileText.Text = textFile?.Contents;
        }
    }
}

Please note I have refactored this using Notepad++ and haven't put it through a compiler.

Upvotes: 2

swforlife
swforlife

Reputation: 578

Your Form class doesn't know where or what OpenTextFile() is, same goes for fileName. You need to create an instance of the object you want to use. Try adding this code to MainForm:

    private OpenFile _openFile;

    public MainForm()
    {
        this._openFile = new OpenFile();
        InitializeComponent();
    }

This creates a new instance of the OpenFile class that MainForm can now use.

You can also make OpenFile static but that is not considered to be a best practice.

Please note that since your OpenFile.fileName is not initialized that you might want to add something like this as well.

public OpenFile(string initialFileName = "defaultFilename"){
    this.fileName = initialFileName;
}

you can then even specify the filename in MainForm by providing it as an argument. Or if you don't want to set an argument you can do a null check before reading/using the fileName.

for more info about objects and constructor see: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/objects

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/constructors

Upvotes: 1

Related Questions