Setup
I'm using System.Windows.Forms.Form as window source. Constructor:
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
Form-Load:
_Device = new Device(this, DeviceConfiguration.SelectConfiguration(Bits.Eight, Bits.Eight, Bits.Eight, Bits.Eight, Bits.Twentyfour, Bits.Eight, true, true), 1024, 768);
Debug.WriteLine(GL.GetString(GL.GL_EXTENSIONS));
base.OnLoad(e);
In the load method the device configuration is selected, which is actually working. Calls like GL.GetString(GL.GL_EXTENSIONS) are working. All gl-methods are loaded too so all used methods are supported.
Form-Shown:
Application.Initialize(this);
RenderSettings = new RenderSettings(this);
Debug.WriteLine("After setup render settings: " + GL.GetErrorDescription());
_Textures[0] = Resources.LoadTexture("Resources\\Buttons\\btn_big_bg_normal.dds");
_Textures[1] = Resources.LoadTexture("Resources\\Buttons\\btn_big_bg_normal.dds");
base.OnShown(e);
_IsShown = true;
The Application.Initialize(this) initializes the framework used ( written by me ) and is also initializing a default shader, which is working, and model which is not working.
This causes the rendering later to fail because the returning handle is invalid.
Framework
I've a own OpenGL-wrapper which has enums for all parameters to enable development without looking in the OpenGL-references many times. As example the method glGenBuffers:
[MethodGL]
public delegate void glGenBuffers(Int32 count, ArrayBufferHandle[] buffers);
The ArrayBufferHandle definition:
/// <summary>
/// Represents a handle to a buffer
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 4)]
[ComVisible(true)]
public struct ArrayBufferHandle
{
[FieldOffset(0)]
UInt32 Handle;
/// <summary>
/// Determines if the buffer is valid
/// </summary>
public Boolean IsValid { get { return Handle > 0; } }
/// <summary>
/// Retrieves the default array buffer
/// <br>which is used to reset bindings</br>
/// </summary>
public static ArrayBufferHandle Default { get { return new ArrayBufferHandle(); } }
/// <summary>
/// Retrieves the string representation of the current object
/// </summary>
/// <returns>The current object represented as string</returns>
public override String ToString()
{
return String.Format("[ArrayBufferHandle Handle:{0} Valid:{1}]", Handle, IsValid);
}
}
Of course the shader creation is working and uses the types like the shown above.
Conclusion
I don't think i've much more to say to the issues i've faced. glGenBuffers and glGenTextures returning zero and i don't know why. The setup seems like others i've already used.
Of course the window is shown with a blue background when not rendering any models and i don't have any errors when using glGetError.
First: Thanks to Mārtiņš Možeiko who led me into the right direction.
As it turns out the method void glGenBuffer(Int32 count, UInt32[] buffers) is working, but the method void glGenBuffers(Int32 count, ArrayBufferHandle[] buffers) isn't. Both have a 4-byte / element array which is passed but .NET seems to handle them different. The solution has been simple:
void glGenBuffers(Int32 count, [Out]ArrayBufferHandle[] buffers)
Add a Out attribute to the array.
Related
As the title suggests I have a situation where I want to bring a secondary form (FormB which does not have a taskbar icon) to the front (z-order) when my main form (FormA) is activated (has focus).
For example, I have my program running with two forms (FormA and FormB). I then open up notepad maximized (just to cover both of the forms [FormA and FormB] that my application contains on the screen). Then I click the Windows Taskbar item for my application (which is FormA) at the bottom of the screen to open my application back up. When that happens FormB is not shown and is left in the background of the z-order. I would like for both forms be brought to the front of all other forms.
In the end when restoring FormA like any normal application, FormB should be the same Z-ORDER minus ONE.
Current Source Example
public partial class FormA : Form
{
public FormA()
{
Log FormB = new FormB();
FormB.Show();
Log FormB = new FormB();
FormB.ShowDialog();
}
private void FormA_Activated(object sender, EventArgs e)
{
if (FormB.Visible)
{
FormB.Show();
}
}
}
I ended up finding out how to do this with a little trial and error. In hopes this helps someone else out in the future I wanted to post my findings here.
Because this isn't directly supported by the .NET Form class you need to use the the SetWindowPos API. Four things need to be done to make this happen. All of the source below can be added to your FORM's source (FormA).
As shown below the Usage (#4) is showing an example that I had using tool strip menu items (File, Edit, etc) at the top of my main form (FormA). Then the second form which is my log form (FormB) is considered the secondary form. This can be done with multiple forms (more than 2 like my example shows).
Note: All of my tool strip menu items have CheckOnClick set to TRUE. This way each time you select the menu items they are being turned on and off.
Declare The Enums
#region Enums
public enum SpecialWindowHandles
{
/// <summary>
/// Places the window at the top of the Z order.
/// </summary>
HWND_TOP = 0,
/// <summary>
/// Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows.
/// </summary>
HWND_BOTTOM = 1,
/// <summary>
/// Places the window above all non-topmost windows. The window maintains its topmost position even when it is deactivated.
/// </summary>
HWND_TOPMOST = -1,
/// <summary>
/// Places the window above all non-topmost windows (that is, behind all topmost windows). This flag has no effect if the window is already a non-topmost window.
/// </summary>
HWND_NOTOPMOST = -2
}
[Flags]
public enum SetWindowPosFlags : uint
{
/// <summary>
/// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
/// </summary>
SWP_ASYNCWINDOWPOS = 0x4000,
/// <summary>
/// Prevents generation of the WM_SYNCPAINT message.
/// </summary>
SWP_DEFERERASE = 0x2000,
/// <summary>
/// Draws a frame (defined in the window's class description) around the window.
/// </summary>
SWP_DRAWFRAME = 0x0020,
/// <summary>
/// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
/// </summary>
SWP_FRAMECHANGED = 0x0020,
/// <summary>
/// Hides the window.
/// </summary>
SWP_HIDEWINDOW = 0x0080,
/// <summary>
/// Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
/// </summary>
SWP_NOACTIVATE = 0x0010,
/// <summary>
/// Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
/// </summary>
SWP_NOCOPYBITS = 0x0100,
/// <summary>
/// Retains the current position (ignores X and Y parameters).
/// </summary>
SWP_NOMOVE = 0x0002,
/// <summary>
/// Does not change the owner window's position in the Z order.
/// </summary>
SWP_NOOWNERZORDER = 0x0200,
/// <summary>
/// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
/// </summary>
SWP_NOREDRAW = 0x0008,
/// <summary>
/// Same as the SWP_NOOWNERZORDER flag.
/// </summary>
SWP_NOREPOSITION = 0x0200,
/// <summary>
/// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
/// </summary>
SWP_NOSENDCHANGING = 0x0400,
/// <summary>
/// Retains the current size (ignores the cx and cy parameters).
/// </summary>
SWP_NOSIZE = 0x0001,
/// <summary>
/// Retains the current Z order (ignores the hWndInsertAfter parameter).
/// </summary>
SWP_NOZORDER = 0x0004,
/// <summary>
/// Displays the window.
/// </summary>
SWP_SHOWWINDOW = 0x0040,
}
#endregion
Declare The APIs
#region APIs
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, SpecialWindowHandles hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
#endregion
Create The Functions
#region Functions
private void BringVisibleWindowsToFront()
{
//Get parent form handle
IntPtr hndParentWindow = this.Handle;
//Bring all childern forms to the front
foreach (Form frmChild in Application.OpenForms)
{
//If form is not this form
if (frmChild.Handle != this.Handle)
{
//If form is visible and not minimized
if (frmChild.WindowState != FormWindowState.Minimized && frmChild.Visible == true)
SetWindowPos(frmChild.Handle, SpecialWindowHandles.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
}
//Set the parent form to the top most z order
SetWindowPos(hndParentWindow, SpecialWindowHandles.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
private void SetAllFormWindowsAlwaysOnTop(bool active)
{
SpecialWindowHandles OnTop = (active ? SpecialWindowHandles.HWND_TOPMOST : SpecialWindowHandles.HWND_NOTOPMOST);
//Get parent form handle
IntPtr hndParentWindow = this.Handle;
//Bring all childern forms to the front
foreach (Form frmChild in Application.OpenForms)
{
//If form is not this form
if (frmChild.Handle != this.Handle)
{
//If form is visible and not minimized
if (frmChild.WindowState != FormWindowState.Minimized && frmChild.Visible == true)
SetWindowPos(frmChild.Handle, OnTop, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
}
//Set the parent form to the top most z order
SetWindowPos(hndParentWindow, OnTop, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
#endregion
Usage (tool strip menu items example)
#region ToolStripMenuItems events
//Shows or hides the log form (FormB)
private void showLogToolStripMenuItem_Click(object sender, EventArgs e)
{
if (showLogToolStripMenuItem.Checked)
{
FormB.ShowLog(true);
SetAllFormWindowsAlwaysOnTop(alwaysOnTopToolStripMenuItem.Checked);
}
else
FormB.ShowLog(false);
}
//Sets all the forms (in this case FormA and FormB) to always be on top or not
private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e)
{
SetAllFormWindowsAlwaysOnTop(alwaysOnTopToolStripMenuItem.Checked);
}
#endregion
There is documentation with definitions on what each enum does. But how am I able to demo/see this in practice? And how can I possibly know when to use which priority?
Here's some code I have created in attempt to see how the priorty affects the ordering, and it provides me with proof that the ordering is correct (the first loop iteration will have added a SystemIdle enum to the dispatch queue), but it still got added to the string last
private void btn_Click(object sender, RoutedEventArgs e)
{
StringBuilder result = new StringBuilder();
new Thread(() =>
{
var vals = Enum.GetValues(typeof(DispatcherPriority)).Cast<DispatcherPriority>().Where(y => y >= 0).ToList();
vals.Reverse();
vals.ForEach(x =>
{
Dispatcher.BeginInvoke(new Action(() =>
{
result.AppendLine(string.Format("Priority: {0} Enum:{1}", ((int)x), x.ToString()));
}), x);
});
}).Start();
ShowResultAsync(result, 2000);
}
private async void ShowResultAsync(StringBuilder s, int delay)
{
await Task.Delay(delay);
MessageBox.Show(s.ToString());
}
and the output order stays the same, even when the list is reversed (added this line just after vals gets assigned):
vals.Reverse();
So once again, is there anything more I can use when determining which dispatch priority I should assign?
In the Prism Framework the DefaultDispatcher which wraps Dispatcher uses a Normal priority. This should be the bread-and-butter for nearly all application scenarios.
/// <summary>
/// Wraps the Application Dispatcher.
/// </summary>
public class DefaultDispatcher : IDispatcherFacade
{
/// <summary>
/// Forwards the BeginInvoke to the current application's <see cref="Dispatcher"/>.
/// </summary>
/// <param name="method">Method to be invoked.</param>
/// <param name="arg">Arguments to pass to the invoked method.</param>
public void BeginInvoke(Delegate method, object arg)
{
if (Application.Current != null)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, method, arg);
}
}
}
As long as you are not running any actual logic on the UI thread I would recommend doing this.
If you did for some reason want to run "quick" logic on the UI thread you could follow the advice here and stick with a value of Background.
I did look into it a little and I found some usages in NuGet's source where they use Send, Normal, Background and ApplicationIdle for various reasons but in my WPF development I have never had to fine tune usage of DispatcherPriority to this degree.
A client has a problem with employees opening a program displaying sensitive information and walking away without closing it. They've asked me to monitor when the application is open and close it after a certain amount of inactivity.
What I've done is created a new program that launches the original, but with another form on top of it. If my form is transparent, I can click through the form with no problems, and manipulate the underlying program. If my form is (slightly) opaque, clicks will register with my program, but will not pass through.
What I need is a way to let my form register that a click has happened, reset its timer, and pass the click through to the underlying application. I can reset the timer, or I can pass the click through, but not both.
Here's the code I'm currently trying. The first section defines my form as transparent and keeps it on top of the other application.
private void Form1_Load(object sender, EventArgs e)
{
this.Opacity = 0.40;
this.TopMost = true;
}
I thought this would let me monitor for a click, reset the timer, then pass it through, but it's not working, so I must be missing something. EDIT: WT_NCHITTEST = 0x84, and HTTRANSPARENT = -1, as indicated here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645618%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCHITTEST) // m.Msg 0x84 means that Windows is asking our form whether it is "transparent" or not.
{
timeToClose = 10;
m.Result = new IntPtr(HTTRANSPARENT); // tell Windows yes, to pass everything through.
}
base.WndProc(ref m);
}
That is way over engineered, I implement inactivity checks with System.Timers.Timer and PInvoke GetLastInputInfo. Run a second application that monitors for workstation inactivity and close sensitive applications when the threshold is violated.
Initialize the timer with an interval and set it to auto-reset, then just check for inactivity every time the timer elapses. If the inactivity time is greater than your threshold, shut it down.
Here is the code for GetLastInputInfo.
public static class Tracker
{
/// <summary>
/// Reference to the GetLastInputInfo in the user32.dll file.
/// </summary>
/// <param name="plii">LASTINPUTINFO struct</param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
/// <summary>
/// Polls the system for the milliseconds elapsed since last user input.
/// </summary>
/// <returns>Milliseconds since last user input.</returns>
public static uint GetIdleTime()
{
LASTINPUTINFO lastInput = new LASTINPUTINFO();
lastInput.cbSize = (uint)Marshal.SizeOf(lastInput);
GetLastInputInfo(ref lastInput);
return ((uint)Environment.TickCount - lastInput.dwTime);
}
}
/// <summary>
/// Struct required and populated by the user32.dll GetLastInputInfo method.
/// </summary>
internal struct LASTINPUTINFO
{
public uint cbSize; //Size of the struct.
public uint dwTime; //TickCount at last input.
}
My inactivity timer implementation looks like this:
public void InitializeTimer()
{
ActivityTimer = new System.Timers.Timer(Configuration.TimeoutSettings["IntervalMilliseconds"]);
ActivityTimer.AutoReset = true;
ActivityTimer.Elapsed += OnElapsedPollForActivity;
ActivityTimer.Start();
}
/// <summary>
/// Method is called in ValidationForm_FormClosing, unsubscribes handler from event and stops the clock.
/// </summary>
public void TerminateTimer()
{
ActivityTimer.Elapsed -= OnElapsedPollForActivity;
ActivityTimer.Stop();
}
/// <summary>
/// Fires on ActivityTimer.Elapsed, polls workstation for time since last user input.
/// </summary>
private void OnElapsedPollForActivity(object sender, System.Timers.ElapsedEventArgs e)
{
if (Tracker.GetIdleTime() > Configuration.TimeoutSettings["TriggerMilliseconds"])
{
Logger.LogException(CurrentSession.SessionID, DateTime.Now, new InactivityException("Detected extended period of workstation inactivity."));
Application.Exit();
}
}
With this code:
using (pbDialog = new pbDialogs())
{
ProgressBar = new frmProgress(this, false);
ProgressBar.SetProgressLabelText("Inventory Data");
MessageBox.Show("Set progress label text to Inventory data");
typeProgress = (int) ProgressStates.ProgressQRY;
ProgressBar.label1.Text += " (Receiving)";
if (pbDialog != null)
{
MessageBox.Show("pbDialog is not null");
//pbDialog.ShowDialog(ProgressBar, this);
}
else
{
MessageBox.Show("pbDialog IS null");
ProgressBar.ShowDialog();
}
ProgressBar = null;
MessageBox.Show("Made it to compressDB()");
compressDB();
. . .
}
I see "Set progress label text to Inventory data"
then "pbDialog is not null"
then "Made it to compressDB()"
Nothing too odd there; but if I uncomment the line that is commented above, I see only "pbDialog is not null"
It is hanging for some reason as a result to the call to ShowDialog(); what is really strange, though, is that this prevents "Set progress label text to Inventory data" from displaying. Why would that be the case?
Note: I think the "pb" in the code stands for "peanut brittle" or some such; I'm pretty sure about the "brittle" part, anyway.
UPDATE
Yeah, the use of ShowDialog() with pbDialog is one of scads of examples that the original coder was practicing job security by obscurity - but then he [un]fortunately skedaddled, leaving in his wake a cesspool of spaghetti/eggshell code with no comments, misleading names and every sort of bizarre and convoluted, counterintuitive practice imaginable in the witches brew he purportedly considered a masterpiece of elegant design and clever-clever tricks.
pbDialog is an instance of a class (pbDialogs). Just to give you a taste of how macabre and convoluted and tangled it all is, here is that class:
public class pbDialogs : IDisposable
{
private static Form m_top;
public pbDialogs()
{
} // pbDialogs Constructor
public static void Activate( Form form )
{
form.Capture = true;
IntPtr hwnd = OpenNETCF.Win32.Win32Window.GetCapture();
form.Capture = false;
OpenNETCF.Win32.Win32Window.SetForegroundWindow( hwnd );
} // Activate
/// <summary>
/// This method makes ShowDialog work the way I want, I think.
/// </summary>
/// <remarks>
/// Here is what it does:
/// 1. Sets the caption of the new window to the same as the caller's.
/// 2. Clears the caption of the parent so it won't show up in the task list.
/// 3. When the ShowDialog call returns, brings the previous window
/// back to the foreground.
/// </remarks>
/// <param name="dialog"></param>
/// <param name="parent"></param>
public void ShowDialog( Form dialog, Control parent )
{
Control top = parent.TopLevelControl;
string caption = top.Text;
dialog.Text = caption;
top.Text = "--pending--"; // Don't show parent in task list
dialog.Activated += new EventHandler( form_Activated );
dialog.Closed += new EventHandler( form_Closed );
m_top = dialog; // New top-most form
dialog.ShowDialog();
m_top = (Form)top; // The top dialog just changed
dialog.Activated -= new EventHandler( form_Activated );
dialog.Closed -= new EventHandler( form_Closed );
top.Text = caption; // Make visible in task list again
Activate( (Form)top ); // And make it the active window
} // ShowDialog
/// <summary>
/// If one of our other windows, such as the main window,
/// receives an activate event, it will activate the current
/// top-most window instead.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void form_Activated( object sender, EventArgs e )
{
if( (m_top != null) && !(sender == m_top) ) // Is this the top-most window?
Activate( m_top ); // No, activate the top dialog
} // form_Activated
private static void form_Closed( object sender, EventArgs e )
{
m_top = null; // When you close the top dialog, it's not top anymore
} // form_Closed
#region IDisposable Members
public void Dispose()
{
// TODO: Add pbDialogs.Dispose implementation
}
#endregion
} // class pbDialogs
There is also a "related" ProgressBar -- a form which shares a file with pbDialogs, and whose instance variable is defined in the file that contains the code above thusly:
public static frmProgress ProgressBar;
This is definitely "whack-a-mole" code - if I make one small, seemingly innocuous change, all Dallas breaks loose in what even a semi-sane person would consider a completely unrelated part of the code.
This may be an indication of just how squirrelly this code/project is: I will make a new build after commenting out a couple of lines, and the size of the file will change from 400KB to 408, or from 412 to 408, etc. It's not normal behavior for an .exe to change that much in size (in a relative sense) with such a small change, is it?
UPATE 2
This, in frmProgress (which has both "public class frmProgress : System.Windows.Forms.Form" and "public class pbDialogs : IDisposable") scares me:
using System.Windows.Forms;
using OpenNETCF.Windows.Forms;
The second (OpenNETCF) is grayed out, indicating it's not really used, but it may be that it was previously used, and somehow that "Windows.Forms" code inadvertently got switched to "System" code, and that may be contributing to its current groundsquirellyness.
ShowDialog is generally a blocking call. The code will not continue past this until the dialog is closed.
I'll be copying a large file over the network using my winforms app and I need to show some kind of progress bar. Rather than cook up my own copy routine, I was thinking that it might be better to simply show the built-in file copy dialog.
I would also need a "Copy complete" and "Copy failed" notification.
I need this to work on Windows XP, Vista and 7. Is there a way to call to engage this functionality from my c# code?
Answer taken from: here
Windows Vista does indeed include a new copy engine that supports exactly what you're looking to do. However, it's possible that previously existing functionality may meet your needs. For example, if you want to copy, move, rename, or delete an individual file or directory, you can take advantage of SHFileOperation (exposed from shell32.dll), which is already wrapped by the Visual Basic® runtime. If you're using Visual Basic 2005, you can simply use functionality from the My namespace, for example:
My.Computer.FileSystem.CopyDirectory(
sourcePath, destinationPath, UIOption.AllDialogs)
Accomplishing the same thing in C# involves only a little more work, adding a reference to Microsoft.VisualBasic.dll (from the Microsoft® .NET Framework installation directory) and using code such as the following:
using Microsoft.VisualBasic.FileIO;
...
FileSystem.CopyDirectory(
sourcePath, destinationPath, UIOption.AllDialogs);
When run, this will result in the same progress UI you'd see if you were doing the same file operations from Windows Explorer. In fact, when running on Windows Vista, you automatically get the new Window Vista progress UI, as shown in Figure 1.
I know it's a old thread but I faced to similar requirement and finally this is the code I used,
maybe it helps someone else.
the credit belongs to here
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Pietschsoft
{
public class NativeProgressDialog
{
private IntPtr parentHandle;
private Win32IProgressDialog pd = null;
private string title = string.Empty;
private string cancelMessage = string.Empty;
private string line1 = string.Empty;
private string line2 = string.Empty;
private string line3 = string.Empty;
private uint value = 0;
private uint maximum = 100;
public string Title
{
get
{
return this.title;
}
set
{
this.title = value;
if (pd != null)
{
pd.SetTitle(this.title);
}
}
}
public string CancelMessage
{
get
{
return this.cancelMessage;
}
set
{
this.cancelMessage = value;
if (pd != null)
{
pd.SetCancelMsg(this.cancelMessage, null);
}
}
}
public string Line1
{
get
{
return this.line1;
}
set
{
this.line1 = value;
if (pd != null)
{
pd.SetLine(1, this.line1, false, IntPtr.Zero);
}
}
}
public string Line2
{
get
{
return this.line2;
}
set
{
this.line2 = value;
if (pd != null)
{
pd.SetLine(2, this.line2, false, IntPtr.Zero);
}
}
}
public string Line3
{
get
{
return this.line3;
}
set
{
this.line3 = value;
if (pd != null)
{
pd.SetLine(3, this.line3, false, IntPtr.Zero);
}
}
}
public uint Value
{
get
{
return this.value;
}
set
{
this.value = value;
if (pd != null)
{
pd.SetProgress(this.value, this.maximum);
}
}
}
public uint Maximum
{
get
{
return this.maximum;
}
set
{
this.maximum = value;
if (pd != null)
{
pd.SetProgress(this.value, this.maximum);
}
}
}
public bool HasUserCancelled
{
get
{
if (pd != null)
{
return pd.HasUserCancelled();
}
else
return false;
}
}
public NativeProgressDialog(IntPtr parentHandle)
{
this.parentHandle = parentHandle;
}
public void ShowDialog(params PROGDLG[] flags)
{
if (pd == null)
{
pd = (Win32IProgressDialog)new Win32ProgressDialog();
pd.SetTitle(this.title);
pd.SetCancelMsg(this.cancelMessage, null);
pd.SetLine(1, this.line1, false, IntPtr.Zero);
pd.SetLine(2, this.line2, false, IntPtr.Zero);
pd.SetLine(3, this.line3, false, IntPtr.Zero);
PROGDLG dialogFlags = PROGDLG.Normal;
if (flags.Length != 0)
{
dialogFlags = flags[0];
for (var i = 1; i < flags.Length; i++)
{
dialogFlags = dialogFlags | flags[i];
}
}
pd.StartProgressDialog(this.parentHandle, null, dialogFlags, IntPtr.Zero);
}
}
public void CloseDialog()
{
if (pd != null)
{
pd.StopProgressDialog();
//Marshal.ReleaseComObject(pd);
pd = null;
}
}
#region "Win32 Stuff"
// The below was copied from: http://pinvoke.net/default.aspx/Interfaces/IProgressDialog.html
public static class shlwapi
{
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
static extern bool PathCompactPath(IntPtr hDC, [In, Out] StringBuilder pszPath, int dx);
}
[ComImport]
[Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface Win32IProgressDialog
{
/// <summary>
/// Starts the progress dialog box.
/// </summary>
/// <param name="hwndParent">A handle to the dialog box's parent window.</param>
/// <param name="punkEnableModless">Reserved. Set to null.</param>
/// <param name="dwFlags">Flags that control the operation of the progress dialog box. </param>
/// <param name="pvResevered">Reserved. Set to IntPtr.Zero</param>
void StartProgressDialog(
IntPtr hwndParent, //HWND
[MarshalAs(UnmanagedType.IUnknown)] object punkEnableModless, //IUnknown
PROGDLG dwFlags, //DWORD
IntPtr pvResevered //LPCVOID
);
/// <summary>
/// Stops the progress dialog box and removes it from the screen.
/// </summary>
void StopProgressDialog();
/// <summary>
/// Sets the title of the progress dialog box.
/// </summary>
/// <param name="pwzTitle">A pointer to a null-terminated Unicode string that contains the dialog box title.</param>
void SetTitle(
[MarshalAs(UnmanagedType.LPWStr)] string pwzTitle //LPCWSTR
);
/// <summary>
/// Specifies an Audio-Video Interleaved (AVI) clip that runs in the dialog box. Note: Note This method is not supported in Windows Vista or later versions.
/// </summary>
/// <param name="hInstAnimation">An instance handle to the module from which the AVI resource should be loaded.</param>
/// <param name="idAnimation">An AVI resource identifier. To create this value, use the MAKEINTRESOURCE macro. The control loads the AVI resource from the module specified by hInstAnimation.</param>
void SetAnimation(
IntPtr hInstAnimation, //HINSTANCE
ushort idAnimation //UINT
);
/// <summary>
/// Checks whether the user has canceled the operation.
/// </summary>
/// <returns>TRUE if the user has cancelled the operation; otherwise, FALSE.</returns>
/// <remarks>
/// The system does not send a message to the application when the user clicks the Cancel button.
/// You must periodically use this function to poll the progress dialog box object to determine
/// whether the operation has been canceled.
/// </remarks>
[PreserveSig]
[return: MarshalAs(UnmanagedType.Bool)]
bool HasUserCancelled();
/// <summary>
/// Updates the progress dialog box with the current state of the operation.
/// </summary>
/// <param name="dwCompleted">An application-defined value that indicates what proportion of the operation has been completed at the time the method was called.</param>
/// <param name="dwTotal">An application-defined value that specifies what value dwCompleted will have when the operation is complete.</param>
void SetProgress(
uint dwCompleted, //DWORD
uint dwTotal //DWORD
);
/// <summary>
/// Updates the progress dialog box with the current state of the operation.
/// </summary>
/// <param name="ullCompleted">An application-defined value that indicates what proportion of the operation has been completed at the time the method was called.</param>
/// <param name="ullTotal">An application-defined value that specifies what value ullCompleted will have when the operation is complete.</param>
void SetProgress64(
ulong ullCompleted, //ULONGLONG
ulong ullTotal //ULONGLONG
);
/// <summary>
/// Displays a message in the progress dialog.
/// </summary>
/// <param name="dwLineNum">The line number on which the text is to be displayed. Currently there are three lines—1, 2, and 3. If the PROGDLG_AUTOTIME flag was included in the dwFlags parameter when IProgressDialog::StartProgressDialog was called, only lines 1 and 2 can be used. The estimated time will be displayed on line 3.</param>
/// <param name="pwzString">A null-terminated Unicode string that contains the text.</param>
/// <param name="fCompactPath">TRUE to have path strings compacted if they are too large to fit on a line. The paths are compacted with PathCompactPath.</param>
/// <param name="pvResevered"> Reserved. Set to IntPtr.Zero.</param>
/// <remarks>This function is typically used to display a message such as "Item XXX is now being processed." typically, messages are displayed on lines 1 and 2, with line 3 reserved for the estimated time.</remarks>
void SetLine(
uint dwLineNum, //DWORD
[MarshalAs(UnmanagedType.LPWStr)] string pwzString, //LPCWSTR
[MarshalAs(UnmanagedType.VariantBool)] bool fCompactPath, //BOOL
IntPtr pvResevered //LPCVOID
);
/// <summary>
/// Sets a message to be displayed if the user cancels the operation.
/// </summary>
/// <param name="pwzCancelMsg">A pointer to a null-terminated Unicode string that contains the message to be displayed.</param>
/// <param name="pvResevered">Reserved. Set to NULL.</param>
/// <remarks>Even though the user clicks Cancel, the application cannot immediately call
/// IProgressDialog::StopProgressDialog to close the dialog box. The application must wait until the
/// next time it calls IProgressDialog::HasUserCancelled to discover that the user has canceled the
/// operation. Since this delay might be significant, the progress dialog box provides the user with
/// immediate feedback by clearing text lines 1 and 2 and displaying the cancel message on line 3.
/// The message is intended to let the user know that the delay is normal and that the progress dialog
/// box will be closed shortly.
/// It is typically is set to something like "Please wait while ...". </remarks>
void SetCancelMsg(
[MarshalAs(UnmanagedType.LPWStr)] string pwzCancelMsg, //LPCWSTR
object pvResevered //LPCVOID
);
/// <summary>
/// Resets the progress dialog box timer to zero.
/// </summary>
/// <param name="dwTimerAction">Flags that indicate the action to be taken by the timer.</param>
/// <param name="pvResevered">Reserved. Set to NULL.</param>
/// <remarks>
/// The timer is used to estimate the remaining time. It is started when your application
/// calls IProgressDialog::StartProgressDialog. Unless your application will start immediately,
/// it should call Timer just before starting the operation.
/// This practice ensures that the time estimates will be as accurate as possible. This method
/// should not be called after the first call to IProgressDialog::SetProgress.</remarks>
void Timer(
PDTIMER dwTimerAction, //DWORD
object pvResevered //LPCVOID
);
}
[ComImport]
[Guid("F8383852-FCD3-11d1-A6B9-006097DF5BD4")]
public class Win32ProgressDialog
{
}
/// <summary>
/// Flags that indicate the action to be taken by the ProgressDialog.SetTime() method.
/// </summary>
public enum PDTIMER : uint //DWORD
{
/// <summary>Resets the timer to zero. Progress will be calculated from the time this method is called.</summary>
Reset = (0x01),
/// <summary>Progress has been suspended.</summary>
Pause = (0x02),
/// <summary>Progress has been resumed.</summary>
Resume = (0x03)
}
[Flags]
public enum PROGDLG : uint //DWORD
{
/// <summary>Normal progress dialog box behavior.</summary>
Normal = 0x00000000,
/// <summary>The progress dialog box will be modal to the window specified by hwndParent. By default, a progress dialog box is modeless.</summary>
Modal = 0x00000001,
/// <summary>Automatically estimate the remaining time and display the estimate on line 3. </summary>
/// <remarks>If this flag is set, IProgressDialog::SetLine can be used only to display text on lines 1 and 2.</remarks>
AutoTime = 0x00000002,
/// <summary>Do not show the "time remaining" text.</summary>
NoTime = 0x00000004,
/// <summary>Do not display a minimize button on the dialog box's caption bar.</summary>
NoMinimize = 0x00000008,
/// <summary>Do not display a progress bar.</summary>
/// <remarks>Typically, an application can quantitatively determine how much of the operation remains and periodically pass that value to IProgressDialog::SetProgress. The progress dialog box uses this information to update its progress bar. This flag is typically set when the calling application must wait for an operation to finish, but does not have any quantitative information it can use to update the dialog box.</remarks>
NoProgressBar = 0x00000010
}
#endregion
}
}
How you can use it:
private Pietschsoft.NativeProgressDialog pd;
private uint progressPercent;
Timer timer1 = new Timer();
private void button1_Click_1(object sender, EventArgs e)
{
timer1.Interval = 300;
timer1.Tick += (s,ev)=>
{
progressPercent++;
if (pd.HasUserCancelled)
{
timer1.Stop();
pd.CloseDialog();
}
else
{
// Update the progress value
pd.Value = progressPercent;
pd.Line2 = "Percent " + progressPercent.ToString() + "%";
if (progressPercent >= 100)
{
timer1.Stop();
pd.CloseDialog();
}
}
};
pd = new Pietschsoft.NativeProgressDialog(this.Handle);
pd.Title = "Performing Operation";
pd.CancelMessage = "Please wait while the operation is cancelled";
pd.Maximum = 100;
pd.Value = 0;
pd.Line1 = "Line One";
pd.Line3 = "Calculating Time Remaining...";
//pd.ShowDialog(); // Defaults to PROGDLG.Normal
pd.ShowDialog(Pietschsoft.NativeProgressDialog.PROGDLG.Modal, Pietschsoft.NativeProgressDialog.PROGDLG.AutoTime, Pietschsoft.NativeProgressDialog.PROGDLG.NoMinimize);
progressPercent = 0;
timer1.Start();
}