Changing textbox from another class - c#

I am trying to change textbox data on my program, but it isn't working.
The form is already shown but when I try it doesn't update.
I'm trying to use form1.show(); but my program is using multithreading and it spams windows in every thread.
What I tried so far is this.
Form1.CS code
private delegate void NameCallBack(string varText);
public void UpdateTextBox(string input)
{
if (InvokeRequired)
{
paidbox.BeginInvoke(new NameCallBack(UpdateTextBox), new object[] { input });
}
else
{
paidbox.Text = paidbox.Text + input;
// textBox.Text = textBox.Text + Environment.NewLine + input // This might work as append in next line but haven't tested so not sure
}
}
On my Program.CS I used on every bool I want it to be updated.
public static Form1 MainLogWindow = new Form1();
Cheker.MainLogWindow.UpdateTextBox("test test");
This isn't working, it compiles successfully but it doesn't update when the process start the text on textbox.
Sorry for my bad english.
P.S I'm new on C#, I know only php and javascript so this is so new to me.

Related

Why does my mainform freeze when I use clear texbox and append text methods "simultanly" ? (multithreading)

I have a textbox on my mainform, and a thread running in an other class which is sometimes updating this same textbox by adding some text using Texbox.AppendText("some text"). On my mainform, I have a Clear button, which is clearing the texbox when the user press this button, using Texbox.Clear().
But I notice that sometimes, when the user press this clearing button at the same time that the other thread is updating this same textbox, my IHM is freezing for some seconds, and then I can use it again.
I used thread safe invoke methods, like in the MSDN doc says, but I can't figure why it's not working properly. Here's my code :
// In my main form MainWindow
public static MainWindow GetInstance()
{
if (_IHM_Main == null)
{
_IHM_Main = new MainWindow();
}
return _IHM_Main;
}
public delegate void SafeCallDelegate(System.Windows.Forms.TextBox textBox, String texte);
public static void AppendText(System.Windows.Forms.TextBox textBox, String texte)
{
if (textBox.InvokeRequired)
{
var d = new SafeCallDelegate(AppendText);
textBox.Invoke(d, new object[] { texte });
textBox.AppendText(texte);
}
else
textBox.AppendText(texte);
}
private void btn_Clear_Click(object sender, EventArgs e)
{
if (textBox3.InvokeRequired)
{
MainWindow.GetInstance().BeginInvoke(new MethodInvoker(() => MainWindow.GetInstance().textBox3.Clear()));
}
else
textBox3.Clear();
}
// In my Class1.cs where the thread is running
private delegate void ChangeTextBox(System.Windows.Forms.TextBox textbox, String texte);
private void UpdateTextboxRandomly()
{
MainWindow.GetInstance().Invoke(new ChangeTextBox(MainWindow.AppendText), MainWindow.GetInstance().textBox3, "adding some text");
}
Finally I solved my issue by using (with the same code) a RichTextBox instead of a TextBox.
It seems that TextBox is not fitted for that serial reception task

Unable To Write to a text box from a method

Im currently trying to create a messaging application from client to server. When the server sends the client a message it will open up an new form and add the text to a text box inside the form.
However the client is defiantly reviving the message, I tested this with a message box displaying the message before trying to update the text box. The text box is displaying the message perfectly.
However, when i try to edit the textboxt nothing happens. However my other method on a button click works perfectly. really unsure why this is happening.
I've also written an invoke method to check if the textbox need's to be invoked as i am using differen't threads for some methods.
Below is an example of my code with some screenshots the method below is being opened from another form "MAIN" i have reference to it in the code. Not sure if that is causing an issue perhaps?
public ChatWindow()
{
InitializeComponent();
Thread timerThread = new Thread(Main.ReceiveLoop);
timerThread.Start();
}
private void txtChatLog_TextChanged(object sender, EventArgs e)
{
}
private void btnSendMessage_Click(object sender, EventArgs e)
{
string clientReply = txtReply.Text;
string Message = "ClientMsg§" + clientReply;
var time = DateTime.Now;
txtChatWindow.AppendText($"{time} client: {clientReply}");
txtChatWindow.AppendText(Environment.NewLine);
Main main = new Main();
main.ChatResponse(Message);
txtReply.Text = "";
}
delegate void setTextCallBack(string message);
public void UpdateChatLog(string message)
{
if (txtChatWindow.InvokeRequired)
{
setTextCallBack d = new setTextCallBack(UpdateChatLog);
this.Invoke(d, new object[] { message });
}
else
{
var time = DateTime.Now;
string newMessage = message.Split('$')[1];
string messageToDisplay = $"{time} Server: {newMessage}";
MessageBox.Show(messageToDisplay);
this.txtChatWindow.AppendText(messageToDisplay);
this.txtChatWindow.AppendText(Environment.NewLine);
}
}
Below are some images of my server and the code running on the client:
as you can see the server is recieving what the client replies with, the textbox is also being appended for when the client presses the SEND button. However it isn't being changed when the method is called. You know the method is being called because the MessageBox.Show is being called and displaying what should be in the text box.
Really unsure what the issue is here. If anyone could help me It would be very appreciated! This is a project for my university degree that is due in soon!
Thankyou in advance!

C# Take combobox item from one form and add its name as text to another

Ok so I'm attempting to create a simple game. In a nutshell it's a resource management game where the player will attempt to manage a thieves guild. In regards to running missions I've created a Thief class, a new instance of which is created when a new thief is recruited. I have coded within the thief class the ability to gain experience and level up.
Here's my specific problem:
I want the player to be able to select which thief/thieves to send on a mission. I have thought about it and figured that opening a new form and populating it with checkboxes is the easiest way to allow this. These checkboxes will be related to a List<thief> of thieves, the player then checks the thieves s/he wants to send and these are then stored in another List<thief> and passed on to the run mission function.
I've built a separate project with the intention of testing and playing around with this before putting it into the main program. The test project consists of two forms: The first (frmMain) with a textbox to hold the selected options and a button to open the second form (frmSelect). Currently I can open and populate the second form (frmSelect) but when I try to add the checked options to the textbox I simply...well can't.
So far I have tried directly accessing the textbox by typing frmMain.txtOptionsDisplay in the cs file of frmSelect but it causes the following error:
An object reference is required for the non-static field, method or
property
I tried to create a new form in frmSelect and make it equal to the active instance of frmMain with: Form frmTemp = frmMain.ActiveForm; and then alter the textbox using frmTemp as a go-between but that produced the error:
'System.Windows.Forms.Form' does not contain a definition for
'txtOptionsDisplay'.
Having searched both google and stackoverflow forums I've encountered answers that I either have never heard of (Threading) or answers that I kind've recognise but can't interpret the code pasted to make it relevant to my problem (delegates).
Any advice or pointers would be fantastic.
EDIT:
frmMain code:
public frmMain()
{
InitializeComponent();
selections.Add("Option 1");
selections.Add("Option 2");
}
private void btnClick_Click(object sender, EventArgs e)
{
frmSelectOptions.Show();
int length = selections.Count();
for (int i = 0; i < length; i++)
{
CheckBox box = new CheckBox();
box.Text = selections[i];
box.AutoSize = true;
box.Location = new Point(50, 50*(i+1));
frmSelectOptions.grpControls.Controls.Add(box);
}
}
public void updateText(string option)
{
txtOptionsDisplay.Text += option;
}
}
frmSelect code:
public List<CheckBox> selectedOptions = new List<CheckBox>();
Form frmTemp = frmMain.ActiveForm;
public frmSelect()
{
InitializeComponent();
}
private void btnSelect_Click(object sender, EventArgs e)
{
foreach (CheckBox box in grpControls.Controls)
{
if (box.Checked == true)
selectedOptions.Add(box);
}
this.Hide();
}
}
I hope this formats correctly... I'm kinda new and don't know how to indent. Oh look there's a preview...
Does this help?
Your problem is that controls defined within a form by default receive the private access identifier. Hence you could just add a property along the lines of
public ControlType ProxyProperty {
get {
return txtOptionsDisplay;
}
}
Besides from that you should think about wether what you're trying is actually a good solution. Manipulating forms from one to another will become a huge clusterfuck in terms of maintenance later on.
I'd suggest using the Singleton pattern for your frmMain. This will help safeguard you from accidentally launching another instance of frmMain and at the same time, will give you access to frmMain's objects. From there, you can either write accessors to Get your txtOptionsDisplay or you can make it public. Below is an example:
public class frmMain
{
private static frmMain Instance = null;
private static object LockObj = new object();
public static frmMain GetMain()
{
// Thread-safe singleton
lock(LockObj)
{
if(Instance == null)
Instance = new frmMain();
return Instance;
}
}
public string GetOptionsDisplayText()
{
return txtOptionsDisplay.Text;
}
}
public class frmSelect
{
private void frmSelect_Load(object sender, EventArgs e)
{
// Set whatever text you want to frmMain's txtOptionsDisplay text
txtDisplay.Text = frmMain.GetMain().GetOptionsDisplayText();
}
}
If you do go this route, don't forget to update Program.cs to use frmMain's singleton.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Application.Run(new frmMain()); - Old method
Application.Run(frmMain.GetMain());
}

Form freezes when opened for second time

I am developing a Windows Forms application that access a WCF service. I ran into a great problem that I can't predict the reason of it. Even the Visual Studio debugger not showing any exception in the Output view. The scenario is like this, I have a custom user control that has a linkLabel on it. Whenever the link label is clicked, a form is opened and a class object is passed to it. The class definition of this object resides on WCF service on a remote server. Now the problem is that when I click the linkLabel, the form opens perfectly loading each of its component according to the class object passed to it. But when I close this form and click that linkLabel again, the form opens but immediately freezes after loading some elements. I tried many code variations. Edited many part of code that I think can affect. But none of them showed the difference. Since, I don't know where actually is the code has error, I am posting the linkLabel click code and functions that are called after it is clicked.
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Enabled = false;
string temp = Title.Text;
Title.Text = "Opening...";
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(openTopic));
t.Start();
Title.Text = temp;
Enabled = true;
}
void createTopicWindow()
{
TopicViewer t = new TopicViewer(t);
Invoke(new Action(() => t.Show()));
}
private void openTopic()
{
Invoke(new Action(() => createTopicWindow()));
}
The above is the edited code, since I was getting Cross thread exception before.
Following is the code of constructor of the form that is called when clicked the linkLabel:
try
{
InitializeComponent();
this.t = topic;
if (IsHandleCreated == false)
CreateHandle();
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(loadTopic));
th.Start();
Common.openedTopics.Add(this);
AddComment addComment1 = new AddComment();
addComment1.Topic = t;
addComment1.Dock = DockStyle.Fill;
panel5.Controls.Add(addComment1);
backgroundWorker1.RunWorkerAsync();
}
catch (Exception)
{ }
void loadTopic()
{
Invoke(new Action(()=>tHead = new TopicHeader()));
Global.SetControlPropertyThreadSafe(tHead,"Topic", t);
Global.SetControlPropertyThreadSafe(tHead,"Dock", DockStyle.Fill);
Invoke(new Action(()=>panel1.Controls.Add(tHead)));
Global.SetControlPropertyThreadSafe(this,"Text", t.Title + " - Topic Viewer");
if (t.Description.Trim().Length > 0)
{
Global.SetControlPropertyThreadSafe(webBrowser1, "DocumentText", t.Description);
}
else
{
Invoke(new Action(() => tabControl1.TabPages[0].Dispose()));
}
Global.SetControlPropertyThreadSafe(tabPage2, "Text", "Comments (" + client.getComCount(t.TopicID) + ") ");
}
TopicHeader is another small user control.
Please anyone tell me the solution to this?
If you are using .Net 4.5, then using async/await would be easiest solution. That way, you don't need any Invokes
async private void Form1_Load(object sender, EventArgs e)
{
string s = await Task<string>.Factory.StartNew(LongRunningTask,
TaskCreationOptions.LongRunning);
this.Text = s;
}
string LongRunningTask()
{
Thread.Sleep(10000);
return "------";
}
I can't give a direct answer to you question, but this may give a hold on.
public void Form_Load()
{
// do some stuff on the gui-thread
// i need to do something that takes a long time:
ThreadPool.QueueUserWorkItem((state) =>
{
// i'll execute it on the ThreadPool
// Long running code....
// update results in mainform on gui thread.
Invoke(new Action( delegate
{
// because the invoke will execute this on the gui-thread, you'll able to update controls.
// update my gui controls.
DataGrid.Source = myReceiveDataThing;
}));
}
}
You might expand the code, to check if the form is still valid.

Threading, let one thread know the others progress

Ok, well I have been at it for a while now and I decided to just use threads. I am making a syntax highlighter but I keep getting terrible performance with the file sizes that it will usually be used for. So I made two forms, the first shows the file in plain text and has a button that says "openincolor" when you click that I start a new thread as such
private void button1_Click(object sender, EventArgs e)
{
ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
theThread.Start();
}
public class ColoringThread
{
string text;
public ColoringThread(string initText)
{
text = initText;
}
public void OpenColorWindow()
{
Form2 form2 = new Form2(text);
form2.ShowDialog();
}
};
I want this form to send back a message each time it has complete say x lines of coloring. Then I will take that and figure out the progress and display it to the user.
How might I go about sending a message, or event(...? can I do that) to my first form to let it know of the others progress?
One very simple way to do this is with BackgroundWorker. It already provides an event to report progress.
How about something like this? This adds an event to the ColoringThread class which is subscribed to by the calling class.
private void button1_Click(object sender, EventArgs e) {
ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
colorer.HighlightProgressChanged += UpdateProgress;
Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
theThread.Start();
}
private void UpdateProgress(int linesComplete) {
// update progress bar here
}
public class ColoringThread
{
string text;
public delegate void HighlightEventHandler(int linesComplete);
public event HighlightEventHandler HighlightProgressChanged;
public ColoringThread(string initText) {
text = initText;
}
public void OpenColorWindow() {
Form2 form2 = new Form2(text);
form2.ShowDialog();
int linesColored = 0;
foreach (String line in text.Split(Environment.NewLine)) {
// colorize line here
// raise event
if (HighlightProgressChanged != null)
HighlightProgressChanged(++linesColored);
}
}
};
You can pass an object as argument to the Thread.Start and share your data between the current thread and the initiating thread.
Here is a good example:
How to share data between different threads In C# using AOP?
Or you can use BackgroundWorker which has ReportProgress
What you need is System.Windows.Threading.Dispatcher's BeginInvoke method. You can't directly modify a WPF object from your background thread, however you can dispatch a delegate to do that.
In your derived Window class object you have the Property Dispatcher, so you use it as follows:
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(status) => { StatusTextBox.Text = status },
thestatus
);
I'm sorry that I can't test that currently and I don't have the project here, where I did that. But I'm sure it will work, good luck ;)
Update: Oops, you're using Form's... I've written about WPF, sorry.

Categories