It has been a long question, so here is the summary first,
I have a Client class for my messenger project.
My Client class has a Socket.
I use its BeginReceive method to get messages from server.
In async callback of BeginReceive, I get the message using EndReceive.
When I get the message, I fire the MessageReceived event with message itself.
I use this Client class in my message form.
In message form, I can get the received message by attaching a method to Client.MessageReceived.
I use OnMessageReceived method for this purpose.
When I get the message, I can show it on a TextBox (using Control.Invoke) or MessageBox.
I can also add a new tab to my TabControl in OnMessageReceived.
When I try to initialize a WebBrowser control, I get ThreadStateException.
The control I use to display messages derives from WebBrowser control, so I need it.
Threading.Thread.CurrentThread.ThreadState is "Background".
I don't think you'll need the details but you can find the detailed question I first intented to post below.
Many thanks.
I'm working on a messenger project (Server is a Windows Service and Client is a Windows Forms Application) using Net.Socket's async methods.
I fire Client's MessageReceived event in callback of Socket.BeginReceive;
Everything is how I want them to be until here.
I use MessageReceived event in my form (the one that two people writes to each other) I can do anything I want to the UI using Control.Invoke method (if required) with one annoying exception.
I have tabbed conversations in the form so when a message arrives, I check if there is an open conversation (tab) with the sender. If yes, I select that tab and display the message. If no, I create a new ConversationTab.
Now, I'm sorry if it's being a long question than it should be or if I can't explain myself sufficently. English is not my first language and this is my first question in Stack Overflow.
So, here is the ConversationTab:
public class ConversationTab : TabPage
{
public User Friend { get; private set; }
public MessageBrowser MessageBrowser { get; private set; }
public ConversationTab(User friend) : base(friend.DisplayName)
{
Friend = friend;
MessageBrowser = new MessageBrowser();
Controls.Add(MessageBrowser);
MessageBrowser.Dock = DockStyle.Fill;
}
}
MessageBrowser derives from WebBrowser and the reason I use this is because I could not apply custom styles (color, font, size) 'per message' using RichTextBox. RichTextBox.SelectedColor doesn't always work or I couldn't make it work as intended. MessageBrowser let's me use CSS instead. Wandering off of the subject? Sorry.
Here is the NewConversation method I call when MessageReceived event fires:
public void NewConversation(User friend)
{
ConversationTab tab = Conversations.FirstOrDefault(c => c.Friend.Id == friend.Id);
if (tab != null)
ActiveConversation = tab;
else
{
tab = new ConversationTab(friend);
// add tab to TabControl
}
// bla
}
"Conversations" gets the tab pages of the TabControl and "ActiveConversation" gets or sets the SelectedTab property of the TabControl.
My point in creating these properties are mostly thread-safety logic inside.
So the question: It's throwing ThreadStateException in "tab = new ConversationTab(friend)" part of the above code. It is the "MessageBrowser = new MessageBrowser()" part of the first code and the constructor of MessageBrowser. The reason of why I didn't provide MessageBrowser's constructor code is because the exception is thrown before any line of inner code gets executed (It is about WebBrowser's constructor, I get this exception when I try to initalize a WebBrowser, too.)
Actually I don't even get an exception, the application just closes there without notifying me about anything. I saw the exception when I try to call "MessageBrowser = new MessageBrowser()" on ConversationTab's constructor in Watch window.
I'm kind of new to using threads and asynchronous methods.
MSDN says:
ThreadStateException is thrown by methods that cannot perform the requested operation due to the current state of a thread.
In my case, the thread's state is "Background".
I have no clue about what am I doing wrong.
Thank you very much if you read the whole thing and thank you much more if you can help.
This seems to be related to using COM (web browser control uses COM) in .NET where thread apartment needs to be set to STA.
Try adding [STAThread] to your entry point.
Have a look at this.
Related
So using windows form builder, I have created a new form with textbox in it, calling this form as LogForm.cs, this form/class has a method called log(string text).
In my main form class (Form1.cs), I have created an instance of that form.
LogForm logForm = new LogForm();
logForm.log("Logger has started...");
and it show fine on the LogForm textbox. But when I call logForm.log("Some logging info...") On my code inside a thread, it somehow makes my application crash.
How do I deal with this? Please help me demostrate a small code.I am fairly new to C# and programming as a whole so I hope you consider.
Use/call this function in LogForm.log (btw methods in C# are usually capitalized).
private void SetText(string text)
{
Action set = () => yourTextBox.Text = text;
if (yourTextBox.InvokeRequired)
{
yourTextBox.Invoke(set);
}
else
{
set.Invoke();
}
}
If it cannot be set from the current thread yourTextBox.InvokeRequired will be true and the function will work it out. Otherwise it just sets it directly.
Inspiration from this answer at possible duplicate.
Since you are saying the problem persists I'll show a bit more code and try to expain it further.
First of all, I edited the SetText method. I added the private modifier since this function is not indended to be called anywhere outside of LogForm. I also added the curly brackets since that's my preferred style and it also makes sure that the if-statement behaves as expected.
public void Log(string message) {
SetText(message);
//do stuff
}
Both of these methods (Log and SetText) are placed inside the LogForm class. You can now call logForm.Log("Logger has started..."); from any thread as long as your form (containing the textbox) is already initialized. This usually happens in the constructor by calling InitializeComponent(); on the first line.
Without knowing more about your code this is probably as far as I can help you.
I've been studying Android lately and I tried to port one of its functions to C# compact framework.
What I did is create an Abstract class that I call Activity.
This class looks like this
internal abstract class Activity
{
protected Form myForm;
private static Activity myCurrentActivity = null;
private static Activity myNextActivity = null;
internal static void LoadNext(Activity nextActivity)
{
myNextActivity = nextActivity;
if (myNextActivity != null)
{
myNextActivity.Show();
if (myCurrentActivity != null)
{
myCurrentActivity.Close();
myCurrentActivity = null;
}
myCurrentActivity = myNextActivity;
myNextActivity = null;
}
}
internal void Show()
{
//PROBLEM IS HERE
Application.Run(myForm);
//myForm.Show();
//myForm.ShowDialog();
//
}
internal void Close()
{
myForm.Close();
}
internal void GenerateForm()
{
///Code that uses the Layout class to create a form, and then stores it in myForm
//then attaches click handlers on all the clickable controls in the form
//it is besides the point in this problem
}
protected abstract void Click(Control control);
//this receives all the click events from all the controls in the form
//it is besides the point in this problem
}
The problem I have is with running the part of the Show() command
Basically all my classes implement the above class, load an xml file and display it.
When I want to transition to a new class/form (for example going from ACMain to ACLogIn)
I use this code
Activity.LoadNext(new ACLogIn);
Which is supposed to load the next form, show it , and unload the current form
I have tried these solutions (in the Show() method) and here is the problem with each one
using myForm.ShowDialog()
This works, but blocks execution, which means that the old form does not close, and the more I move between the forms the more the process stack increases
using myForm.Show()
This works, closes the old form after the old one is shown, but immediately after that closes the program and terminates it
using Application.Run(myForm)
This works only on the first form loaded, when I move to the next form, it shows it then throws an exception saying "Value does not fall within the expected range"
Can someone help me fix this or find an alternative?
If you're really after creating your own framework for this navigation, you need to re-work you thinking. The Form instance passed into Application.Run must never close - when it does, Application.Run finishes execution and (typically) your static void Main entry point exits and the app terminates.
What I would propose is that you change your Activity to either being a UserControl:
public abstract class Activity : UserControl
{
....
}
or Composing one
public abstract class Activity
{
private UserControl m_control;
....
}
Then instead of closing and showing Forms, parent all of the Activities inside the main Form as a container.
As fair warning, this is going to get complex when you start wanting to show things in a Tab motif instead of a Stack, or having split views. Frameworks seem simple to create, but they're not so I'd at least consider using something already done unless you have compelling reasons to want to roll your own.
Application.Run is generally used with the overload that takes a Form parameter. This would be the "main" form that would be responsible for starting/showing other forms. This "main" form could be "hidden". But, I think that's a little awkward.
Alternatively, you don't need a main form, you can use Application.Run() to start a message pump to process Windows messages; but, then the thread is busy processing messages and cannot show dialogs (they must be shown in the thread that is running Application.Run). You can get around this by creating one or more form objects before calling Application.Run and these form objects could create a Timer object that would call Form.Show() or Form.ShowDialog() on the Timer.Tick event handler so that for form is shown after the call to Run. I think this is a little awkward as well.
Both of these solutions kind of circumvent the way you're expected to use Windows and WinForms; so, I think you need to think about re-designing this application to work with the way that Windows and .NET works.
I'm using a timer to regularly read-in a log file and post certain contents to a textbox in a Windows Form Application I'm developing in C#. I do this by sending a string to set_textbox_thread which posts the text (s) to the appropriate textbox (tbc) in the else below. The code below works in the practice application I built. However, the same code runs, but fails to update my textbox in the full application I'm building. It seems to be failing on the Invoke statement, which fails to call set_textbox_thread again. My theory is that, because my full application has a more complex set of controls, I am not calling Invoke via the correct control. I've tried calling it via "this" the parent panel, the parent form, and the button that triggers set_textbox_thread, and am dealing with the same outcome. Two questions:
Which control should I call Invoke under?
Is there a way to retrieve "the thread that owns the controls underlying windows handle? Can I do this through the Controls.Owner method?
I have tried making this Invoke call using try/catch, but am unable to retrieve an error message in the catch. Any ideas how to resolve the issue this way?
Thanks in advance!
private delegate void stringDelegate(string s);
private void set_textbox_thread(string s)
{
TextBox tbc = get_thread_tb();
if (tbc.InvokeRequired)
{
MessageBox.Show("Invoke Required");
stringDelegate sd = new stringDelegate(set_textbox_thread);
**this.Invoke(sd, new object[] {s });**
MessageBox.Show("Invoke Completed");
}
else
{
1) It doesn't matter what control you invoke under; there is only one UI thread and any control to marshal the call back to that UI thread.
2) what could you possibly do with this thread?
3) not much detail there. How do you know you have error messages? And what do you mean by "error messages"
Call invoke on the text box instead:
tbc.Invoke(sd, new object[] {s });
I'm new in Silverlight and i am doing some tests. With my current test I try to display in real time the current Clipboard content. But there is a weird behaviors with this code :
namespace SilverlightTest
{
public partial class MainPage : UserControl
{
private Timer _timer;
public MainPage()
{
InitializeComponent();
var dispatcher_timer = new DispatcherTimer {Interval = new TimeSpan(0, 0, 0, 5)};
dispatcher_timer.Tick += new EventHandler(timer_Callback);
dispatcher_timer.Start();
}
private void timer_Callback(object state, EventArgs eventArgs)
{
current_clip_board.Content = Clipboard.GetText();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
current_clip_board.Content = Clipboard.GetText();
}
}
}
The button Event and the timer Event are suppose to do exactly the same action.
But it doesn't ! The Button works fine and set the clipboard text into the label but the timer throw an exception :
Clipboard access is not allowed
The question is : why ? :)
Thanks.
PS : I would bet on a thread problem :p
Clipboard access, in a partial trust (in-browser) Silverlight application (the scenario you're likely referring to above), is restricted. The GetText property is accessible only in scenarios that the Silverlight runtime determines were initiated by the user. Your example is perfect -- by a button click for example. A dispatch timer however is not user initiated, so the property throws an exception (this is especially important within the context of a in-browser application, which could be a big security hole if you could create a Silverlight application that just ran silently in the browser, watching the user's clipboard updates without their knowledge).
See this clipboard documentation for more details.
Just trigger Clipboard.ContainsText() instead of Text. The method ContainsText is allowed!
Have you tried this:
private void timer_Callback(object state, EventArgs eventArgs)
{
Dispatcher.Invoke(new System.Threading.ThreadStart(delegate()
{
current_clip_board.Content = Clipboard.GetText();
}
}
edit
After a quick search, it appears that Clipboard is only available in response to a user action see here and here.
In partial trust (the default mode for
browser-hosted Silverlight-based
applications), Silverlight also
restricts clipboard access to its two
key APIs GetText and SetText. These
APIs can only be invoked from within a
context that is determined by the
Silverlight runtime to be in response
to a user-initiated action. For
example, clipboard access is valid
from within a handler for a Click or
KeyDown event. In contrast, clipboard
access is not valid from a handler for
Loaded or from a constructor, and
access attempts throw exceptions.
If your only option is to use a timer, then don't do it at all. The clipboad is a shared resource, and you're going to raise "cannot open clipboard" errors in other programs as they try to access the clipboard. i.e. user copies something from WinWord, WinWord tries to open the clipboard, but can't, because you've got it locked while you're examining it.
Hello this works for me but only in IE Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() => HtmlPage.Window.Eval("window.clipboardData.setData('Text','testtestest')"));
just use getData method
So I have this interface that is just one big GO button that syncs a bunch of data from one tool to another. The problem is it takes a really long freaking time and some users are left wondering whats the deal. So I am wondering if there is a way that I can put something in my loop so that every so many entries it sends something back to the page to update them on the progress.
Currently it is just an .aspx page with an aspx.cs behind it. The Go button fires off the whole process and it calls Response.Write a ton of times (as well as writing the same thing to a log file I made) but the Responses don't show until the entire thing is done.
Please advise.
You could design a class which will be stored in the session and which will represent the current state of the operation:
public class OperationState
{
public object Result { get; set; }
public int Progress { get; set; }
public string Error { get; set; }
}
An instance of this class could be created when you start the operation and store it in the user session. Then at each step of the operation you could retrieve it from session and update the progress property. Once the operation terminates you could set the Result property or the Error property in case an exception occurs. In the meantime you could design a PageMethod which will be accessible from client script. This method will simply return the State instance from the session. You will then invoke it periodically and asynchronously from javascript to check the progress and update the DOM to notify the user.
I am assuming you are calling another class to do the work. Lets call this the WorkerClass
You can have the WorkerClass have an event hooked up to it, that the .aspx page hooks up too and will write a message when the event is triggered.
// Overload EventArgs to send messageas back up
public delegate void UpdateMethod(object sender, EventArgs e);
public class WorkerClass
{
public event UpdateMethod UpdateMethod;
}
WorkerClass worker = new WorkerClass();
worker.UpdateMethod += new UpdateMethod(worker_UpdateMethod);
EDIT based on Comment it is on there page
If you don't want to refactor to another class doing the work (which I suggest). You can post the messages this way.
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
this.ProcessMassiveWorkLoad();
}
private void ProcessMassiveWorkLoad()
{
for(int i = 0; i < 100000; i++)
{
// Do some work
// Write the fact you have work
Response.Write(string.Format("Done {0} of 100000", i);
}
}
The simplest way to resolve your issue is to call Response.Flush() after each Response.Write.
This will flush the current response buffer back to the client, enabling them to see the current state of the page.
Even David's method would need this to get the responses out to the user in a timely manner.
The better solution would be along the lines of Darin's solution, which would involve some client side scripting of (say) an update panel, that you refresh with a JavaScript timer to get the latest state, but that may introduce other issues for you (needing JavaScript turned on, rewriting the long running method as something you can fire off asynchronously, etc).
If it's any consolation, I've done both in the past, and would use either again.