I'm making a console terminal using ListView for my custom server.
It writes any string that was given to it from the same class Main but refuses if its from another Misc.
Heres a gif of the following code in action
The first part of the gif is from myFunction(). As you can see, the messagebox shows that str in stringToConsole() contains a string("report 1" and "report 2") but it wouldnt add it.
Second part of the gif is from Execute_Click event. As you can see, again the messagebox shows that str in stringToConsole() contains a string(whatever i type) and it would add it
Following code is in Class Misc.
Following code's strings are not able to be added.
public static string myFunction()
{
Main myClass = new Main();
myClass.stringToConsole("report 1", "ConsoleList");
Thread.Sleep(2000); // emulate work
myClass.stringToConsole("report 2", "ConsoleList");
return "string";
}
Following codes are inside the form class Main.
private void startupProcedure()
{
label1.Text = Misc.myFunction();
}
This add strings to the ListView(Console List)
public void stringToConsole(string str, string destination)
{
if (destination == "ConsoleList")
{
// to check if str has a value
MessageBox.Show(str); // string does have a value
ConsoleList.Items.Add(str); // refuse to use str from myFunction()
}
}
Following code's strings are able to be added.
private void Execute_Click(object sender, EventArgs e)
{
executeCommandLine(CommandLine.Text, "ConsoleList");
CommandLine.Clear();
}
public void executeCommandLine(string commandLine, string destination)
{
stringToConsole(commandLine, destination); // this shows in Listview
}
A very similar question was asked some hours ago. You have a very, very basic problem:
Main myClass = new Main();
You are instantiating a new Main form, however, you are never showing it or using it outside the function's scope, hence, you are not modifying the Main instance you want to modify.
An easy way to do this would be to pass the Main instance to the function:
public static string myFunction(Main formInstance)
{
formInstance.stringToConsole("report 1", "ConsoleList");
Thread.Sleep(2000); // emulate work
formInstance.stringToConsole("report 2", "ConsoleList");
return "string";
}
private void startupProcedure()
{
label1.Text = Misc.myFunction(this);
}
Related
I'm trying to pass a value(an element id) from a WinForm back to the Command.cs file but I'm getting an error:
System.NullReferenceException: Object reference not set to an instance of an object.
at BatchSheetMaker.Command.Execute(ExternalCommandData commandData, String& message, ElementSet elements)
I'm following the youtube tutorial here and it seems fairly easy and straight forward but passing back to the Command.cs is another layer of complexity.
I have the Command.cs code wrapped in a try/catch block which just tells me that there's nullReferenceException however it doesn't tell me which line it's occurring at. I've looked around but havn't found any tips on how to make the debug show the error line. If anyone has any other pointers, that'd be helpful.
Form1.cs
public partial class Form1 : System.Windows.Forms.Form
{
private UIApplication uiapp;
private UIDocument uidoc;
private Autodesk.Revit.ApplicationServices.Application app;
private Document doc;
private string myVal;
public string MyVal
{
get { return myVal; }
set { myVal = value; }
}
public Form1(ExternalCommandData commandData)
{
InitializeComponent();
uiapp = commandData.Application;
uidoc = uiapp.ActiveUIDocument;
app = uiapp.Application;
doc = uidoc.Document;
}
public delegate void delPassData(System.Windows.Forms.ComboBox text);
private void Form1_Load(object sender, EventArgs e)
{
//Create a filter to get all the title block types.
FilteredElementCollector colTitleBlocks = new FilteredElementCollector(doc);
colTitleBlocks.OfCategory(BuiltInCategory.OST_TitleBlocks);
colTitleBlocks.WhereElementIsElementType();
foreach(Element x in colTitleBlocks)
{
comboBox1TitleBlockList.Items.Add(x.Name);
}
}
private void button1Continue_Click(object sender, EventArgs e)
{
MyVal = comboBox1TitleBlockList.Text;
}
Command.cs
Form1 form1 = new Form1(commandData);
String elementString = form1.MyVal.ToString();
Element eFromString = doc.GetElement(elementString);
ElementId titleBlockId = eFromString.Id;
ViewSheet sheet = ViewSheet.Create(doc, titleBlockId);
Run your entire add-in inside the Visual Studio debugger and step through your code line by line. That will show you exactly where the exception is thrown and enable you to easily identify what is causing the problem.
Changed my code to this and it started working:
form1.cs
public string MyVal;
//{
//get { return myVal; }
//set { myVal = value; }
//}
this link was helpful along with tutorials on youtube on how to pass values from form to form.
I'm trying to add a "log" message from my class to a ListBox on my form. Within the form I would just be able to use lblog.add("message"), but as I'm trying to clean up my code, what is the best way to pass the "message" to the front end?
I found a suggestion that has the code below, but wondering if there is a simpler way?
Form:
// This is all required so that we can call the function from another class
public void publicLogMessage(string message)
{
if (InvokeRequired)
{
Invoke(new OutputDelegate(logMessage), message);
}
}
public delegate void OutputDelegate(string message);
public void logMessage(string message)
{
lblog.Items.Add(DateTime.Now + " " + message);
}
Class:
//This is required so that we can call the "PublicLogMessage" function on the main form
public frmMain formToOutput;
public speechRecognition(frmMain f)
{
formToOutput = f;
}
Usage:
formToOutput.logMessage
You now have a pretty tight coupling between your algorithm and your ouput method. Your algorithm knows all about your output method (for example that it's a form with a specific signature).
I would suggest decoupling it:
private readonly Action<string> log;
public speechRecognition(Action<string> log)
{
this.log = log;
}
public void DoWork()
{
this.log("work started");
// ...
this.log("work in progress");
// ...
this.log("work ended");
}
This class knows nothing about the logging method. It only knows it gets a string. The class controlling both the output method (form) and algorithm (class above) can then link them together:
var form = new YourFormWithLoggingWindow();
var algorithm = new speechRecognition(form.publicLogMessage);
Now the algorithm will log to the form. You could have called it using
var algorithm = new speechRecognition(Console.WriteLine);
and it would log to the console in a Console Application. The algorithm does not care and does not need your form to compile. It's independent. Your form does not know the algorithm either. It's independent, too.
You could even have unit testing that checks the logging:
var log = new List<string>();
var algorithm = new speechRecognition(log.Add);
algorithm.DoWork();
Assert.AreEqual(log.Count, 3);
Use if/else when using InvokeRequired, I don't think there are other optimizations at the moment.
public void publicLogMessage(string message)
{
if (InvokeRequired)
Invoke(new OutputDelegate(logMessage), message);
else
logMessage(message);
}
public delegate void OutputDelegate(string message);
private void logMessage(string message)
{
lblog.Items.Add(DateTime.Now + " " + message);
}
private void listboxlrm(byte[] text)
{
if (this.listBox2.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(listboxlrm);
this.Invoke(d, new object[] { text });
}
else
{
byte[] convert = new byte[text[4]];
Array.Copy(text, 6, convert, 0, text[4]);
string yourtext = System.Text.Encoding.UTF8.GetString(convert);
this.listBox2.Items.Insert(0, string.Format(yourtext));
}
}
I am using that method.... If you use insert always add the top.
I need to populate a listbox from a text file at startup. I also need to have the firt 2 lines eliminated from the listbox along with all blank lines, but that isn't to important right now. I am currently stuck at getting the listbox populated at all. Here is my code so far:
struct CDCLocationEntry
{
public string name;
}
public partial class StartupForm : Form
{
private List<CDCLocationEntry> CDCList = new List<CDCLocationEntry>();
public StartupForm()
{
InitializeComponent();
}
private void ReadFile()
{
try
{
StreamReader inputFile;
string line;
CDCLocationEntry entry = new CDCLocationEntry();
inputFile = File.OpenText("P3S1 Data File For Import.txt");
while (!inputFile.EndOfStream)
{
line = inputFile.ReadLine();
CDCList.Add(entry);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void DisplayText()
{
foreach (CDCLocationEntry entry in CDCList)
{
CDCLocationListBox.Items.Add(entry.name);
}
}
private void StartupForm_Load(object sender, EventArgs e)
{
ReadFile();
DisplayText();
}
visual studio is say my problem is here:
struct CDCLocationEntry
{
public string name;
}
the message I'm getting is:
Warning 1 Field 'Project_3___Section_1.CDCLocationEntry.name' is never
assigned to, and will always have its default value null
none of my notes or online help is giving me an answer for this.
Any help that you can provide would be greatly appreciated
You need to create a CDCLocationEntry instance inside the loop and assign, at this instance property name, the line coming from your file
inputFile = File.OpenText("P3S1 Data File For Import.txt");
while (!inputFile.EndOfStream)
{
CDCLocationEntry entry = new CDCLocationEntry();
entry.name = inputFile.ReadLine();
CDCList.Add(entry);
}
Your actual code creates just one instance of CDCLocationEntry outside the loop and, without assigning anything to the name property, adds this same instance in every loop.
I was wondering if anyone could explain why when I use the following code I get different results. For further explaination, I'm using a dll that was created in C#, it's an rcon framework. The richtextbox displays 3 lines then will not display anymore whereas my debug console continues to get data from my rcon connection.
I'm using:
Private Shared Sub HandleMessage(args As BattlEyeMessageEventArgs)
Debug.WriteLine(args.Message)
Form1.RichTextBox3.AppendText(args.Message & vbNewLine)
Form1.RichTextBox3.SelectionStart = Form1.RichTextBox3.TextLength
If args.Message = "Connected!" Then
Form1.Button3.Enabled = True
End If
End Sub
If it helps, here's the C# code for the EventHandler:
using System;
namespace BattleNET
{
public delegate void BattlEyeMessageEventHandler(BattlEyeMessageEventArgs args);
public class BattlEyeMessageEventArgs : EventArgs
{
public BattlEyeMessageEventArgs(string message)
{
Message = message;
}
public string Message { get; private set; }
}
}
private delegate void UpdateRichTextBox3Delegate(RichTextBox3 textBox, string text);
private void UpdateRichTextBox3(RichTextBox3 textBox, string text){
if(textBox.InvokeRequired){
textBox.Invoke(new UpdateRichTextBox3Delegate(UpdateRichTextBox3),new object[]{textBox, text});
return;
}
textBox.AppendText(String.format("{0}{1}", text,Environment.NewLine));
}
Check if RichTextBox3 doesn't require to be invoked first before updating it.
call UpdateRichTextBox3(Form1.RichTextBox3, "some text to append");
I hope that the title and this simple example says everything.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void UpdateLabel(string str)
{
label1.Text = str;
MessageBox.Show("Hello");
}
private void buttonIn_Click(object sender, EventArgs e)
{
UpdateLabel("inside");
}
private void buttonOut_Click(object sender, EventArgs e)
{
MyClass Outside = new MyClass();
Outside.MyMethod();
}
}
public class MyClass
{
public void MyMethod()
{
Form1 MyForm1 = new Form1();
MyForm1.UpdateLabel("outside");
}
}
When I'm trying to change lable1 from MyClass it does nothing. But I can get to the UpdateLable method from outside, it says Hello to me, it just doesn't change the label.
Use a delegate for setting your label
public class MyClass {
Action<String> labelSetter;
public MyClass(Action<String> labelSetter) {
this.labelSetter = labelSetter;
}
public void MyMethod() {
labelSetter("outside");
}
}
.
public void buttonOut_Click(object sender, EventArgs e) {
var outside = new MyClass(UpdateLabel);
outside.MyMethod();
}
a bit unsure because the example actually leaves some bits unclear... but here is a try:
public class MyClass
{
public void MyMethod(Form1 F)
{
F.UpdateLabel("outside");
}
}
this works as long as MyClass is NOT running on a different thread - otherwise the call to UpdataLabel must be synchronized with the UI thread...
EDIT:
private void buttonOut_Click(object sender, EventArgs e)
{
MyClass Outside = new MyClass();
Outside.MyMethod(this);
}
Either go with Yahia's way (it has been updated and will work correctly) or try the following (probably overkill for what you're trying to do... whatever that is).
UPDATE:
Based on your comment in the question, you are also doing the work in MyClass on a different thread. Code change is below.
public partial class Form1 : Form
{
// keep a reference to a MyClass object for your Form's lifetime
private MyClass _myClass;
public Form1()
{
InitializeComponent();
// Intstantiate your MyClass object so you can use it.
_myClass = new MyClass();
// Register to the MyClass event called UpdateLabel.
// Anytime MyClass raises the event, your form will respond
// by running the UpdateLabelFromMyClass method.
_myClass.UpdateLabel += UpdateLabelFromMyClass;
}
private void button1_Click(object sender, EventArgs e)
{
// Call MyMethod in your MyClass object. It will raise
// the UpdateLabel event.
// update, you are calling this on a background thread?
_myClass.MyMethod();
}
void UpdateLabelFromMyClass(string message)
{
// Update your label with whatever message is passed in
// from the MyClass.UpdateLabel event.
// UPDATE: If called from a background thread you'll need this:
this.BeginInvoke( (Action) (()=>
{
label1.Text = message;
}));
}
}
public class MyClass
{
// An event that can be raised, allowing other classes to
// subscribe to it and do what they like with the message.
public event Action<string> UpdateLabel;
public void MyMethod()
{
// Raise the UpdateLabel event, passing "Outside" as
// the message.
UpdateLabel("Outside");
}
}
After wasting a ton of time on what should be a simple task and trying every answer on stack overflow, I said, if C# wants to make it stupid hard to change the text of a simple label, I am going to come up with a stupid fix.
Here is what you do:
In Form1 or whatever form has the label you want add:
public void setStatus()
{
lblStatus.Text = status;
}
public static string status;
Now, add a timer to Form1 and have it run "setStatus();" on every tick
Now, in any class, just write:
Form1.status = "Change label to this text";
you need to make both the method MyMethod and the label in question static. But if you do then you cannot access MyMethod through a new instance of the form instead you have to access it directly like Form1.MyMethod(). But if you do make the label the static visual studio will make it non-static one you access the label from the designer so you will have to keep making it static from form1.designer.cs. Also if you do make the label static change every line that refers to any of its properties so if it says this.label1.Text change it to label1.Text. This should give you the desired effect