Workflow Foundation - Resizing the RuleSetDialog

If you've ever spent any time developing business rules in Windows Workflow Foundation using the RuleSetDialog then you'll have very quickly become infuriated with its inability to resize. The "letterbox" view of if/then/else expressions that allows for a maximum of three lines of text becomes extremely hard to work with if your rules are anything but very basic. It’s a common gripe. Unfortunately with the advent of WF4 nothing changed.

If you're using the RuleSetDialog re-hosted externally to Visual Studio (see the External RuleSet Toolkit Sample as an example), or perhaps using the Workflow Foundation 4 Policy Activity from the Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4, then this is a quick post to share a simple solution that will make life good again.

clip_image001_2_4B0B3011

Firstly, I'd like to give credit to this post by Timur Fanshteyn who introduced the idea. The solution presented here extends it by providing proportional resizing of the Rule Set panel, Rule Definition panel and the if/then/else text boxes as apposed to just making the "if" and "else" text boxes anchor to their parent container. The screenshot above shows the resized dialog. The Rule Set panel will always be approximately 1/3 the size of the Rule Definition panel and the text boxes within the Rule Definition panel are equally sized.

So, let's look at the code changes that you need to apply to your application.

MakeRuleSetDialogResizeable is the method you call from your code, passing it an instance of the RuleSetDialog that needs resizing behaviour added. It enables resizing of the dialog, anchors the top level controls and adds an event handler that will be called each time the user attempts to resize the dialog in order to recalculate the new layout.

 private static void MakeRuleSetDialogResizable(RuleSetDialog ruleSetDialog)
{
    ruleSetDialog.FormBorderStyle = FormBorderStyle.Sizable;
    ruleSetDialog.HelpBtton = false;
    ruleSetDialog.MaximizeBox = true;
    ruleSetDialog.MinimumSize = new Size(580, 440);
    ruleSetDialog.Controls["okCancelTableLayoutPanel"].Anchor = AnchorStyles.Right | AnchorStyles.Bottom;
    ruleSetDialog.Controls["rulesGroupBox"].Controls["panel1"].Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
    ruleSetDialog.Controls["rulesGroupBox"].Controls["panel1"].Controls["chainingBehaviourComboBox"].Anchor = AnchorStyles.Top | AnchorStyles.Right;
    ruleSetDialog.Controls["rulesGroupBox"].Controls["panel1"].Controls["chainingLabel"].Anchor = AnchorStyles.Top | AnchorStyles.Right;
    ruleSetDialog.Controls["rulesGroupBox"].Controls["panel1"].Controls["rulesListView"].Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
    ruleSetDialog.Resize += new EventHandler(RuleSetDialog_Resize);
}

Next we introduce the event handler that is called whenever we resize. The actual work is delegated to the LayoutRuleSetDialog method.

 private static void RuleSetDialog_Resize(object sender, EventArgs e)        
{
    Control ruleSetDialog = sender as Control;
    if (ruleSetDialog != null)
    {
        LayoutRuleSetDialog(ruleSetDialog);
    }
}

Since the .NET Windows Forms framework does not provide any out-of-the-box layout managers that will resize our controls for us, the size and position of each control needs to be recalculated based on the new size of the dialog. The following method calculates the new size and position of the Rules Set panel (rulesGroupBox) and the Rules Definition panel (ruleGroupBox) giving the former 1/3 and the latter 2/3 of the estate. The layout of the content of ruleGroupBox is then delegated to the LayoutRuleGroupBox.

 private static void LayoutRuleSetDialog(Control ruleSetDialog)
{
    const int TopOffset = 50;
    const int BottomOffset = 50;
    const int Padding = 20;
    const int LeftMargin = 10;
 
    int availableHeight = ruleSetDialog.Size.Height - TopOffset - BottomOffset;
    int rulesGroupBoxHeight = availableHeight / 3;
    int ruleGroupBoxHeight = availableHeight - rulesGroupBoxHeight;
    int groupBoxWidth = ruleSetDialog.Size.Width - 20;
 
    Control rulesGroupBox = ruleSetDialog.Controls["rulesGroupBox"];
    if (rulesGroupBox != null)
    {
        rulesGroupBox.SetBounds(LeftMargin, TopOffset, groupBoxWidth, rulesGroupBoxHeight - Padding);
    }
 
    Control ruleGroupBox = ruleSetDialog.Controls["ruleGroupBox"];
    if (ruleGroupBox != null)
    {
        ruleGroupBox.SetBounds(LeftMargin, rulesGroupBoxHeight + TopOffset, groupBoxWidth, ruleGroupBoxHeight - Padding);
        LayoutRuleGroupBox(ruleGroupBox);
    }
}

Finally, we recalculate the size and position of the if/then/else text boxes and corresponding labels, giving each approximately a 1/3 share of the available estate.

 private static void LayoutRuleGroupBox(Control ruleGroupBox)
{
   const int TopOffset = 80;
   const int Padding = 20;
   const int LeftMargin = 10;
   const int LabelHeight = 17;
 
   int textBoxHeight = (ruleGroupBox.Size.Height - TopOffset) / 3;
   int textBoxWidth = ruleGroupBox.Size.Width - 20;
 
   Control conditionTextBox = ruleGroupBox.Controls["conditionTextBox"];
   if (conditionTextBox != null)
   {
       conditionTextBox.SetBounds(LeftMargin, TopOffset, textBoxWidth, textBoxHeight - Padding);
   }
 
   Control conditionLabel = ruleGroupBox.Controls["conditionLabel"];
   if (conditionLabel != null)
   {
       conditionLabel.SetBounds(LeftMargin, TopOffset - LabelHeight, textBoxWidth, LabelHeight);
   }
 
   Control thenTextBox = ruleGroupBox.Controls["thenTextBox"];
   if (thenTextBox != null)
   {
       thenTextBox.SetBounds(LeftMargin, textBoxHeight + TopOffset, textBoxWidth, textBoxHeight - Padding);
   }
 
   Control thenLabel = ruleGroupBox.Controls["thenLabel"];
   if (thenLabel != null)
   {
       thenLabel.SetBounds(LeftMargin, textBoxHeight + TopOffset - LabelHeight, textBoxWidth, LabelHeight);
   }
 
   Control elseTextBox = ruleGroupBox.Controls["elseTextBox"];
   if (elseTextBox != null)
   {
       elseTextBox.SetBounds(LeftMargin, (textBoxHeight * 2) + TopOffset, textBoxWidth, textBoxHeight - Padding);
   }
 
   Control elseLabel = ruleGroupBox.Controls["elseLabel"];
   if (elseLabel != null)
   {
       elseLabel.SetBounds(LeftMargin, (textBoxHeight * 2) + TopOffset - LabelHeight, textBoxWidth, LabelHeight);
   }
}

That's it. You should be able to maximize or drag the resize handle to resize the dialog. Happy rules editing!

Written by Christopher Owczarek