I came across such situation and i decided to write this little Form that has built in support for displaying overall and detail progresses and manages to dispatch thread calls into the thread owning the callers.
// Basic configs.
formProgress = new FormProgress();
formProgress.OperationCancelRequested +=
new FormProgress.OperationCancelRequestedDelegate(formProgress_OperationCancelRequested);
formProgress.ShowPercentageInTitle = true;
formProgress.ShowProgressDetails = true;
formProgress.SupportCancelation = true;
formProgress.ResetOnCancel = true;
// These will be performed likely from other threads , or backgroud worker.
formProgress.OverallProgressHeaderText = "Overall Progress:";
formProgress.OverallProgressPercentage = 30;
formProgress.DetailProgressHeaderText = "Operation n in progress...";
formProgress.DetailProgressPercentage = 20;
Source Code:
private static object syncLock = new object();
public delegate void OperationCancelRequestedDelegate();
public event OperationCancelRequestedDelegate OperationCancelRequested;
private bool showPercentageInTitle;
///
/// If set to true then the global progress percentage will be appended to the title.
///
public bool ShowPercentageInTitle
{
get { return showPercentageInTitle; }
set
{
if (showPercentageInTitle != value)
{
showPercentageInTitle = value;
UpdateTitleDisplay();
}
}
}
private bool supportCancelation = false;
///
/// if set to true, a cancel button will apear in the form when clicked raises OperationCancelRequested event.
///
public bool SupportCancelation
{
get
{
return supportCancelation;
}
set
{
supportCancelation = value;
SetControlPropertyValue(btnCancel, "Visible", value);
}
}
private bool cancelRequested = false;
///
/// Returns a value indicating if canceling the operation is requested.
///
public bool CancelRequested { get { return cancelRequested; } }
private string title;
///
/// Main progress dialog title.
///
public string Title
{
get
{
return this.title;
}
set
{
if (this.title != value)
{
this.title = value;
UpdateTitleDisplay();
}
}
}
///
/// Global progress percentage
///
public int OverallProgressPercentage
{
get
{
return mainProgressBar.Value;
}
set
{
if (value != mainProgressBar.Value)
{
if (value < 0 || value > 100)
{
throw new ArgumentOutOfRangeException("OverallProgressPercentage", value,
"OverallProgressPercentage should be between 0 and 100");
}
else
{
SetControlPropertyValue(mainProgressBar, "Value", value);
UpdateTitleDisplay();
}
}
}
}
///
/// Detail progress percentage
///
public int DetailProgressPercentage
{
get
{
return detailProgressBar.Value;
}
set
{
if (value != detailProgressBar.Value)
{
if (value < 0 || value > 100)
{
throw new ArgumentOutOfRangeException("DetailProgressPercentage", value,
"DetailProgressPercentage should be between 0 and 100");
}
else
{
SetControlPropertyValue(detailProgressBar, "Value", value);
}
}
}
}
///
/// Text to be displayed as a global progress header information.
///
public string OverallProgressHeaderText
{
get
{
return lblOverallProgress.Text;
}
set
{
SetControlPropertyValue(lblOverallProgress, "Text", value);
}
}
///
/// Text to be displayed as a detail progress header information.
///
public string DetailProgressHeaderText
{
get
{
return lblDetailProgress.Text;
}
set
{
SetControlPropertyValue(lblDetailProgress, "Text", value);
}
}
private bool showProgressDetails = true;
private int orignalGroupDetailsHeight = 0;
///
/// If set to true it will display progress details.
///
public bool ShowProgressDetails
{
get
{
return showProgressDetails;
}
set
{
if (showProgressDetails != value)
{
showProgressDetails = value;
// Set the new details groupbox visibility.
SetControlPropertyValue(groupBoxDetails, "Visible", showProgressDetails);
// Update the control's Height.
int height = this.Height;
if (showProgressDetails)
{
height += orignalGroupDetailsHeight;
}
else
height -= orignalGroupDetailsHeight;
SetControlPropertyValue(this, "Height", height);
}
}
}
private void UpdateTitleDisplay()
{
string valueToDisplay = title;
if (showPercentageInTitle)
{
valueToDisplay += (" " + OverallProgressPercentage.ToString() + "%");
}
if (String.IsNullOrEmpty(valueToDisplay))
valueToDisplay = " ";
SetControlPropertyValue(this, "Text", valueToDisplay);
}
private delegate void SetControlPropertyValueDelegate(Control control, System.Reflection.PropertyInfo pInfo, object value);
private void SetControlPropertyValue(Control control, string propertyName, object value)
{
// Set to control thread's access to this Form properties.
lock (syncLock)
{
if (control == null)
{
throw new ArgumentNullException("control");
}
if (String.IsNullOrEmpty(propertyName))
{
throw new ArgumentNullException("propertyName");
}
System.Reflection.PropertyInfo pInfo = control.GetType().GetProperty(propertyName);
if (pInfo == null)
{
throw new Exception("control: " + control.Name + " does not have the property: " + propertyName);
}
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyValueDelegate(SetControlPropertyValue),
control, pInfo, value);
}
else
{
pInfo.SetValue(control, value, null);
}
}
}
private void SetControlPropertyValue(Control control, System.Reflection.PropertyInfo pInfo, object value)
{
pInfo.SetValue(control, value, null);
}
public FormProgress()
{
InitializeComponent();
// Main title can't be null or empty otherwise the form will become locked and can not be moved.
this.Text = " ";
orignalGroupDetailsHeight = groupBoxDetails.Height;
}
private void btnCancel_Click(object sender, EventArgs e)
{
btnCancel.Text = "Canceling...";
btnCancel.Enabled = false;
this.cancelRequested = true;
// Process all remainig messages for this form.
Application.DoEvents();
lock (syncLock)
{
if (OperationCancelRequested != null)
{
// Make sure to call the target on its own thread.
if (OperationCancelRequested.Target is Control)
{
Control target = OperationCancelRequested.Target as Control;
if (target.InvokeRequired)
{
target.Invoke(OperationCancelRequested, null);
}
else
OperationCancelRequested();
}
else
OperationCancelRequested();
}
if (resetOnCancel)
Reset();
}
}
private bool resetOnCancel = true;
///
/// If set to true, the value of the progress bars will we set to 0 and labels will be empty.
///
public bool ResetOnCancel
{
get
{
return resetOnCancel;
}
set
{
resetOnCancel = value;
}
}
private void Reset()
{
btnCancel.Text = "Cancel";
btnCancel.Enabled = true;
lblDetailProgress.Text = "";
lblOverallProgress.Text = "";
Text = " ";
ResetProgress();
}
private void ResetProgress()
{
mainProgressBar.Value = 0;
detailProgressBar.Value = 0;
}
No comments:
Post a Comment