Archive
HowTo: use a Timer component for delayed execution in WinForms
This is my contribution to:
http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms
Sometimes a Timer component is useful and easy to setup in WinForms, just set its interval and then enable it, then make sure the first thing you do in its Tick event handler is to disable itself.
I think Timer runs the code in its own thread, so you may still need to do a BeginInvoke (called upon the WinForm object [this]) to run your Action.
private WebBrowserDocumentCompletedEventHandler handler;
//need to make it a class field for the handler below
//(anonymous delegates seem to capture state at point of
// definition, so they can't capture their own reference) private string imageFilename; private bool exit; public void CaptureScreenshot(
Uri address = null,
string imageFilename = null,
int msecDelay = 0,
bool exit = false) { handler = (s, e) => { webBrowser.DocumentCompleted -= handler; //must do first this.imageFilename = imageFilename; this.exit = exit; timerScreenshot.Interval = (msecDelay>0)? msecDelay : 1; timerScreenshot.Enabled = true; }; webBrowser.DocumentCompleted += handler; Go(address); //if address == null, will use URL from UI } private void timerScreenshot_Tick(object sender, EventArgs e) { timerScreenshot.Enabled = false; //must do first BeginInvoke((Action)(() => //Invoke at UI thread { //run in UI thread BringToFront(); Bitmap bitmap = webBrowser.GetScreenshot(); if (imageFilename == null) imageFilename = bitmap.ShowSaveFileDialog(); if (imageFilename != null) { Directory.CreateDirectory(
Path.GetDirectoryName(
Path.GetFullPath(imageFilename)));
//create any parent directories needed bitmap.Save(imageFilename); } bitmap.Dispose(); //release bitmap resources if (exit) Close(); //this should close the app, this is main form }), null); }
You can see the above in action at ClipFlair‘s WebCapture tool (http://gallery.clipflair.net/WebCapture, source code at: http://ClipFlair.codeplex.com, see Tools/WebCapture folder) that grabs screenshots from websites.
BTW, if you want to call the executable from command-line make sure you go to Properties of the project and at Security tab turn-off ClickOnce security (else it can’t access command-line).
HowTo: Display version information on WinForm title
This is my contribution to
http://stackoverflow.com/questions/7178725/version-number-in-winform-form-text
I’m using the following at the WinForm of the WebCapture tool I’m making for ClipFlair:
public MainForm()
{
InitializeComponent();
Version version = Assembly.GetExecutingAssembly().GetName().Version;
Text = Text + " " + version.Major + "." + version.Minor +
" (build " + version.Build + ")"; //change form title
}
Not showing revision number to the user, build number is enough technical info
Make sure your AssemblyInfo.cs ends in the following (remove the version it has there by default) for VisualStudio to autoincrement build and revision number. You have to update major and minor versions yourself at every release (update major version for new features, minor version when you do just fixes):
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build
// and Revision Number by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.*")]
Update: Since now WebCapture is ClickOnce-deployed from the web (also has nice autoupdate support thanks to ClickOnce) and I want to avoid user confusion by showing the published version of the app (as the ClickOnce installation page does) on the WinForm titlebar, I now use the following code instead, based on a relevant post I found on the web:
public MainForm()
{InitializeComponent();
ShowVersion();
}
private void ShowVersion()
{Version version = (ApplicationDeployment.IsNetworkDeployed)?
ApplicationDeployment.CurrentDeployment.CurrentVersion :Assembly.GetExecutingAssembly().GetName().Version;
//if network deployed show published version (as the web install page)
Text = Text + " " + version.Major + "." + version.Minor + "."+ version.Build + "." + version.Revision;
//change form title
}
BTW, speaking of ClickOnce, I noticed that if at the autoupdate dialog shown when you launch the app from its desktop shortcut (and there’s a new version available) you press “Skip”, it won’t offer you this update anymore when you launch an app (I hope that when an even newer update is there it will prompt you), but you can override that behaviour by going to the installation page for your ClickOnce app (where you installed it from in the first place) and press the “Install” button there.
HowTo: Save screenshot of a control hosted on a WinForm
I’m trying to automate grabbing of activity screenshots from ClipFlair Activity Gallery, but most command-line screen capturing tools, like SiteShoter and IECapt fail to get an image from Silverlight content (they get just a background color).
Some other opensource tool that I could tweek was ThumbPage, but that didn’t support command-line and needed to disable Silverlight hardware acceleration in the webpage source (that is not define enableGPUAcceleration Silverlight param or set it to false) to grab the Silverlight content too in the image. This is probably because that tool was written in VB6 and must be using plain GDI instead of GDI+ or other newer graphics API, but I’m not sure if that’s the issue with the h/w acceleration.
So, I started making my own WinForms-based tool in Visual Studio. The following sample code is based on ArsenMkrt’s reply at StackOverflow, but this one allows you to capture a control in your form (my tool has a WebBrowser control in it and want to capture just its display). Note the use of PointToScreen method:
//Project: WebCapture //Filename: ScreenshotUtils.cs //Author: George Birbilis (http://zoomicon.com) //Version: 20130820 using System.Drawing; using System.Windows.Forms; namespace WebCapture { public static class ScreenshotUtils { public static Rectangle Offseted(this Rectangle r, Point p) { r.Offset(p); return r; } public static Bitmap GetScreenshot(this Control c) { return GetScreenshot(new Rectangle(c.PointToScreen(Point.Empty), c.Size)); } public static Bitmap GetScreenshot(Rectangle bounds) { Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height); using (Graphics g = Graphics.FromImage(bitmap)) g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size); return bitmap; } public const string DEFAULT_IMAGESAVEFILEDIALOG_TITLE = "Save image"; public const string DEFAULT_IMAGESAVEFILEDIALOG_FILTER = "PNG Image (*.png)|*.png|JPEG Image (*.jpg)|*.jpg|Bitmap Image (*.bmp)| *.bmp|GIF Image (*.gif)|*.gif"; //*** NOTE: the string above is a single line, split it for posting here ***// public static SaveFileDialog GetImageSaveFileDialog( string title = DEFAULT_IMAGESAVEFILEDIALOG_TITLE, string filter = DEFAULT_IMAGESAVEFILEDIALOG_FILTER) { SaveFileDialog dialog = new SaveFileDialog(); dialog.Title = title; dialog.Filter = filter; return dialog; } public static void ShowSaveFileDialog(this Image image, IWin32Window owner = null) { using (SaveFileDialog dlg = GetImageSaveFileDialog()) if (dlg.ShowDialog(owner) == DialogResult.OK) image.Save(dlg.FileName); } } }
Note the “using” clause above, it makes sure that the local variable g of type Graphics is Disposed immediately after the respective clause exits, instead of leaving any allocated resources for the garbage collector to handle. Plus the compiler makes sure the “g” variable isn’t use at the following code in that method by mistake (which could occur if you disposed it there by hand instead).
Having the Bitmap object you can just call Save on it:
private void btnCapture_Click(object sender, EventArgs e) { webBrowser.GetScreenshot().Save("C://test.jpg", ImageFormat.Jpeg); }
The above assumes the Garbage Collector (aka GC) will grab the bitmap, but maybe it’s better to assign the result of someControl.getScreenshot() to a Bitmap variable, then dispose that variable manually when finished, especially if you’re doing this grabbing often (say you have a list of webpages you want to load and save screenshots of them):
private void btnCapture_Click(object sender, EventArgs e) { Bitmap bitmap = webBrowser.GetScreenshot(); bitmap.ShowSaveFileDialog(); bitmap.Dispose(); //release bitmap resources }
Update:
Now WebCapture tool is ClickOnce-deployed from the web (also has nice autoupdate support thanks to ClickOnce) and you can find its source code at http://ClipFlair.codeplex.com.
Speaking of ClickOnce, I noticed that if at the autoupdate dialog shown when you launch the app from its desktop shortcut (and there’s a new version available) you press “Skip”, it won’t offer you this update anymore when you launch an app (I hope that when an even newer update is there it will prompt you), but you can override that behaviour by going to the installation page for your ClickOnce app (where you installed it from in the first place) and press the “Install” button there.
Save file dialog with Encoding dropdown
Time/Date slider (trackbar) component for .NET WinForms
http://www.codeproject.com/cs/miscctrl/TimeSlider.asp
for using only with time, use custom format of the style "hh:mm:ss.ff" (remember to set it to use the custom format instead of the predefined ones)
the only thing that doesn’t seem to work OK is the "ShowSegment" property – it doesn’t draw the red underline bar for the current time selection at the correct position
Flexible table components for .NET WinForms
http://www.devage.com/Wiki/ViewArticle.aspx?name=sourcegrid&version=0
quite mature
XPTable:
http://www.codeproject.com/cs/miscctrl/XPTable.asp
a bit underdeveloped, but interesting in that it tries to mimic JTable of Java/Swing. One could try J# Supplemental UI library instead, but J# Swing (sup ui lib) controls don’t interoperate with WinForms unfortunately (don’t know if anyone made a host control using reflection)
Delphi-like ActionList component for .NET WinForms
note that to add event handler for an action you have to first select
if from the component selector dropdownbox that is at the top of the
properties window of VS.net (can’t select it at the actions collection
editor)
I suggest rebuilding the sourcecode with .NET 2 (VS.net 2005), although the DLL seems to work ok even without doing that
Most Recently Used (MRU) component for .NET 2.0 WinForms
This very nice component was made for .NET 1.1 WinForms MenuBar control. If you scroll down that page to the discussions you’ll see a posting of mine with the code I converted to make it work with .NET 2.0 WinForms MenuStrip control
other .net 2.0 mru library is
http://www.codeproject.com/csharp/mrutoolstripmenu.asp (for .NET2’s MenuStrip)
but it’s not a component and one has to write code to use it