Hide

YetaWF Documentation

Display
Print

Property Lists

Any form uses the same format, a label followed by an input field (or DropDownList or other component), repeated over and over. Normally these labels and components appear in table form, but can be restyled using CSS.

YetaWF is data driven. This means that you don't "program" forms in any way. You provide a model that is then rendered as a form (using the PropertyList component).

Here is an example of a form, from the UserSettings package, SettingsEdit.cs:

[Trim]
public class EditModel {

    [UIHint("Hidden")]
    public int Key { get; set; }

    [Caption("Time Zone"), Description("Your time zone - all dates/times within this web site will be adjusted for the specified time zone")]
    [UIHint("TimeZone"), StringLength(UserData.MaxTimeZone), Required]
    public string TimeZone { get; set; }

    [Caption("Date Format"), Description("The desired date format when dates are displayed on this website")]
    [UIHint("Enum"), Required]
    public Formatting.DateFormatEnum DateFormat { get; set; }

    [Caption("Time Format"), Description("The desired time format when times are displayed on this website")]
    [UIHint("Enum"), Required]
    public Formatting.TimeFormatEnum TimeFormat { get; set; }

    [Caption("Grid Actions"), Description("The desired display method for available actions in grids")]
    [UIHint("Enum")]
    public Grid.GridActionsEnum GridActions { get; set; }

    [Caption("Language"), Description("The default language used for the entire site (only used when localization is enabled)")]
    [UIHint("LanguageId"), StringLength(LanguageData.MaxId)]
    public string LanguageId { get; set; }

    [Caption("Show Search Toolbar"), Description("Defines whether the search toolbar is always shown on grids - If not shown, it can still be accessed using the search button in each grid, at the bottom of the grid, next to the refresh button")]
    [UIHint("Boolean")]
    public bool ShowGridSearchToolbar { get; set; }

    [Caption("Show Page Ownership"), Description("Defines whether pages that can't be seen by anonymous users or regular users are shown with special background colors - Requires a skin that supports ownership display")]
    [UIHint("Boolean"), SuppressIfEqual("ShowDevInfo", false)]
    public bool ShowPageOwnership { get; set; }

    [Caption("Show Module Ownership"), Description("Defines whether modules that can't be seen by anonymous users or regular users are shown with special background colors - Requires a skin that supports ownership display")]
    [UIHint("Boolean"), SuppressIfEqual("ShowDevInfo", false)]
    public bool ShowModuleOwnership { get; set; }

    [Caption("Show Enum Values"), Description("Defines whether enumerated values (in dropdown lists) show their numeric value. Numeric values are typically only useful for programming purposes")]
    [UIHint("Boolean"), SuppressIfEqual("ShowDevInfo", false)]
    public bool ShowEnumValue { get; set; }

    [Caption("Show Variables"), Description("Defines whether variable names are shown for properties and all available variables are listed on property pages. Variables are used for variable substitution in modules and pages and of course for programming purposes")]
    [UIHint("Boolean"), SuppressIfEqual("ShowDevInfo", false)]
    public bool ShowVariables { get; set; }

    [Caption("Show Internal Data"), Description("Defines whether internal information is shown (e.g., ids)")]
    [UIHint("Boolean"), SuppressIfEqual("ShowDevInfo", false)]
    public bool ShowInternals { get; set; }

    [Caption("Confirm Delete"), Description("Defines whether delete actions must be confirmed before items are deleted")]
    [UIHint("Boolean")]
    public bool ConfirmDelete { get; set; }

    [Caption("Confirm Actions"), Description("Defines whether actions must be confirmed before they are executed. This is normally used for actions that need a prompt but are not destructive (delete) in nature")]
    [UIHint("Boolean")]
    public bool ConfirmActions { get; set; }

    public bool ShowDevInfo { get; set; }

    public EditModel() 
}

When the view is rendered, the model turns into the User Settings form:

Skins Solution Folder

The labels show a caption and a tooltip (when the user hovers over the label). These are derived from the Caption attribute (see YetaWF.Core.Models.Attributes.CaptionAttribute). The tooltip is derived from the Description attribute (see YetaWF.Core.Models.Attributes.DescriptionAttribute). Below is the label definition for the Time Zone field (repeated from the example above). That's all that's needed to render a label for a field (really a <label> tag).

[Caption("Time Zone"), Description("Your time zone - all dates/times within this web site will be adjusted for the specified time zone")]

The dropdownlist for timezone selection is rendered by the UIHint attribute (see YetaWF.Core.Models.Attributes.UIHintAttribute). In this example, the TimeZone component is used.

[UIHint("TimeZone"), StringLength(UserData.MaxTimeZone), Required]

The StringLength attribute (see YetaWF.Core.Models.Attributes.StringLengthAttribute) defines the maximum string length acceptable as input. The Required attribute (see YetaWF.Core.Models.Attributes.RequiredAttribute) defines that this input field is required. If it is not provided, an error message is automatically shown. Many components implement client-side validation, so the Required attribute would force client-side validation.

There are additional attributes that control rendering of the form. The SuppressIfEqual attribute can be used to suppress display of a property if another property in the model has a specific value. In the example below, the ShowVariables field is only shown if the ShowDevInfo property is equal to true (which means it's suppressed if it's equal to false).

[Caption("Show Variables"), Description("Defines whether variable names are shown for properties and all available variables are listed on property pages. Variables are used for variable substitution in modules and pages and of course for programming purposes")]
[UIHint("Boolean"), SuppressIfEqual(nameof(ShowDevInfo), false)]
public bool ShowVariables { get; set; }

Conditional Processing

Often certain fields should only be shown under certain conditions. For example if a check box is selected or if a dropdownlist has a certain value. The ProcessIf attribute (see YetaWF.Core.Models.Attributes.ProcessIfAttribute) is used for just this purpose. It controls both client-side and server side whether a field is shown.

The example below (from the Blog package, DisqusConfig.cs) shows how ProcessIf and RequiredIf can be used.

The GravatarDefault, GravatarRating, GravatarSize fields are only shown (processed) if the AvatarType property is equal to DisqusConfigData.AvatarTypeEnum.Gravatar. The AvatarType property is represented by a dropdownlist rendering the enum values for DisqusConfigData.AvatarTypeEnum. Whenever the user changes the dropdownlist of the AvatarType property, the GravatarDefault, GravatarRating, GravatarSize fields are shown/hidden as needed.

[Trim]
public class Model {

    [Caption("Shortname"), Description("The Shortname you assigned to your site (at Disqus) - If omitted, Disqus comments are not available")]
    [UIHint("Text40"), StringLength(DisqusConfigData.MaxShortName), ShortNameValidation, Trim]
    [HelpLink("https://disqus.com/admin/settings/general/")]
    public string ShortName { get; set; }

    [Caption("Single Sign On"), Description("Defines whether SSO (Single Sign On) is enabled for your site allowing users to log in using their credentials")]
    [UIHint("Boolean")]
    public bool UseSSO { get; set; }

    [Caption("Secret Key"), Description("Defines the Secret Key used for SSO (Single Sign On) - The Secret Key is created on the Disqus site when defining the SSO application")]
    [UIHint("Text80"), StringLength(DisqusConfigData.MaxPublicKey), RequiredIf(nameof(UseSSO), true), Trim]
    [ExcludeDemoMode]
    public string PrivateKey { get; set; }
    [Caption("Public Key"), Description("Defines the Public Key used for SSO (Single Sign On) - The Public Key is created on the Disqus site when defining the SSO application")]
    [UIHint("Text80"), StringLength(DisqusConfigData.MaxPrivateKey), RequiredIf(nameof(UseSSO), true), Trim]
    [ExcludeDemoMode]
    public string PublicKey { get; set; }

    [Caption("Login Url"), Description("Defines the Url used when the user wants to log into the site to leave a comment (using SSO)")]
    [UIHint("Url"), AdditionalMetadata("UrlType", UrlTypeEnum.Local | UrlTypeEnum.Remote), UrlValidation(UrlValidationAttribute.SchemaEnum.Any, UrlTypeEnum.Local), StringLength(Globals.MaxUrl), Trim]
    [RequiredIf(nameof(UseSSO), true)]
    public string LoginUrl { get; set; }

    [Caption("Login Popup Width"), Description("Defines the width of the popup window created by Disqus to log the user into the site (using SSO)")]
    [UIHint("IntValue4"), Range(20, 9999), RequiredIf(nameof(UseSSO), true)]
    public int Width { get; set; }
    [Caption("Login Popup Height"), Description("Defines the height of the popup window created by Disqus to log the user into the site (using SSO)")]
    [UIHint("IntValue4"), Range(20, 9999), RequiredIf(nameof(UseSSO), true)]
    public int Height { get; set; }

    [Caption("Avatar Type"), Description("Defines the source for user avatars (using SSO)")]
    [UIHint("Enum")]
    public DisqusConfigData.AvatarTypeEnum AvatarType { get; set; }

    [Caption("Gravatar Default"), Description("Defines the default Gravatar image for visitors leaving comments (used when users have not defined an image at http://www.gravatar.com)")]
    [UIHint("Enum"), RequiredIf("GravatarDefault", true)]
    [ProcessIf(nameof(AvatarType), DisqusConfigData.AvatarTypeEnum.Gravatar)]
    public Gravatar.GravatarEnum GravatarDefault { get; set; }

    [Caption("Allowed Gravatar Rating"), Description("Defines the acceptable Gravatar rating for displayed Gravatar images")]
    [UIHint("Enum")]
    [ProcessIf(nameof(AvatarType), DisqusConfigData.AvatarTypeEnum.Gravatar)]
    public Gravatar.GravatarRatingEnum GravatarRating { get; set; }

    [Caption("Gravatar Size"), Description("The width and height of the displayed Gravatar images (in pixels)")]
    [UIHint("IntValue4"), Range(16, 100)]
    [ProcessIf(nameof(AvatarType), DisqusConfigData.AvatarTypeEnum.Gravatar)]
    public int GravatarSize { get; set; }

    public Model() 
}

It is possible to have multiple dependent properties or chains of properties, which is fully supported.

Comparing To Another Property

The ProcessIf attribute (and its siblings) compare a named property to one or more specific values given as parameters to the attribute. It is also possible to compare a named property to another property in the same model, as long as both properties have the same type, using the YetaWF.Core.Models.Attributes.ProcessIfBase.ValueOf field.

[ProcessIf(nameof(FirstProperty), ProcessIfBase.ValueOf + nameof(OtherProperty)]
public string SomeProperty { get; set; }

In the above example, the property SomeProperty is only processed (shown) if the value of the property FirstProperty is the same as the property OtherProperty. All properties must be located in the same model.

In the next example we're comparing the property FirstProperty to two other properties and to an explicit value. Only if one of the comparisons succeeds will the property SomeProperty be shown.

[ProcessIf(nameof(FirstProperty), ProcessIfBase.ValueOf + nameof(SecondProperty), ProcessIfBase.ValueOf + nameof(ThirdProperty), 99]
public string SomeProperty { get; set; }

...
public int FirstProperty { get; set; }
...
public int SecondProperty { get; set; }
...
public int ThirdProperty { get; set; }

Processing Attributes

There are a number of sibling attributes that work just like the ProcessIf attribute but implement different comparison logic:

AttributeLinkDescription
ProcessIfYetaWF.Core.Models.Attributes.ProcessIfAttributeProcess (show) a field based on whether other property values are equal.
ProcessIfNotYetaWF.Core.Models.Attributes.ProcessIfNotAttributeProcess (show) a field based on whether other property values are not equal.
ProcessIfSuppliedYetaWF.Core.Models.Attributes.ProcessIfSuppliedAttributeProcess (show) a field based on whether another property is supplied, i.e., is not null or 0.
ProcessIfNotSuppliedYetaWF.Core.Models.Attributes.ProcessIfNotSuppliedAttributeProcess (show) a field based on whether another property is not supplied, i.e., is null or 0.
HideIfNotSuppliedYetaWF.Core.Models.Attributes.HideIfNotSuppliedAttributeHides a field based on whether another property is not supplied, i.e., is null or 0.
SuppressIfEqualYetaWF.Core.Models.Attributes.SuppressIfEqualAttributeHides a field based on whether other property values are equal. This prevents rendering the field server side and is typically used for properties that have a null value to prevent cluttering the form with fields that have no values.
SuppressIfNotEqualYetaWF.Core.Models.Attributes.SuppressIfNotEqualAttributeHides a field based on whether other property values are not equal. This prevents rendering the field server side and is typically used for properties that have a null value to prevent cluttering the form with fields that have no values.

Required Fields (Conditional)

The RequiredIf attribute (see YetaWF.Core.Models.Attributes.RequiredIfAttribute) is used to define that properties are only required if another property has a specific value.

RequiredIf(nameof(UseSSO), true)

In the above example, all properties using the RequiredIf attribute are required only if the UseSSO property is set to true. Otherwise they are not required.

Comparing To Another Property

The RequiredIf attribute (and its siblings) compare a named property to one or more specific values given as parameters to the attribute. It is also possible to compare a named property to another property in the same model, as long as both properties have the same type, using the YetaWF.Core.Models.Attributes.ProcessIfBase.ValueOf field.

[RequiredIf(nameof(FirstProperty), ProcessIfBase.ValueOf + nameof(OtherProperty)]
public string SomeProperty { get; set; }

In the above example, the property SomeProperty is only required if the value of the property FirstProperty is the same as the property OtherProperty. All properties must be located in the same model.

In the next example we're comparing the property FirstProperty to two other properties and to an explicit value. Only if one of the comparisons succeeds will the property SomeProperty be required.

[ProcessIf(nameof(FirstProperty), RequiredIfBase.ValueOf + nameof(SecondProperty), RequiredIfBase.ValueOf + nameof(ThirdProperty), 99]
public string SomeProperty { get; set; }

...
public int FirstProperty { get; set; }
...
public int SecondProperty { get; set; }
...
public int ThirdProperty { get; set; }

Required Attributes

There are a number of sibling attributes that work just like the RequiredIf attribute but implement different comparison logic:

AttributeLinkDescription
RequiredYetaWF.Core.Models.Attributes.RequiredAttributeA field is (always) required. No other properties are inspected.
SelectionRequiredYetaWF.Core.Models.Attributes.SelectionRequiredAttributeA selection in a dropdownlist is required. No other properties are inspected.
RequiredIfYetaWF.Core.Models.Attributes.RequiredIfAttributeA field is required based on whether other property values are equal.
RequiredIfNotYetaWF.Core.Models.Attributes.RequiredIfNotAttributeA field is required based on whether other property values are not equal.
RequiredIfInRangeYetaWF.Core.Models.Attributes.RequiredIfInRangeAttributeA field is required based on whether another property is within the specified starting/ending range.
RequiredIfSuppliedYetaWF.Core.Models.Attributes.RequiredIfSuppliedAttributeA field is required based on whether another property is supplied, i.e., is not null or 0.
SameAsYetaWF.Core.Models.Attributes.SameAsAttributeA field is required to have the same value as another property.

Tabbed Forms

A tabbed form is easily implemented by adding the Category attribute to each property (see YetaWF.Core.Models.Attributes.CategoryAttribute). For each unique category name, a new tab is added. No other action is required for tabbed forms.

Even if the Category attribute is used, if a form only has one named category, the form will not be tabbed. At least 2 distinct category names are required for a tabbed forms. This allows the use of the Category attribute on all fields. YetaWF will automatically select tabbed or non-tabbed form rendering as needed.

[Category("Blog"), Caption("Blog Entry Url"), Description("URL to display a blog entry with comments")]
[UIHint("Url"), AdditionalMetadata("UrlType", UrlTypeEnum.Local), UrlValidation(UrlValidationAttribute.SchemaEnum.Any, UrlTypeEnum.Local)]
[StringLength(Globals.MaxUrl), Required, Trim]
public string BlogEntryUrl { get; set; }

[Category("Comments"), Caption("Show Gravatar"), Description("Defines whether Gravatar images are shown for visitors leaving comments")]
[HelpLink("http://www.gravatar.com")]
[UIHint("Boolean"), Trim]
public bool ShowGravatar { get; set; }