|
This is very nice work.
I tested the wizard control yesterday, and apart from a few small things, it worked perfectly. The few improvement suggestions I have are the following:
1) There should be a FinishButtonEnabled property. You might not want the button to be enabled before the user has performed some kind of action on the last page.
2) If I set the HelpButtonVisible to False, I would like the other buttons to align to the right so that I don't have the big space where the help button normally is.
3) I want the intermediate step and the finish step to have the same look as the start step - i.e. with an image on the left hand side. So I set the Binding Image to the supplied Left.png (the same that is used in the start step). But it didn't work. The image was stretched to be a top aligned banner like the one that was already there in the intermediate step.
All of these things I'm sure I can fix myself editing the source, but I thought I'd tell about them. It might be a good improvement for future versions.
Apart from these little issues, thank you very much for a nice control.
Cheers,
Johnny J.
|
|
|
|
|
Thanks a lot.
For the first one use NextButtonEnabled property.
I will definately look into these issue.
|
|
|
|
|
Very well done - and thank you for sharing. This saved me a lot of time.
One suggestion - a method to set the current step.
|
|
|
|
|
I have a textbox and a label on an intermediate step. It's position is set like so:
<br />
this.label1.AutoSize = true;<br />
this.label1.Location = new System.Drawing.Point(13, 89);<br />
this.label1.Name = "lblGroupName";<br />
this.label1.Size = new System.Drawing.Size(70, 13);<br />
this.label1.TabIndex = 0;<br />
this.label1.Text = "Group Name:";<br />
<br />
this.txtGroupName.Anchor = System.Windows.Forms.AnchorStyles.None;<br />
this.txtGroupName.Location = new System.Drawing.Point(212, 82);<br />
this.txtGroupName.Name = "txtGroupName";<br />
this.txtGroupName.Size = new System.Drawing.Size(478, 20);<br />
this.txtGroupName.TabIndex = 1;<br />
But when the wizard runs, the textbox is way down and right of where it should be. The label remains exactly where it should be.
Can anyone point me to where I can troubleshoot the issue?
|
|
|
|
|
I have a similar issue. It has to do with the Anchor property of controls, put inside a WizardStep. If I set control's (e.g. GroupBox) Anchor = Top, Bottom, Left to a it looks OK in design time, but at runtime it gets stretched way down, below the border of the wizard step. The problem occurs not only with the Top-Bottom anchoring, but with the Right anchor, too.
It seems to me that when the position of the controls is determined, the container control is somehow at its maximum size (not the size I see in the designer).
UPDATE: I debugged the problem with a simple scenario: an intermediate step (middleStep) and of a groupbox in it. Initially the middleStep's size is (0,0). When adding it to the wizardStepsPanel.Controls in WizardControl.DoReLayout() the middleStep size changes to the container's size, but the groupbox expands too much.
Initial:
demoWizard.Size {Width = 524 Height = 394}
middleStep.Size {Width = 0 Height = 0}
groupBox1.Size {Width = 50 Height = 100}
After DoReLayout():
demoWizard.Size {Width = 524 Height = 394}
middleStep.Size {Width = 524 Height = 354}
groupBox1.Size {Width = 574 Height = 454}
It is easy to notice that final groupBox1.Size = initial groupBox1.Size + middleStep.Size.
A quick workaround is to set the initial wizard step size, without affecting the controls in it. We can add and call the following method: ResizeProperlyAllWizardSteps() just after the wizard control is initialized:
<br />
<br />
public void ResizeProperlyAllWizardSteps()<br />
{<br />
Size size = wizardStepsPanel.Size;<br />
foreach(WizardStep step in wizardStepCollection)<br />
{<br />
ResizeWizardStepWithoutAffectingControls(step, size);<br />
}<br />
}<br />
<br />
private static void ResizePageWithoutAffectingControls(WizardStep step, Size size)<br />
{<br />
if(step.Size == size)<br />
return;<br />
<br />
Control[] controls = new Control[step.Controls.Count];<br />
step.Controls.CopyTo(controls, 0);<br />
<br />
step.Controls.Clear();<br />
<br />
step.Size = size;<br />
<br />
step.Controls.AddRange(controls);<br />
}
NOTE: Unfortunately at design time sometimes the controls' size and location get changed. I suppose that a similar fix can be applied, but don't have the time to investigate.
-- modified at 4:14 Tuesday 29th May, 2007
-- modified at 6:36 Tuesday 29th May, 2007
-- modified at 12:24 Tuesday 29th May, 2007
Ivan Mitev
|
|
|
|
|
Actually, I don't believe the size of the step container is set at all. It looks like the default value is very small (e.g. 0,0) and the anchors are set from there. In any case, I've been farting with the same problem, although I took a different path with my solution.
I added a DockedControl property to the IntermediateStep control. If you assign this control, it's "docked" below the title/header information. There's some smoke and mirrors in there, but it solves the issue with sizing and the step containers. Make it a panel or similar container and the controls you place inside that panel should now follow the proper anchoring/docking behavior.
Here's the details. I'd be happy to post the code, if I had a good place to put it,
Added the following code to IntermediateStep, including a call to InitializeComponent() below:
///
/// Height of the header information.
///
private int HEADER_HEIGHT = 60;
// Control specifically noted for docking within the step.
// This allows us to dock it under all the title information
// at the top.
private Control mDockedControl = null;
///
/// Initialize the component.
///
private void InitializeComponent()
{
SizeChanged += new System.EventHandler(
IntermediateStep_SizeChanged);
}
///
/// Resize any "docked" controls so that they continue to appear
/// docked (under the header).
///
///
/// <param name="sender" />
/// sender
///
/// <param name="e" />
/// e
///
private void IntermediateStep_SizeChanged(
object sender, System.EventArgs e)
{
if (mDockedControl != null)
{
mDockedControl.Size =
new Size(Size.Width, Size.Height - HEADER_HEIGHT);
}
}
///
/// This control will be docked under the header information
/// shown in the step.
///
[Description("Specify a control to dock under the header information."), Category("Layout")]
public Control DockedControl
{
get
{
return mDockedControl;
}
set
{
mDockedControl = value;
// Make it looked docked.
if (mDockedControl != null)
{
mDockedControl.Location =
new Point(0, HEADER_HEIGHT + 1);
mDockedControl.Size =
new Size(Size.Width, Size.Height - HEADER_HEIGHT);
}
}
}
-- Ken Wootton
|
|
|
|
|
Thanks Ken,
Your workaround worked for me, even though it was not very easy to apply those changes in an existing wizard with 7 pages.
I actually moved your suggested code to the WizardStep base class (instead of IntermediateStep) and made minor modifications, which I post here:
<br />
protected int HEADER_HEIGHT = 60;<br />
protected int FOOTER_HEIGHT = 0;<br />
protected int LEFT_MARGIN_WIDTH = 0;<br />
<br />
private void InitializeComponent()<br />
{<br />
SizeChanged += WizardStep_SizeChanged;<br />
}<br />
<br />
private void WizardStep_SizeChanged(object sender, EventArgs e)<br />
{<br />
if (mDockedControl != null)<br />
{<br />
mDockedControl.Size = new Size(<br />
Size.Width - LEFT_MARGIN_WIDTH,<br />
Size.Height - HEADER_HEIGHT - FOOTER_HEIGHT);<br />
}<br />
}<br />
<br />
[Description("Specify a control to dock under the header information.")]<br />
[Category("Layout")]<br />
public Control DockedControl<br />
{<br />
get { return mDockedControl; }<br />
set<br />
{<br />
mDockedControl = value;<br />
<br />
if (mDockedControl != null)<br />
{<br />
mDockedControl.Location = new Point(LEFT_MARGIN_WIDTH, HEADER_HEIGHT);<br />
mDockedControl.Size =<br />
new Size(Size.Width - LEFT_MARGIN_WIDTH, Size.Height - HEADER_HEIGHT - FOOTER_HEIGHT);<br />
}<br />
}<br />
}
That's how I can just set in StartStep: LEFT_MARGIN_WIDTH = 180; and analogically set FOOTER_HEIGHT in the FinishStep.
Ivan Mitev
|
|
|
|
|
Ah. Does that make the docked property available on all pages, including the start page? At the time I wrote the previous code, I didn't realize this problem occasionally happens with the start page as well. Next time I touch that code, I'll have to apply your update.
|
|
|
|
|
Yes, this issue affects all pages (including Start and Finish). Putting the DockedControl property in the base class solves the problem.
My previous workaround worked only when resizing the current page, while the others pages didn't respond to the resizing.
Ivan Mitev
|
|
|
|
|
I have an easier solution that solves this problem. Simply open up WizardStep.cs and find the Size property. Change the DesignerSerializationVisibility.Hidden to DesignerSerializationVisibility.Visible. This will force the step size to be added to the designer generated code and the anchors will work.
This works best if you are adding a new WizardControl to a form. If you existing WizardControl and don't feel like remaking it, just add a step and then remove it and the designer code should update itself with the Size in there.
|
|
|
|
|
Hi,
is it possible to use the wizard in commercial applications?
BTW: great work
|
|
|
|
|
You can use this in commertial application......
|
|
|
|
|
Could you please actually specify a licence for your code? Without an explicit licence in the code, no-one is actually allowed to use your work, which is a shame.
Given that you have already said that it's OK to use this code in commercial apps, the BSD licence would probably be appropriate as it permits use in both free software and proprietary software projects:
http://www.opensource.org/licenses/bsd-license.html
It requires people to retain your copyright headers (which you should also include!) but do anything with it they please.
Alternatively, if you are releasing this code into the Public Domain, please explicitly state that in the code.
And BTW your AssemblyInfo.cs file lists the copyright holder as Microsoft.... you might want to change that!
|
|
|
|
|
it is a very good works, thanks
|
|
|
|
|
Hi,
very cool control!
I need to modify the position, size and text alignment for the Next, Previous etc. buttons. If i open the WizardControl.cs file the designer of VS 2005 displays an error.
Can you give me some advice, how to achieve this?
Thanks
Thomas
|
|
|
|
|
Right click on "WizardControl.cs" the select menuitem "View code".
Most simple solution: publish the Buttons as properties. Then you can modify them directly in designtime.
<br />
[Category("WizardControl Buttons")]<br />
public Button ButtonBack<br />
{<br />
get { return BackButton; }<br />
set { BackButton= value; }<br />
}<br />
<br />
[Category("WizardControl Buttons")]<br />
public Button ButtonNext<br />
{<br />
get { return NextButton; }<br />
set { NextButton = value; }<br />
}<br />
<br />
[Category("WizardControl Buttons")]<br />
public Button ButtonCancel<br />
{<br />
get { return CancelButton; }<br />
set { CancelButton = value; }<br />
}<br />
<br />
[Category("WizardControl Buttons")]<br />
public Button ButtonHelp<br />
{<br />
get { return HelpButton; }<br />
set { HelpButton = value; }<br />
}<br />
|
|
|
|
|
Thank you very much - this helps!
Another question: Dependend on the actual context i need to disable a interior wizard page so if it should be activated the next page appears. The same in the back direction.
I tried some ways, but they have all disadvantages. Can you tell me the best way to achieve this?
Thanks in advance!
|
|
|
|
|
Skipping a step can be done using the "NextButtonClick" - event of the WizardControl.
There you can check the index of the current page and (depending on the context) alter the index of the next page.
Unfortunately the control still has a bug preventing a correct evaluation of the current index.
To fix the bug change the following methods in "WizardStepCollection.cs":
<br />
public int Add(object value)<br />
{<br />
WizardSteps.Add(value as WizardStep);<br />
(value as WizardStep).WizardControl = Owner;<br />
if (Count != 1)<br />
{<br />
owner.UpdateButtons();<br />
}<br />
else<br />
{<br />
owner.OnSetFirstStep();<br />
}<br />
<br />
OnStepAdded();<br />
return WizardSteps.Count;<br />
}<br />
<br />
public void Insert(int index, object value)<br />
{<br />
WizardSteps.Insert(index, value as WizardStep);<br />
(value as WizardStep).WizardControl = Owner;<br />
owner.OnChangeCurrentStepIndex(index, true);<br />
if (Count != 1)<br />
{<br />
owner.UpdateButtons();<br />
}<br />
else<br />
{<br />
owner.OnSetFirstStep();<br />
}<br />
OnStepAdded();<br />
}<br />
<br />
public void Remove(object value)<br />
{<br />
int i = WizardSteps.IndexOf(value as WizardStep);<br />
WizardSteps.Remove(value as WizardStep);<br />
(value as WizardStep).WizardControl = null;<br />
owner.OnChangeCurrentStepIndex(i - 1, true);<br />
if (Count != 1)<br />
{<br />
owner.UpdateButtons();<br />
}<br />
else<br />
{<br />
owner.OnSetFirstStep();<br />
}<br />
OnStepRemoved();<br />
}<br />
<br />
public void RemoveAt(int index)<br />
{<br />
WizardSteps[index].WizardControl = null;<br />
WizardSteps.RemoveAt(index); <br />
owner.OnChangeCurrentStepIndex(index, true); <br />
owner.OnResetWizardSteps();<br />
if (Count != 1)<br />
{<br />
owner.UpdateButtons();<br />
}<br />
else<br />
{<br />
owner.OnSetFirstStep();<br />
}<br />
OnStepRemoved();<br />
}<br />
Now you can check correctly in the event mentioned above:
<br />
private void WizardControl1_NextButtonClick(WizardControl sender, WizardNextButtonClickEventArgs args)<br />
{<br />
if (WizardControl1.CurrentStepIndex == Step2.StepIndex)<br />
{<br />
if (check something here)<br />
args.NextStepIndex = Step5.StepIndex; <br />
}<br />
}<br />
|
|
|
|
|
Hi again,
i think there is a little bug in the StepIndex property (WizardStep.cs):
In my opinion the code
if (wizardControlParent.WizardSteps[i].Name != Name)
{
return i;
}
should be changed to
if (wizardControlParent.WizardSteps[i].Name == Name)
{
return i;
}
After this everything works great.
Am i correct?
Greetings
|
|
|
|
|
Even more elegant, you could just check the collection for the existence of the current step:
<br />
public int StepIndex<br />
{<br />
get<br />
{<br />
return wizardControlParent == null ?<br />
-1 :<br />
wizardControlParent.WizardSteps.IndexOf(this); <br />
}<br />
}<br />
-- modified at 4:48 Wednesday 16th May, 2007
|
|
|
|
|
Hi again,
looks good, but it results in an stack overflow, because the
IndexOf() Method was called recursively in the WizardStepsCollection.
I fixed it like this and it works:
From: public int IndexOf(object value)
{
return IndexOf(value as WizardStep);
}
To: public int IndexOf(object value)
{
return WizardSteps.IndexOf(value as WizardStep);
}
Greetings
|
|
|
|
|
Whoops sorry - I missed to mention that fix in my last post.
|
|
|
|
|
I've just tried this fix and every time I click on the intermediate steps it crashes my application, I will check this again in case I've made a mistake.
|
|
|
|
|
I'm not seeing a "WizardStepCollectin.cs" file
|
|
|
|
|
Okay i have to admit this is a brilliant piece of work
When i click on either the smarttag or the WizardSteps collection button i get the following exception :
Unable to cast object of type 'WizardBase.WizardStepCollection' to type 'WizardBase.WizardStepCollection'
|
|
|
|