Using methods from another class in forms, C# - c#

Thank you very much for the response. I have edited my post to show the QuoteMgr class. I use this mostly to read and save quotes to file; it is read back into an array of quotes called mylist. I can't figure out how to call QuoteMgr methods from within all of the four forms I have created. The only way I have found is to instantiate QuoteMgr from within one of the forms, but that won't work for the other three forms. The method I want to use in different forms is getRandomQuote() - haven't written the other methods yet.
My plan was to read data from a file, display the quote on the main form, and offer choice to add more quotes, edit one, or display another quote. A different form would be displayed, appropriate to the choice made.
At heart the problem is I don't fully grasp OOP. I understand the idea of having an abstract class to inherit methods from. If I do this, will the different forms be able to access my array of quotes ("mylist")? For data integrity, I think I only want one instance of my data floating around. In which case, I could have an abstract class with all of the quote manipulation methods, and use QuoteMgr only to read/write to file.
From the standpoint of learning the right way to program, is this the right design?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
namespace Quote
{
class QuoteMgr
{
Quotes myquote = new Quotes();
Quotes objectToSerialize = new Quotes();
Serializer serializer = new Serializer();
string myFile = "H:\\Dad\\Quotes\\quotes.quo";
public QuoteMgr()
{
}
static Random r = new Random();
public void getFile()
{
//fills myquote.mylist with quote strings from file
if (File.Exists(myFile))
{
objectToSerialize = serializer.DeSerializeObject(myFile);
myquote.myList = objectToSerialize.QuoteList;
//myquote.ShowQuotes();
}
else
{
FileInfo makeFile = new FileInfo(#myFile);
makeFile.Create();
}
}//end of get file
public void saveFile()
{
objectToSerialize.QuoteList = myquote.myList;
serializer.SerializeObject(myFile, objectToSerialize);
}
public string getRandomQuote()
{
int x = myquote.myList.Count-1;
return myquote.myList[r.Next(x)];
}
public void GUIop()
{
getFile();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 myMainScreen = new Form1();
//make all other forms invisible
myMainScreen.Visible = true;
changeQuoteDisplayed(myMainScreen);
Application.Run(myMainScreen);
saveFile();
}
public void changeQuoteDisplayed(Form1 theForm)
{
string s;
s = getRandomQuote();
theForm.lblDisplayQuote.Text = s;
}
}
}

It sounds like you should move the changeQuoteDisplayed method into your Form class...it doesn't make any sense to call a method in another class with your Form as an argument, only to have the other class modify the form you passed it. For one, you should not have public UI components inside your Form..if you must modify these from outside the form, make their data accessible through Properties.
If this is a method that all of your forms need to use, then perhaps you should allow them to inherit it through an abstract class, providing an abstract property as well that your child classes will use to implement set, making it update whatever UI component the child class needs updated on that method call. It could look something like this:
public abstract class QuoteBase
{
protected void changeQuoteDisplayed()
{
string s;
s = getRandomQuote();
LabelText = s;
// theForm.lblDisplayQuote.Text = s;
}
public abstract String LabelText
{
get; set;
}
}
public class EditQuote : QuoteBase
{
public override String LabelText
{
get { return lblDisplayQuote.Text; }
set { lblDisplayQuote.Text = value; }
}
}
Now all you need to do is implement the LabelText property in all of your Quote classes to update whatever label you want without needing to send an instance of your form to some other class to get an update.
Of course, this is just a wild guess..it's hard to tell what you should actually do without more information.

Related

Keep getting the CS0122 Error and do not understand how to fix it

I have this code, and the 'lblPatientVital1-4', 'lblBedNumber' and 'lblPatientName' bits of code keep giving me the CS0122 error. Saying it is inaccessible due to its protection level. I have looked around the internet made the source files not read only but still have no luck.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HospitalMonitor
{
public class Controller
{
public CentralModule centralStationBedsideDetails;
public string module1Name, module2Name, module3Name, module4Name, bedName, patientName;
public CentralModule CentralStationBedsideDetails
{
get { return centralStationBedsideDetails; }
set { centralStationBedsideDetails = value; }
}
public void SetSelectedModules(string module1, string module2, string module3, string module4, string bednumber, string pName)
{
module1Name = module1;
module2Name = module2;
module3Name = module3;
module4Name = module4;
bedName = bednumber;
patientName = pName;
SetCentralStationBedsideDetails();
}
public void SetCentralStationBedsideDetails()
{
centralStationBedsideDetails.lblPatientVital1.Text = module1Name;
centralStationBedsideDetails.lblPatientVital2.Text = module2Name;
centralStationBedsideDetails.lblPatientVital3.Text = module3Name;
centralStationBedsideDetails.lblPatientVital4.Text = module4Name;
centralStationBedsideDetails.lblBedNumber.Text = bedName;
centralStationBedsideDetails.lblPatientName.Text = patientName;
}
}
}
If you look at the line
centralStationBedsideDetails.lblPatientVital1.Text = module1Name;
the error message is telling you that
lblPatientVital1
is a property or field of centralStationBedsideDetails that is declared as (mostly likely) private (and certainly not public). This is common in WinForms forms.
You can either modify that property to make it public (or internal, if this code is in the same assembly), or you can provide an additional wrapper property or method that sets/gets the value of that internal property/field and is visible to your code (public, or internal and in the same assembly).
You're not showing the code for it, but presumably the implementation for CentralModule has members (either properties or class-level variables) by those names. The error is telling you that you're trying to access them as though they are public, but they are not public.
They may be protected, internal, or private perhaps. But they are not public. And therefore your code can't directly access them on that object.
You can make them public, or you can make public members (properties or methods) which provide the functionality you're looking for. Perhaps something like this:
public Label PatientVital1
{
get { return lblPatientVital1; }
}
which you could use as:
centralStationBedsideDetails.PatientVital1.Text = module1Name;
Or, to de-couple consuming code from UI technologies, you can just expose a method:
public void SetPatientVital1Text(string text)
{
lblPatientVital1.Text = text;
}
which you could use as:
centralStationBedsideDetails.SetPatientVital1Text(module1Name);

Following the DRY principle in ASP.NET

I have just recently got involved in a classic ASP.NET project which contains lots of storing and reading values from the session and query strings. This could look something like the following:
Session["someKey"]=someValue;
And somewhere else in the code the value in the session is read. Clearly this violates the DRY principle since you'll have the literal string key spread out all over the code. One way to avoid this could be to store all keys as constants that could be referenced everywhere there is a need to read and write to the session. But I'm not sure that's the best way to do it. How would you recommend I best handle this so that I don't violate the DRY principle?
Create a separate public class where you can define your constants, e.g
public class SessionVars
{
public const string SOME_KEY = "someKey";
public const string SOME_OTHER_KEY = "someOtherKey";
}
and then anywhere in your code you can access session variables like this:
Session[SessionVars.SOME_KEY]=someValue;
This way you can get IntelliSence and other bells and whistles.
I think you're reading too much into DRY. I pertains more to things that could be wrapped up in a function. I.e. instead of repeating the same fives lines all over the place wrap those 5 lines in a function and call the function everywhere you need it.
What you have as an example is just setting a value in a dictionary (the session object in this case), and that is the simplest way to store and retrieve objects in it.
I can't remember for the life of me where I humbly re-purposed this code from, but it's pretty nice:
using System;
using System.Web;
namespace Project.Web.UI.Domain
{
public abstract class SessionBase<T> where T : class, new()
{
private static readonly Object _padlock = new Object();
private static string Key
{
get { return typeof(SessionBase<T>).FullName; }
}
public static T Current
{
get
{
var instance = HttpContext.Current.Session[Key] as T;
lock (SessionBase<T>._padlock)
{
if (instance == null)
{
HttpContext.Current.Session[Key]
= instance
= new T();
}
}
return instance;
}
}
public static void Clear()
{
var instance = HttpContext.Current.Session[Key] as T;
if (instance != null)
{
lock (SessionBase<T>._padlock)
{
HttpContext.Current.Session[Key] = null;
}
}
}
}
}
The idea behind it two fold. The type created should be the only type you need. It's basically a big strongly-typed wrapper. So you have some object you want to keep extending information in:
public class MyClass
{
public MyClass()
public string Blah1 { get; set; }
}
Then down the road you extend MyClass and you don't want to have to remember all the Key Values, store them in AppSettings or Const variables in Static Classes. You simply define what you want to store:
public class MyClassSession : SessionBase<MyClass>
{
}
And anywhere in your program you simply use the class.
// Any Asp.Net method (webforms or mvc)
public void SetValueMethod()
{
MyClassSesssion.Current.Blah1 = "asdf";
}
public string GetValueMethod()
{
return MyClassSession.Current.Blah1;
}
Optionally you could place the access to this session object in a base page and wrap it in a property:
class BasePage : Page
{
...
public string MySessionObject
{
get
{
if(Session["myKey"] == null)
return string.Empty;
return Session["myKey"].ToString();
}
set
{
Session["myKey"] = value;
}
}
...
}
Here you are repeating the myKey string but it is encapsulated into the property. If you want to go to the extreme of avoiding this, create a constant with the key and replace the string.

C# need to pass an object so another class can call it's methods to update it

I have a class that has 3 public List's in it. It's basically just a data holding class.
I have a file with xml like delimiters(begging and ending tags of some sort, and data value that goes in between)
I have a parse class, that detects and adds certain things to certain lists from the data holding class. Basically I'm trying to detect an opening tag, store it in the opening tag List in my data holding class.
The way I'm trying to go (based on the teachers poor excuse for an example) is Main() instantiates the data holding class into an object, likewise for the parse class. Then it calls ParseMain to parse through the file and separate tags, data values, and closing tags to their respective List inside the data holding class. Then after the Parse class is finished, back in main, I'm calling up methods inside the data class to display the data.
I'm basically being yelled at by the compiler because, even though the xml data holder class has been instantiated in main, it doesn't or can't add to the public List's I get specifically this error
"An object reference is required for the non-static field, method, or property"
How can I set the data reading classes List's from my parse class?
My teacher gave a horrible example (he had everything but the classes marked static, and basically just slapped java/c++ style code into a couple classes)
This is all extra credit for a basic programming class I'm in.(the normal version is all structural inside one class)
***Edit- adding code snippets
XMLDoc XMLData = new XMLDoc();
XMLParse parseXML1 = new XMLParse();
//Calls the parseXML1 method passing it
//the name of the currently opened file.
parseXML1.MainParse(fileIn);
then to my main parse
public void MainParse(FileStream fileIn)
{
int byteIn;
while ((byteIn = fileIn.ReadByte()) != -1)
{
if (byteIn == '<')
{
ParseElement(ref byteIn,fileIn);
}
ParseElement looks like
public void ParseElement(ref int byteIn,FileStream fileIn)
{
token += byteIn;
//First I need to get the entire tag stored in token
do
{
byteIn = fileIn.ReadByte();
token += byteIn;
} while (byteIn != '>');
token += '>';
//now I insert a / into my compare token
compareToken = token;
compareToken.Insert(1,"/");
//It's an ending tag
if (token == compareToken)
{
//figure this out later
}
//It's an opening tag,store it into element list
else
{
XMLDoc.elements.Add(token);
//also tried XMLData/elements/Add(token)
}
}
and finally my XMLDoc class looks like....
class XMLDoc
{
public List<string> elements;
public List<string> dataValues;
public List<string> endingElements;
public XMLDoc()
{
elements = new List<string>();
dataValues = new List<string>();
endingElements = new List<string>();
}
//This method simply displays the contents of the arrays
public void DisplayCollected()
{
Console.WriteLine("Begin unformatted dump");
for (int ii = 0; ii <= elements.Count;ii++)
{
Console.WriteLine("\n"+elements[ii]+dataValues[ii]+
"\n"+endingElements[ii]);
}
Console.WriteLine("End unformatted dump\n");
}
//This method will generate an xml style display
//To add later
}
I'm playing around,learning by doing and such. I've at this point had to abandon the teachers example, as mentioned above he just made every method in every class static, probably because he slapped his example together from the main lab work(which is structural and all inside the first class)
EDIT: Okay, it looks like you just need to pass the reference to the object around, e.g.
XmlDoc xmlData = new XmlDoc();
XmlParser parser = new XmlParser();
parser.MainParse(xmlData, fileIn)
...
public void MainParse(XmlDoc xmlData, FileStream fileIn)
{
...
ParseElement(xmlData, ref byteIn, fileIn);
...
}
public void ParseElement(XmlDoc xmlData, ref int byteIn,FileStream fileIn)
{
...
}
I've adjusted the names slightly to be more sensible IMO.
I would recommend that you don't use public fields in XmlDoc, by the way. Public fields violate encapsulation - use properties if you really need to just expose values, but ideally put more behaviour in the object itself.
The error message is pretty good here: "An object reference is required for the non-static field, method, or property"
You are trying to call an instance method in a static fashion, you have two options:
Make the method static.
Instantiate the class and call the method from the instance.
For example:
public class Foo()
{
public void Frob() {}
}
You cannot do this:
Foo.Frob();
But you can do this:
var foo = new Foo();
foo.Frob();
or this:
public class Foo()
{
public static void Frob() {} // Note static
}
[...]
Foo.Frob();

How to use the same info across multiple forms

I am working on my first C# program and have run into a brick wall. I want to be able to set and get variables throughout diferent forms in the same application.
I created a class called "data" which contains the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Application1
{
public class data
{
public string SearchAirport
{
get
{
return searchairport;
}
set
{
searchairport = value;
}
}
}
}
What do I need to put into my forms to be able to use this class??
Right now all I have is:
data.SearchAirport = commandAirport;
string working = data.SearchAirport;
I know I have to add something else to keep from getting the:
"Error 11 An object reference is required for the non-static field, method, or property 'Sector_Datastore_2._0.data.SearchAirport.get'..."
error
Well, you need to declare searchairport:
public class data
{
private string searchairport;
public string SearchAirport
{
get
{
return searchairport;
}
set
{
searchairport = value;
}
}
}
alternatively, you could let C# do that automatically by using the following code:
public class data
{
public string SearchAirport
{
get;
set;
}
}
You are accessing searchAirport statically, and the method itself is not static.
You can either add the static keyword to the SearchAirport method signature or create a data object and then call SearchAirport on that object.
I'd suggest a Service Locator pattern, but I'm afraid it's way too complicated for what the Question-poster wants to achieve.
Just in case it may be useful later on: Service Locator pattern
data d = new data();
....before those lines

Property is null, even after being set in code

I've been trying to solve this for ages (3 days) now and I just cannot figure it out. I will try to explain the problem comprehensively because it is a bit more complex.
My school assignment is to create a simple text game using OOP in C# Visual Studio 2008 (should be built on a library the teacher provided for us). It should only use console. I have a decent experience with OOP from PHP and C++ but I still cannot figure this out.
80% of the text game is already working so I won't bore you with classes and stuff that already works and is not related to the problem. Ok let's get started:
Each command in the game (what you can type into the console and hit enter) is represented by a single class both extending an abstract class and an interface from the library I am supposed to built the game on. Bellow is a class Use which represents a command for using items (e.g. you type "use sword" into the console and the game will look for an item called sword and call its use method):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Game.Commands
{
class Use : TextGame.Commands.ACommand, TextGame.Commands.ICommand
{
private string name;
public new string Name
{
set { this.name = value; }
get { return this.name; }
}
private string description;
public new string Description
{
set { this.description = value; }
get { return this.description; }
}
private string parameters;
public new string Params
{
set { this.parameters = value; }
get { return this.parameters; }
}
public Use(string name, string description) : base(name, description)
{
this.name = name;
this.description = description;
}
private TextGame.Core.GameState gameState;
public TextGame.Core.GameState Execute(TextGame.Core.IGame game)
{
// This is just a test because it appears the problem is
// with the parameters property. There should be a command
// you have typed in the console but its always null
// Note that I have not yet coded the body of this method.
// I will do that once I solve the problem.
if (this.parameters == null)
{
Console.WriteLine("is null");
}
else
{
Console.WriteLine(this.parameters);
}
return this.gameState;
}
}
}
There are two other classes that are used. The Parser class and the Game class. There are a bit longer so I will only post snippets of relevant stuff from them. Parser class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections; // ArrayList, Dictionary, Hashtable
using System.Text.RegularExpressions; // regex engine
using Game.Commands;
namespace Game
{
class Parser
{
private ArrayList commands = new ArrayList();
// All commands that are available in the game so far are
// initialized here in the constructor (and added to the arraylist)...
// skip to the other method this is not important
public Parser()
{
this.commands.Add(new North("^north", "Go north"));
this.commands.Add(new South("^south", "Go south"));
this.commands.Add(new East("^east", "Go east"));
this.commands.Add(new West("^west", "Go west"));
this.commands.Add(new Use("^use\\s\\w+", "Try to use the selected item"));
this.commands.Add(new Quit("^quit", "Quit the game"));
}
// This method takes as an argument a string representing
// a command you type in the console. It then searches the arraylist
// via the regex. If the command exists, it returns an the command object
// from the arraylist
// This works fine and returns right objects (tested)
public TextGame.Commands.ACommand GetCommand(string command)
{
TextGame.Commands.ACommand ret = null;
foreach (TextGame.Commands.ACommand c in this.commands)
{
Regex exp = new Regex(#c.Name, RegexOptions.IgnoreCase);
MatchCollection MatchList = exp.Matches(command);
if (MatchList.Count > 0)
{
ret = c;
}
}
return ret;
}
}
}
Now a snippet from the Game class where I'm using both above classes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TextGame.Core;
using System.Collections;
using Game.Items;
using Game.Commands;
namespace Game
{
class Game : TextGame.Core.IGame
{
public void Play()
{
// Here I read commands from the console in a loop and
// call the ProcessCommand() method. No problem here.
while (true)
{
string command = Console.ReadLine();
this.ProcessCommand(command);
}
}
// This is the IMPORTANT method so take a closer look
private TextGame.Core.GameState gameState;
public TextGame.Core.GameState ProcessCommand(string command)
{
Parser parser = new Parser();
TextGame.Commands.ACommand c = parser.GetCommand(command);
if (c != null)
{
// HERE I ADD THE COMMAND FROM THE CONSOLE TO THE C OBJECT
// I ADD IT VIA THE SETTER TO THE PARAMETERS PROPERTY
// OF THE COMMAND
c.Params = command;
// AND I CALL THE COMMAND'S EXECUTE() METHOD - SEE THE FIRST CLASS -
// USE - WHERE I TEST FOR THE PARAMS PROPERTY BUT IT IS STILL NULL
this.gameState = ((TextGame.Commands.ICommand)c).Execute(this);
}
}
}
}
I have added comments to the snippets to describe where is the problem. I hope I have explained it well.
Anyone has any ideas? I've been working on this projects for about 3 weeks now and most of the stuff went smoothly when 3 days ago I came across this problem and since then I've been trying to get my head around this problem.
Your problem is with the 'new' keyword. Here's where you're using it in the 'Use' class:
private string parameters;
public new string Params
{
set { this.parameters = value; }
get { return this.parameters; }
}
You're creating a different property that just happens to have the same name as a property on the type you are inheriting from. The 'new' keyword tells the compiler you meant to do that.
Basically, this means that if you do the following:
var x = new Use();
x.Params = "abcd";
((ACommand)x).Params = "wxyz";
Console.Writeline("direct: " + x.Params);
Console.Writeline("ACommand: " + ((ACommand)x).Params);
You'll get this output:
direct: abcd
ACommand: wxyz
You probably want to remove the definition of 'Params' entirely from Use and just inherit the one from ACommand. Probably from Name and Description as well, but you should be able to figure out from here if you want that or not.
Without seeing the code for the ACommand class... Try removing the "new" operator in the Params declaration of the Use class. When your setting the property c.Params = command; is actually setting the property of the base class, in the Execute method your checking this.parameters instead of base.Params.
// This is just a test because it appears the problem is
// with the parameters property. There should be a command
// you have typed in the console but its always null
// Note that I have not yet coded the body of this method.
// I will do that once I solve the problem.
This is caused by you declaring new on your properties. These should be override, or not included at all if you don't need to change the logic of ACommand.
When you reference as an ACommand:
TextGame.Commands.ACommand c = parser.GetCommand(command);
c.Params = command;
You will use either ACommand's Params, or your overrides (if you had defined one).
Your new Params shadow ACommand's Params, and are only accessible if your reference is a UseCommand.
Your problem is here:
private string parameters;
public new string Params
{
set { this.parameters = value; }
get { return this.parameters; }
}
In your code:
c.Params = command;
you are referencing the type TextGame.Commands.ACommand. Because you're hiding the Param property in your subclass, you're causing a non-polymorphic reference. Remove the definition above and rely on the base class definition of Param, and you'll be fine.
It's been a while since I ran into this problem, but if you open that up in Reflector I expect you will see that you are hiding the Use.Params property behind a callvirt explicitly bound to its base type there.... as the faster typists pointed out.

Categories

Resources