Jump to content

VS Template with custom UI controls


Recommended Posts

A while back I was working on a effect plugin for PDN that was going well, until, I had to create my own dialog UI as IndirectUI wasn't up the the job -- basically, a complete lack of tabs. So I ended up having to make something for my needs and I decided to do so by having it build the dialog once the plugin is launched.


To make that clear: it doesn't need you to set up all the various control positions in VS, but instead, does something similar to how IndirectUI does it in that you create the control objects you want in the PluginToken class and then the PluginDialog class auto-builds the dialog UI for you.


Anyway, I decided what I would do is recreate the IndirectUI controls -- with some additional features -- along with what where the more "common" controls that developers were creating for their needs; the idea being to create a new template for developers to use so that they end up spending more time on creating effects and less time on creating a custom dialog UI for their effects.


With the plugin dialog base form UI there are a range of options you can customize:


  • Whether you want the version number to be added to the dialog name and how many digits,
  • Whether to show a checkbox toggle and what it's label is -- default is "Seamless".
  • Whether to show a toggle button and what the label is for it -- default is "Compare."
  • Also, whether to show a "DEBUG" tab for the dialog so that you can check certain states -- like where the control is "Exposed" and can be referenced via your effects script, where the current setting for controls are, positions, etc.

Here are the current controls completed (with pics):


  • UITab (container): currently you need to have at least one tab for the dialog to be displayed.
  • UITabMode: allows you to enable / disable a tab page's groups.
  • UIGroup (container): all controls need to be added to a group to be displayed.




  • UISlider: these can be customized so that the bar ticks cam be disabled and a checkbox added -- this can be set to enable the control or to be used in some other way, like if you wish to invert the slider value as an example.
  • UILink: this allows you to link to sliders so that one will update the other -- can be set 1/2 way and to mirror or reverse the value change.
  • UIReset: this lets you reset all the controls you've added to it.


  • UIText: a non-editable textbox control -- can be made editable however, as shown above.
  • UIString
  • UIImport: this allows you to import a file or image from clipboard; the buttons don't have any code -- you'd need to add that via your effect script -- but they can be renamed and disabled to your needs.


  • UIAngle: with this you can also enable an "altitude" option, the default is 0 to 100, as in percent -- you can set the max to whatever you like
  • UIDivider: this just draws a simple dividing ruler between two controls
  • UIHeader: this draws a dividing ruler along with allowing you to add a header -- like "Reset All"


  • UIColor: decided against the color wheel implementation and went with something else a bit more space friendly. Let's you drag a Hue (top bar) and set the Saturation and Luminance via the left square. You can also set the RGB values with the slider along with Alpha. Get / Set methods allow you to get the R, G, B, A values seperately, whereas the Value will return the current ARGB Color.




Controls on the to-do list:


  • UIVector
  • UIButton
  • UICheckbox
  • UIRadio
  • UIState -- for boolean controls like radio buttons where only one can be enable at a time
  • UINumeric
  • UIDropDown
  • UIDouble (container)
  • UIList (container)
  • UIButtonBar (container)


The PluginToken class for the current template:


using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;

namespace SICPDNfx
static internal class Alias
internal enum ControlName
Dummy, //use this if you don't need to keep track of control values -- FYI also used by at least one control as default

internal enum ControlType

public class PluginToken : PaintDotNet.Effects.EffectConfigToken
private DialogOptions dialogOptions = new DialogOptions();
public DialogOptions DialogOptions
get { return dialogOptions; }
set { this.dialogOptions = value; }

private Dictionary dialogControls = new Dictionary();
public Dictionary DialogControls
get { return dialogControls; }
set { this.dialogControls = value; }

private Dictionary dialogLinks = new Dictionary();
public Dictionary DialogLinks
get { return dialogLinks; }
set { this.dialogLinks = value; }

public PluginToken() : base()
// First example tab

UISlider slider1 = new UISlider(Alias.ControlName.Slider1, "XYZW Lightness", 0, 100, 50, false, 2);
UISlider slider2 = new UISlider(Alias.ControlName.Slider2, "XY Lightness", 0, 100, 50, false, 2);
slider2.ShowTicks = false;
UIHeader header1 = new UIHeader(Alias.ControlName.Header1, "Linked Sliders");
UISlider slider3 = new UISlider(Alias.ControlName.Slider3, "XY Lightness", 0, 100, 50, false, 0);
//UISlider slider4 = new UISlider(Alias.ControlName.Slider4, "Brightness", 0, 100, 50, false, 0);
UISlider slider5 = new UISlider(Alias.ControlName.Slider5, "Double Slider", 0, 85, 50, true, 0);
UILink link1 = new UILink(Alias.ControlName.Dummy, slider3, slider5, true, true, false);

object[] nGroupSampleControls = new object[] { slider1, slider2, header1, slider3, slider5 };

UISlider slider6 = new UISlider(Alias.ControlName.Slider6, "Enhance", 0, 100, 75, false, 1);
AddControl(Alias.ControlName.Slider6, slider6);

object[] nGroupTestControls = new object[] { slider6 };

UIReset reset3 = new UIReset(Alias.ControlName.Reset3);

object[] nGroupResetControls = new object[] { reset3 };

UIGroup nGroupSample = new UIGroup(Alias.ControlName.NGroupSample, "Sample Sliders", true, nGroupSampleControls);
AddControl(Alias.ControlName.NGroupSample, nGroupSample);
UIGroup nGroupTest = new UIGroup(Alias.ControlName.NGroupTest, "Test Slider", true, nGroupTestControls);
AddControl(Alias.ControlName.NGroupTest, nGroupTest);
UIGroup nGroupReset = new UIGroup(Alias.ControlName.NGroupReset, "Reset All", true, nGroupResetControls);
AddControl(Alias.ControlName.NGroupReset, nGroupReset);

UIGroup[] nGroups = new UIGroup[] { nGroupSample, nGroupTest, nGroupReset };

Dictionary nModeListNew = new Dictionary();
nModeListNew.Add("Sample Sliders", nGroupSample);
nModeListNew.Add("Test Slider", nGroupTest);
nModeListNew.Add("Enable All", null); // will enable or disable all depending upon "nullDisable" flag for UITabMode constructor

UITabMode tabMode1 = new UITabMode(Alias.ControlName.TabNMode, nModeListNew, "MODE", 0, false);
AddControl(Alias.ControlName.TabNMode, tabMode1);

// Second example tab

UIString string1 = new UIString(Alias.ControlName.String1, "String Label");
UIHeader header2 = new UIHeader(Alias.ControlName.Header2, "Test Heading");
UIText text1 = new UIText(Alias.ControlName.Text1, 60, false);
text1.ReadOnly = false;
UIDivider ruler1 = new UIDivider(Alias.ControlName.Ruler1);
UIImport file1 = new UIImport(Alias.ControlName.File1, true, true);
UIDivider ruler2 = new UIDivider(Alias.ControlName.Ruler2);
UIReset reset1 = new UIReset(Alias.ControlName.Reset1);
object[] sGroupStringControls = new object[] { string1, header2, text1, ruler1, file1, ruler2, reset1 };
UIGroup sGroupString = new UIGroup(Alias.ControlName.SGroupString, "String / File Samples", true, sGroupStringControls);
UIGroup[] sGroups = new UIGroup[] { sGroupString };

// Third example tab

UIAngle angle1 = new UIAngle(Alias.ControlName.Angle1, "Angle: gygygygygy", 90.0, false, String.Empty, Double.NaN);
UIDivider ruler3 = new UIDivider(Alias.ControlName.Ruler3);
UIAngle angle2 = new UIAngle(Alias.ControlName.Angle2, "Angle:", 90.0, true, "Altitude:", 0);
angle2.AltitudeMax = 100;
UIHeader header3 = new UIHeader(Alias.ControlName.Header3, "Reset All");
UIReset reset2 = new UIReset(Alias.ControlName.Reset2);
object[] aGroupAngleControls = new object[] { angle1, ruler3, angle2, header3, reset2 };
UIGroup aGroupAngle = new UIGroup(Alias.ControlName.AGroupAngle, "Angle Controls", true, aGroupAngleControls);
UIGroup[] angleGroups = new UIGroup[] { aGroupAngle };

// Fourth example tab

UIColor color1 = new UIColor(Alias.ControlName.Color1, Color.Empty);
UIDivider ruler4 = new UIDivider(Alias.ControlName.Ruler4);
UIReset reset4 = new UIReset(Alias.ControlName.Reset4);
object[] cGroupColorControls = new object[] { color1, ruler4, reset4 };
UIGroup cGroupColor = new UIGroup(Alias.ControlName.CGroupColor, "Color Control", true, cGroupColorControls);
UIGroup[] colorGroups = new UIGroup[] { cGroupColor };

// Add tabs to dialog options along with the rest

UITab tabSliders = new UITab("Sliders", tabMode1, nGroups);
UITab tabString = new UITab("String / File", null, sGroups);
UITab tabAngle = new UITab("Angle", null, angleGroups);
UITab tabColor = new UITab("Color", null, colorGroups);
UITab tabDebug = new UITab("DEBUG", null, null);
UITab[] dialogTabs = new UITab[] { tabSliders, tabString, tabAngle, tabColor, tabDebug };

DialogOptions.DialogLabel = "PDN Effect Plugin";
DialogOptions.DialogTabs = dialogTabs;
DialogOptions.ToggleShow = true;
DialogOptions.ButtonShow = true;
DialogOptions.DebugShow = true;
DialogOptions.VersionShow = true;
DialogOptions.VersionLength = 2;

protected PluginToken(PluginToken copyMe) : base(copyMe)
this.DialogControls = copyMe.dialogControls;
this.DialogLinks = copyMe.dialogLinks;
this.DialogOptions = copyMe.dialogOptions;

public override object Clone() { return new PluginToken(this); }

private void AddControl(Enum controlName, object control)
dialogControls.Add(controlName, control);

public object GetControl(Enum controlName)
if (DialogControls.ContainsKey(controlName))
object obj = DialogControls[controlName];
return obj;
else { return null; }

private void LinkControls(string controlName1, string controlName2)
dialogLinks.Add(controlName1, controlName2);
dialogLinks.Add(controlName2, controlName1);

public string GetLinked(string controlName)
if (DialogLinks.ContainsKey(controlName))
string linkedControl = DialogLinks[controlName];
return linkedControl;
else { return null; }


To use a control within your PluginEffect class, you do the following to get the reference to it:

UISlider slider6 = (UISlider)token.GetControl(Alias.ControlName.Slider6);

Once you have the reference, you are then able to call its property get and set methods -- like "slider6.Value = 25;" -- along with public methods. ALL of them.


You can download the test plugin here. It will appear in PDN under "_Debug" via the effects menu option. 


The template isn't complete yet, based on what feedback if any I get on the plugin version, I'll decide how much more work I'll do to it before making it available. Also, I have yet to test it via PDN 4.0.


FYI: debug has been enabled so there will be performance issues for the UIAngle and UIColor controls due to the debug textbox being updated. These controls are very smooth on my computer when debug is not enabled ;).

Edited by SIC

----- sELFiNDUCEDcOMA.com

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...