|
My intent is to construct a very simple splash screen that can be used both at project start as well as a status screen for long-running tasks. Here is the guts of the code (SplashScreen is private so that it can only be accessed via the Load class)
public delegate void SetTextDelegate(string message, string action);
partial class SplashScreen : Form
{
#region Private variables and Properties
string actionText = string.Empty;
string messageData = string.Empty;
int messageLength;
bool loaded;
public bool ScreenLoaded { get { return loaded; } }
#endregion
#region .ctor logic
public SplashScreen(string message, string action, string title)
{
InitializeComponent();
messageData = message;
actionText = action;
this.title.Text = title;
SetText();
}
#endregion
#region Form Thread methods
private void SetText()
{
if (InvokeRequired)
Invoke(new MethodInvoker(SetText));
this.messageText.Text = String.Concat(messageData, " : ", actionText);
this.messageLength = this.messageText.Text.Length;
if (loaded)
this.Invalidate();
}
private void ExpandText()
{
if (InvokeRequired)
Invoke(new MethodInvoker(ExpandText));
this.messageText.Text += ".";
this.Invalidate();
}
private void InvokeSetText(string message, string action)
{
messageData = message;
actionText = action;
SetText();
}
protected override void OnLoad(EventArgs e)
{
this.Activate();
base.OnLoad(e);
}
private void threadTimer_Tick(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Thread timer has ticked.");
if (this.messageText.Text.Length > messageLength + 4)
{
SetText();
}
else
{
ExpandText();
}
}
private void SplashScreen_Load(object sender, EventArgs e)
{
this.threadTimer.Enabled = true;
this.threadTimer.Interval = 100;
this.threadTimer.Start();
}
private void SplashScreen_FormClosing(object sender, FormClosingEventArgs e)
{
this.threadTimer.Stop();
}
private void SplashScreen_Shown(object sender, EventArgs e)
{
this.loaded = true;
}
#endregion
#region External Thread methods
public void SetMessageData(string message, string action)
{
if (InvokeRequired)
{
SetTextDelegate invoker = new SetTextDelegate(InvokeSetText);
Invoke(invoker, new object[] { message, action });
}
else
{ }
}
#endregion
}
public static class Loader
{
#region Static methods and data
static SplashScreen screen;
static Thread SplashThread;
static public void ShowSplashScreen(string message, string action, string title)
{
if (SplashThread != null)
return;
screen = new SplashScreen(message, action, title);
SplashThread = new Thread(new ThreadStart(ShowScreen));
SplashThread.IsBackground = true;
SplashThread.SetApartmentState(ApartmentState.STA);
SplashThread.Start();
while (screen.ScreenLoaded == false && SplashThread.ThreadState == ThreadState.Running)
{ }
}
[STAThread]
static void ShowScreen()
{
if (screen != null)
{
Application.Run(screen);
}
}
public static void UpdateScreen(string message, string action)
{
screen.SetMessageData(message, action);
}
public static void TerminateScreen()
{
if (screen == null) return;
if (SplashThread == null) return;
screen.Invoke(new MethodInvoker(screen.Close));
screen = null;
SplashThread = null;
}
I have a testbed application with one button that does a synchronous search and a second one that does a mutli-threaded search. Both use the splashscreen to display steps being performed. My intent for the splashscreen is that a timer ticks every 100 ms and adds a '.' to the end of the message until there are five '.' and then it starts all over again.
Problem: In the synchronous search the timer does not seem to ever get started even though it should be on a totally separate thread and started when the form loads.
Problem: In asynchronous search mode the timer does start, but I get a CrossThreadException the first time I go to add a '.' !!
Does anyone have a suggestion as to where I messed up in my code????
-- modified at 13:11 Thursday 11th May, 2006
|
|
|
|
|
You cannot access a control on a thread other than the one that created it. This has been true for decades now.
Search this site for more info; there's a ton of articles on the topic of how to update a control from a different thread.
Tech, life, family, faith: Give me a visit.
I'm currently blogging about: Islamic Domination: Coming to a Jewish state near you!
The apostle Paul, modernly speaking: Epistles of Paul
Judah Himango
|
|
|
|
|
Obviously you never looked at the code. The splashscreen is started on its' own thread, the owning thread access is controlled via IsInvokable, and the timer is always on the message pump thread unless it is started by the parent thread. I was very careful to ensure that the parent thread never issued a timer.Start()...which is also why the static logic was broken out as a separate class object.
|
|
|
|
|
One glaring thing I'm seeing is that when you do
if (InvokeRequired)
Invoke ( ...);
...
you aren't exiting the function, so you're still trying to continue on the wrong thread PLUS trying to invoke the function.
if (InvokeRequired)
{
Invoke ( ...);
return;
}
...
would correct that particular issue.
--
I've killed again, haven't I?
|
|
|
|
|
Thanks alot. I never saw that!
|
|
|
|
|
Hello everyone.
How do I dynamically (by code and not by adding reference to the project) load class created inside a class-library solution, to my code?
DllImport attribute can load function only as I can tell...
Thanks
|
|
|
|
|
It would depend alot on whether or not it is a COM or .NET dll that you wish to load. If it is a .NET dll you can use reflection to load the assembly that the class lives within. I believe it is in Assembly.Load() that you want to research.
|
|
|
|
|
|
Apologies for the really noob question, but here goes:
I want to make a boolean field in my homespun class available, so that I can link a Checkbox (or multiple checkboxes) to it. I'm not sure what the proper terminology for this link is, maybe data binding?
Here's the source of my class;
class ToggleClass<br />
{<br />
private bool value;<br />
<br />
public bool Value<br />
{<br />
get { return value; }<br />
}<br />
<br />
public ToggleClass ()<br />
{<br />
value=false;<br />
}<br />
<br />
public void Toggle ()<br />
{<br />
while(true)<br />
{<br />
value=!value;<br />
Thread.Sleep(1000);<br />
}<br />
}<br />
}
An application would create an instance of the ToggleClass, and then create a new thread and start the Toggle() method going.
ToggleClass obj=new ToggleClass();<br />
Thread thread=new Thread(new ThreadStart(obj.Toggle));<br />
thread.Start();
Now I assume that I can use
obj.Value
to read the current state of the boolean.
I'm using the C# Express development environment, so I've created a Form, put a Checkbox on it, and then linked my ToggleClass as a data source, so that I can set the DataBindings property of the Checkbox to the Value field of the ToggleClass class.
checkbox1.DataBindings.Add(new Binding("Checked",ToggleClass,"Value",true));
I suspect that I'm missing some way of notifying the Checkbox(es) that the value of the boolean has changed. So that's my question, what else do I need to implement such that the Checkbox will reflect the current value of the bool field? Should I be throwing an event, which the Checkbox(es) catches?
Alternatively, if I'm barking up the wrong tree to get this type of job done, please tell me which tree I should be barking up.
Thanks, Iain.
|
|
|
|
|
you're gonna love this one.
What you have setup so far is 1-way binding. That is to say that by binding your object's Value property to the checkbox's Checked property checking or unchecking the checkbox would affect your object. You should try this to confirm the behaviour.
In order to setup 2-way binding, which will update the checkbox when the underlying Value property chenges, you need a way to notify the UI that the data source's value has changed.
You do this by implementing a PropertyNameChanged event. So in your class you would have something like:
class ToggleClass<br />
{<br />
private bool value;<br />
<br />
public event EventHandler ValueChanged<br />
<br />
public bool Value<br />
{<br />
get { return value; }<br />
<br />
}<br />
<br />
public ToggleClass ()<br />
{<br />
value=false;<br />
}<br />
<br />
public void Toggle ()<br />
{<br />
while(true)<br />
{<br />
value=!value;<br />
if(ValueChanged != null)<br />
ValueChanged(this,EventArgs.Empty)<br />
Thread.Sleep(1000);<br />
}<br />
}<br />
}
Here is some extra reading for you:
http://msdn2.microsoft.com/en-us/library/xz45s2bh.aspx[^]
Current blacklist
svmilky - Extremely rude | FeRtoll - Rude personal emails | ironstrike1 - Rude & Obnoxious behaviour
-- modified at 12:05 Thursday 11th May, 2006
|
|
|
|
|
Thanks for the pointers, and the link to the MSDN article. My code now looks like:
public class ToggleClass : INotifyPropertyChanged<br />
{<br />
public event PropertyChangedEventHandler PropertyChanged;<br />
<br />
private bool value;<br />
public bool Value<br />
{<br />
get { return value; }<br />
}<br />
<br />
public ToggleClass ()<br />
{<br />
value = false;<br />
}<br />
<br />
private void NotifyPropertyChanged (string info)<br />
{<br />
if(PropertyChanged!=null)<br />
{<br />
PropertyChanged(this,new PropertyChangedEventArgs(info));<br />
}<br />
}<br />
<br />
public void Toggle ()<br />
{<br />
while(true)<br />
{<br />
value = !value;<br />
NotifyPropertyChanged("Value");<br />
Thread.Sleep(1000);<br />
}<br />
} <br />
}
I think I now understand about how the interface is implemented, and the event generated when the bool is changed. (Quite similar to my Java days; interfaces and events)
Now the bit which is perplexing me, is the connection to the Checkbox. I don't understand how this DataBindings concept works. When I use the C# Express development environment a toggleClassBindingSource is created for me, and a reference to the ToggleClass is created. The Checkbox appears to be bound to the binding source, but that only references to the class, not an instance of that class. Hence if I had two ToggleClass objects I can't point the Checkbox at either object, only at the class definition.
Thanks again, Iain.
|
|
|
|
|
Hi,
I have a form with tab pages on it, and I'd like to print each page whenever a button is pressed (all the tabpages share the same print button - I just give it a different parent when another tab is pressed). But I can only print once. When the print button is pressed the second time, I get error
An unhandled exception of type 'System.Runtime.InteropServices.ExternalException' occurred in system.drawing.dll
Additional information: A generic error occurred in GDI+.
I'm not sure what to do here. My code is below. I also tried giving the PrintDocument a different name each time, and it still isn't happy.
private void printClick(object sender, System.EventArgs e)<br />
{<br />
Graphics currentTab = this.CreateGraphics();<br />
Size s = this.Size;<br />
Bitmap memoryImage = new Bitmap(s.Width - 10, s.Height - 36, currentTab);<br />
Graphics memoryGraphics = Graphics.FromImage(memoryImage);<br />
IntPtr dc1 = currentTab.GetHdc();<br />
IntPtr dc2 = memoryGraphics.GetHdc();<br />
BitBlt(dc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, dc1, 2, 2, 13369376);<br />
CurrentPage = memoryImage;<br />
currentTab.ReleaseHdc(dc1);<br />
memoryGraphics.ReleaseHdc(dc2);<br />
CurrentPage.Save("sCurrentPage.bmp",System.Drawing.Imaging.ImageFormat.Bmp);<br />
<br />
PrintDocument pd = new PrintDocument();<br />
PageSetupDialog pg = new PageSetupDialog();<br />
printDialog1.Document = pd;<br />
pg.Document = pd;<br />
pg.PageSettings.Landscape = true;<br />
DialogResult result = printDialog1.ShowDialog();<br />
<br />
if (result==DialogResult.OK)<br />
{pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(PrintCurrentPage); pd.Print();}<br />
pd.Dispose();<br />
}
Thanks for any help!!!
Mel
|
|
|
|
|
Having a GDI exception occur tends to make me think that your problem is not with the printing, but rather in creating the bitmap. Where in your code does the stack trace point at? Does the bitmap get saved and have you looked at it to make sure it is the page you expected to print?
Two other things:
You can save yourself one object creation and a little bit of GC stress by changing the last four lines of code to this:
if ( printDialog1.ShowDialog() == DialogResult.OK )
{
pd.PrintPage += .....
}
}
|
|
|
|
|
I commented out just the pd.Print(); and it worked fine. No exceptions, correct bitmap saved each time.
I'm assuming the stack trace is the green arrow/highlight? If so, it points at the line PrintDocument pd = new PrintDocument(); .
Any ideas?
Mel
|
|
|
|
|
In case anyone needs it, the problem is in this line:
pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(PrintCurrentPage);
PrintCurrentPage looked like this:
private void PrintCurrentPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)<br />
{<br />
if (CurrentPage != null){CurrentPage = new Bitmap(@"C:\sCurrentPage.bmp");} <br />
}
I found that "When a bitmap image is loaded from a file the file remains locked open. This may be the root of the problem.
To overcome the file locking open the file on a stream, read the image from the stream and explicitly close the stream."
So I changed PrintCurrentPage to look like this:
if (CurrentPage != null)<br />
{<br />
FileStream fs = File.Open(@"C:\sCurrentPage.bmp", FileMode.Open, FileAccess.Read);<br />
Bitmap bm = (Bitmap)Bitmap.FromStream(fs);<br />
CurrentPage = bm;<br />
e.Graphics.DrawImage(CurrentPage, 0, 0);<br />
fs.Close();<br />
bm.Dispose();<br />
}
And it all worked fine.
Cheers,
Mel
|
|
|
|
|
Thank you!
Just needed it.
|
|
|
|
|
why it doesn't work? It show the draw i want, but when i write text the draw disappear:
public partial class UserControl1 : TextBox
{
public UserControl1()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
this.BorderStyle = BorderStyle.None;
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen p = new Pen(Color.Red ,3.0f);
g.DrawRectangle(p, new Rectangle(0, 0, this.Width, this.Height));
}
}
|
|
|
|
|
Sasuko wrote: but when i write text the draw disappear:
Do you mean that the text you type is never displayed? If so, I would expect this is happening because your OnPaint() method isn't writing it.
/ravi
My new year's resolution: 2048 x 1536
Home | Music | Articles | Freeware | Trips
ravib(at)ravib(dot)com
|
|
|
|
|
OnPaint doesnt fire for TextBox based controls. Its by design. You will have to hack with P/Invoke.
|
|
|
|
|
My assembly is compiled for 64-bit, it is calling an unmanaged DLL, also compiled for 64-bit.
The kicker is that "long" compiles to Int64 on my side whereas "long" compiles to Int32 on the unmanaged side (right?)
So if I declare the unmanaged API like this
[DllImport("MyDll.dll", SetLastError = false)]
static extern void FillTheLong
(
out long theLong,
);
And call it like this
long myLong = 0xFFFFFFFF;
long myOtherLong;
FillTheLong(out myLong, out myOtherLong);
I would expect to hose the stack, but nothing adverse happens across hundreds of calls like this. Hard to believe I am just getting lucky, is the runtime protecting me somehow.
Anybody got a pointer to resources on details of P/Invoke and marshaling?
Thanks
NIK
|
|
|
|
|
It depends. Does FillTheLong take a C++ long? Or an 8 byte integer (i.e. a C# long)?
If the former, your call will mess things up eventually; you'll have some bizzare errors occuring at some point most likely. If you have Visual Studio 2005 and you run this under a debugger, Visual Studio's Managed Debugger Assistant will catch this and let you know the stack is corrupted.
Tech, life, family, faith: Give me a visit.
I'm currently blogging about: Islamic Domination: Coming to a Jewish state near you!
The apostle Paul, modernly speaking: Epistles of Paul
Judah Himango
|
|
|
|
|
FillTheLong takes a C++ (32-bit) long, but I am passing a C# (64-bit) long.
in my example I should have been using
long myLong = 0x0F0F0F0FFFFFFFF;
There is a type mismatch, as confirmed by the fact that the value gets truncated.
I'm just surprised there isn't obvious stack corruption.
NIK
|
|
|
|
|
|
nicknotyet wrote: I'm just surprised there isn't obvious stack corruption.
Why would there be? Isn't that passed "by value". If the function parameter is only 32 bit then only a 32bit space is pushed onto the stack correct?
"What classes are you using ? You shouldn't call stuff if you have no idea what it does" Christian Graus in the C# forum
led mike
|
|
|
|
|
I'm not familiar with the physical layout of the stack, but yes as long as the construction of the callee's (unmanaged DLL) stack only pulls a 32-bit value from an allocated 64-bit space, then it should be ok.
Either way I still have re-write my code to explicitly use Int32 in place of long because of the truncation issues.
Thanks for the feedback.
|
|
|
|
|