I have a problem that i dont understand at all , i need your help...
I am working in a chat , i will do it with sockets , but the problem isnot this ,
I created a control user to my chat box... and another to my bubbles i am trying to use this control users from my main form
private void button1_Click_1(object sender, EventArgs e)
{
chatbox a = new bunifchat.chatbox();
a.send2message("ABC");
}
chatbox is the name of my controlUser and i am trying to use his method send2message, i know that it works but not correctly.
public void send2message(String message)
{
bubble bbl = new bunifchat.bubble(message);
bbl.Location = bubble1.Location; bbl.Left += 100; //add intent
bbl.Size = bubble1.Size;
bbl.Anchor = bubble1.Anchor;
bbl.Top = bbl_old.Bottom + 20;
panel2.Controls.Add(bbl);
//bottom.Top = bbl.Bottom + 50;
bbl_old = bbl; //safe the last added object
}
this method receive the string and create the bubble entering to other controlUser, it works when i run it from the ControlUser Chatbox with the next code:
private void buttonx_Click(object sender, EventArgs e)
{
//panel1.VerticalScroll.Value = panel1.VerticalScroll.Maximum;
String respuesta = txtbox.Text;
sendmessage("hola mundo");
send2message(respuesta);
panel2.VerticalScroll.Value = panel2.VerticalScroll.Maximum;
}
but if i run it from my main form , nothing happens,the code of my bubble is this:
public bubble(String message)
{
InitializeComponent();
label1.Text = message;
Setheight();
}
if i am not explicit, please ask , i need help
enter image description here
when i press the button green , it create more bubbles(blue rectangules) , but i want to send the content from my main form and not from my chat box.
i will do it with sockets
Does that mean you use a socket to receive data on a different thread than the UI-thread?
If so, you need to check, if oyu need to invoke before accessing / creating a new object.
private bool AddMessage(string msg)
{
if (this.InvokeRequired)
return (bool)this.Invoke((Func<string,bool>)AddMessage, msg);
//DO stuff here
send2message(msg);
return false;
}
Related
I'm really confused so I'm hoping someone can help me out here. I'm working on a programming assignment for uni but there's one part that's really been bugging me and I can't move on until it is fixed. I have created two classes. The problems in each are shown here:
class Login : Form1
{
Form1 f = new Form1();
public void LoginCorrect()
{
Form1.attempts = 3;
MessageBox.Show("Correct Credentials Entered!");
f.loginScreenVar = false;
f.mainScreenVar = true;
f.ChangeScreen();
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void ChangeScreen()
{
//Login Screen
txtUsername.Visible = loginScreenVar;
txtPassword.Visible = loginScreenVar;
btnLogin.Visible = loginScreenVar;
lblLoginCaption.Visible = loginScreenVar;
lblUsername.Visible = loginScreenVar;
lblPassword.Visible = loginScreenVar;
//Main Screen
lblWelcomeUser.Visible = mainScreenVar;
btnViewDetails.Visible = mainScreenVar;
btnViewAccounts.Visible = mainScreenVar;
btnLogout.Visible = mainScreenVar;
MessageBox.Show(loginScreenVar.ToString());
}
}
I have some controls on screen in my design which consist of text boxes, labels, and buttons, and these are meant to show and hide at diffferent times. I have created some booleans which can be set to true and false which will also set the visibility of these controls to true and false.
My problem is when accessing ChangeScreen() from my Login class, for some reason the controls don't hide when they're meant to. I've literally got a message box in the ChangeScreen() method which outputs the result of 'loginScreenVar' and this is false. Please can someone tell me why my 'Login Screen' controls are NOT hiding even though 'loginScreenVar' = false.
Another thing to note is when calling this code from a button in the Form1 class, it does work. However, due to the brief of my assignment I need to use multiple classes.
I really hope this isn't a bug and someone can help me here because I literally can't move on until this is fixed, thanks!
The issue is that, as noted in the comments, you create a new instance of Form1.
A whole new object, with own states.
Why can't I see this new instance? - well, if you did f.show() THEN you'd see it.
As it stands, you're still looking at the old instance.
So, what you need is a publicly accessible instance of Form1 which your two classes access, without creating a new instance.
OR
you could also work with two different windows. For example:
Form1_loaded(object sender, EventArgs e)
{
LoginWindow lw = new LoginWindow();
var result = lw.ShowDialog();
if(result == DialogResult.Cancel)
{
Application.Quit();
}
}
Let's assume you have a button for login. When clicked, it checks whether password and user name are correct. If not, the incorrect count gets increased by one. If the incorrect count is >= 3, then you just close the LoginWindow. (Default DialogResult is DialogResult.Cancel). The code might look like this:
LoginBtn_Click(object sender, EventArgs e)
{
if(UserNameInput.Text == userName && PasswordInput.Text == password)
{
failedAttempts = 0;
this.DialogResult = DialogResult.OK;
this.Close();
}
else
{
failedAttempts++;
if(failedAttempts >= 3)
{
MessageBox.Show("Wrong password. Shutting down the application...");
this.Close();
}
else
{
MessageBox.Show("Wrong password. " + (3-failedAttempts) + " tries left.");
}
}
}
This way, if login isn't successful, app quits. Otherwise the main screen appears.
Note: This is a basic solution. In a more complex app, you'd want more sophisticated output (not hard-coded strings) and comparisions using VariableName.Equals();
Let's keep it simple (and in the style you've started) for now:
public partial class Form1 : Form //Change the default "form1" "Button1" etc names as soon as possible
{
private bool loginScreenVar = true; //when naming booleans, use "truth test" sounding names like isLoginScreenMode
private bool mainScreenVar = true;
public Form1() //this is a constructor, a method that is always called when a new instance of this object is created
{
InitializeComponent();
//use the constructor to set things up
loginScreenVar = true;
mainScreenVar = false;
ChangeScreen();//make sure loginscreen is showing
}
public void ChangeScreen()
{
//Login Screen
txtUsername.Visible = loginScreenVar;
txtPassword.Visible = loginScreenVar;
btnLogin.Visible = loginScreenVar;
lblLoginCaption.Visible = loginScreenVar;
lblUsername.Visible = loginScreenVar;
lblPassword.Visible = loginScreenVar;
//Main Screen
lblWelcomeUser.Visible = mainScreenVar;
btnViewDetails.Visible = mainScreenVar;
btnViewAccounts.Visible = mainScreenVar;
btnLogout.Visible = mainScreenVar;
MessageBox.Show(loginScreenVar.ToString());
}
//call this method when the login is correct
public void LoginCorrect()
{
loginScreenVar = false;
mainScreenVar = true;
ChangeScreen();
}
//double click your login button in the forms designer to add this click event handler
public void LoginButton_Clicked(object sender, ClickEventArgs e){
if(txtUsername.Text == "user" && txtPassword.Text == "pass"){
LoginCorrect();
} else {
MessageBox.Show("Login incorrect");
}
}
}
Forget the class Login:Form stuff unless you're really trying to explore object instantiation and making your own classes for things. Your Form1 will be on show when your app starts, do all the logic inside it
A better way to change screens in winforms is by creating two separate panels each one contains the desired controls to be shown and hide so that you can switch between them
Code example:
Form1_loaded(object sender, EventArgs e)
{
LogInPanel.Visible=true;
}
private void ConnectBtn_Click(object sender, EventArgs e)
{
// Do your checking here
// IF conditions met
MainPanel.Visible=true;
}
private void DisconnectBtn_Click(object sender, EventArgs e)
{
// Do your checking here
// IF conditions met
LogInPanel.Visible=true;
}
If you want to keep you methadologie make sure your program.cs runs Login class instead of Form1 class
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!
I asked in a previous question how to "Threading 2 forms to use simultaneously C#".
I realize now that I was not explicit enough and was asking the wrong question.
Here is my scenario:
I have some data, that I receive from a local server, that I need to write to a file.
This data is being sent at a constant time rate that I cant control.
What I would like to do is to have one winform for the initial setup of the tcp stream and then click on a button to start reading the tcp stream and write it to a file, and at the same time launch another winform with multiple check-boxes that I need to check the checked state and add that info simultaneously to the same file.
This processing is to be stopped when a different button is pressed, closing the stream, the file and the second winform. (this button location is not specifically mandatory to any of the winforms).
Because of this cancel button (and before I tried to implement the 2nd form) I used a background worker to be able to asynchronously cancel the do while loop used to read the stream and write the file.
private void bRecord_Click(object sender, EventArgs e)
{
System.IO.StreamWriter file = new System.IO.StreamWriter(AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss") + ".xml", true);
data_feed = client.GetStream();
data_write = new StreamWriter(data_feed);
data_write.Write("<SEND_DATA/>\r\n");
data_write.Flush();
exit_state = false;
string behavior = null;
//code to launch form2 with the checkboxes
//...
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
int var = data_feed.ReadByte();
if (var != -1)
{
data_in += (char)var;
if (data_in.IndexOf("\r\n") != -1)
{
//code to check the checkboxes state in form2
//if (form2.checkBox1.Checked) behavior = form2.checkBox1.Text;
//if (form2.checkBoxn.Checked) behavior = form2.checkBoxn.Text;
file.WriteLine(data_in + behavior);
data_in = "";
}
}
}
while (exit_state == false);
});
worker.RunWorkerAsync();
}
private void bStop_Click(object sender, EventArgs e)
{
exit_state = true;
worker.CancelAsync();
}
I hope I've been clearer now.
I not experienced in event programming and just started in C# so please try to provide some simple examples in the answers if possible.
At first would it be enough to use one Winform? Disable all checkboxes, click a button which enables the checkboxes and start reading the tcpstream? If you need two Forms for other reasons let me know, but i think this isn't needed from what i can see in your question.
Then i would suggest you to use the Task Library from .Net. This is the "modern" way to handle multithreading. BackgroundWorker is kind of old school. If you just able to run on .Net 2.0 you have to use BackgroundWorker, but don't seem to be the case (example follows).
Further if you want to cancel a BackgroundWorker operation this isn't only call CancelAsync();. You also need to handle the e.Cancelled flag.
backgroundWorker.WorkerSupportsCancellation = true;
private void CancelBW()
{
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork += ((sender, args)
{
//Handle the cancellation (in your case do this in your loop for sure)
if (e.Cancelled) //Flag is true if someone call backgroundWorker.CancelAsync();
return;
//Do your stuff.
});
There is no common way to directly cancel the backgroundWorker
operation. You always need to handle this.
Now let's change your code to the modern TAP-Pattern and make some stuff you want to have.
private void MyForm : Form
{
private CancellationTokenSource ct;
public MyForm()
{
InitializeComponent();
checkbox1.Enable = false;
//Disable all checkboxes here.
ct = new CancellationTokenSource();
}
//Event if someone click your start button
private void buttonStart_Click(object sender, EventArgs e)
{
//Enable all checkboxes here
//This will be called if we get some progress from tcp
var progress = new Progress<string>(value =>
{
//check the behaviour of the checkboxes and write to file
file.WriteLine(value + behavior);
});
Task.Factory.StartNew(() => ListenToTcp(ct, progress as IProgress<string)); //starts the tcp listening async
}
//Event if someone click your stop button
private void buttonStop_Click(object sender, EventArgs e)
{
ct.Cancel();
//Disable all checkboxes (better make a method for this :D)
}
private void ListenToTcp(CancellationToken ct, IProgess<string> progress)
{
do
{
if (ct.IsCancellationRequested)
return;
int temp = data_feed.ReadByte(); //replaced var => temp because var is keyword
if (temp != -1)
{
data_in += (char)temp;
if (data_in.IndexOf("\r\n") != -1)
{
if (progress != null)
progress.Report(data_in); //Report the tcp-data to form thread
data_in = string.empty;
}
}
while (exit_state == false);
}
}
This snippet should do the trick. I don't test it so some syntax error maybe occur :P, but the principle will work.
The most important part is that you are not allowed to access gui
components in another thread then gui thread. You tried to access the
checkboxes within your BackgroundWorker DoWork which is no possible
and throw an exception.
So I use a Progress-Object to reuse the data we get in the Tcp-Stream, back to the Main-Thread. There we can access the checkboxes, build our string and write it to the file. More about BackgroundWorker vs. Task and the Progress behaviour you can find here.
Let me know if you have any further questions.
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.
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.