User Interface / Widgets / Widget Elements
In This Topic
    Widget Elements
    In This Topic
     Widgets Overview

    Widgets are represented by the NWidget class, that derives from the NBoxElement class. The NWidget class serves as base class for all NOV UI elements, that need to reside in windows (e.g. buttons, check boxes, combo boxes, tree views, etc.).

    Widgets are box elements and as such inherit the box model and measure and arrange functionality of box elements (see Box Elements for more info). Widgets however extend the features of the box elements with the following ones:

    Margins - the box model, defined by the NBoxElement class is extended to support user controllable margins.

    Arrange Slot and Arrange Slot Placement - the box measure and arrange of box elements is extended to support the notion of an arrange slot. The placement of the widget inside the arrange slot is user controllable.

    - Visibility - widgets also have controllable visibility.

    The rest of this topic discusses these widget features.

     Margins
    The margins of a widget are controlled by its Margins property. Respectively the NBoxElement-GetMargins() method is overriden by widgets to return the value of the Margins property. The margins property can be specified trough styling.
     Arrange Slot and Arrange Slot Placement

    Since widgets are always residing in another box element (window or another widget), that parent box element is responsible for final arrangement of the widget. The parent box element positions its child widget(s) by setting their ArrangeSlot property, the value of which is a rectangle in parent coordinates that is considered to be the widget arrange slot. The arrange slot of a widget has the meaning of area available for arrange.

    The arrange phase of widgets is modified to first perform the arrangement of the widget inside its arrange slot, which is known as the widget slot arrange. The purpose of the widget slot arrange is to position the widget inside the provided arrange slot. During the slot arrange, the widget determines its X and Y coordinates as well as its Width and Height. If the Width and/or Height of the widget change, the widget also performs the default box content arrangement, by calling the ArrangeContent method.

    The placement of the widget inside the arrange slot is affected by the widget HorizontalPlacement and VerticalPlacement properties that take values from the ENHorizontalPlacement and ENVerticalPlacement enumerations respectively. The following tables describes the possible values of the HorizontalPlacement and VerticalPlacement properties and their effect on the widget location and size:

    HorizontalPlacement

    Value Description
    Left The widget is horizontally aligned to the left side of the slot. The widget Width is set to the smaller value of DesiredWidth and ArrangeSlot.Width.
    Center The widget is horizontally centered in the slot.  The widget Width is set to the smaller value of DesiredWidth and ArrangeSlot.Width.
    Right The widget is horizontally aligned to the right side of the slot.  The widget Width is set to the smaller value of DesiredWidth and ArrangeSlot.Width.
    Fit The widget is resized to occupy the whole slot horizontally (default).

    VerticalPlacement

    Value Description
    Top The widget is vertically aligned to the top side of the slot. The widget Height is set to the smaller value of DesiredHeight and ArrangeSlot.Height.
    Center The widget is vertically centered in the slot. The widget Height is set to the smaller value of DesiredHeight and ArrangeSlot.Height.
    Bottom The widget is vertically aligned to the bottom side of the slot. The widget Height is set to the smaller value of DesiredHeight and ArrangeSlot.Height.
    Fit The widget is resized to occupy the whole slot vertically (default).

    The Width and Height of the widget are determined in such a way that they never exceed the size of the arrange slot.

    Certain widgets need to take into account the arrange slot in their desired size measure. In order to do that they need to override the OnArrangeSlotChanged protected virtual method and mark the widget desired size as invalid. A typical example is a wrap flow panel. The following sample demonstrates a simple wrap flow panel implementation, that takes into account the arrange slot in its desired size measure:

    My Wrap Layout Widget
    Copy Code
    public class MyWrapLayoutWidget : NCompositeWidget<NWidget>
    {
        public MyWrapLayoutWidget() { }
        static MyWrapLayoutWidget()
        {
            MyWrapLayoutWidgetSchema = NSchema.Create(typeof(MyWrapLayoutWidget), NCompositeWidget<NWidget>.NCompositeWidgetSchema);
        }
        protected override NSize MeasureContent(bool defaultMeasure)
        {
            // determine available size. In case of default measure -> use an infinitely larget area, otherwise use arrange slot size, deflated with insets
            NSize availableSize;
            if (defaultMeasure)
            {
                availableSize = new NSize(Double.PositiveInfinity, Double.PositiveInfinity);
            }
            else
            {
                availableSize = ArrangeSlot.Size;
                availableSize.Deflate(GetInsets());
            }
            // distribute widgets to lanes
            NList<NList<NWidget>> lanes = DistributeCells(availableSize, defaultMeasure);
            // measure the lanes
            double width = 0;
            double height = 0;
            for (int i = 0; i < lanes.Count; i++)
            {
                NSize laneSize = MeasureLane(lanes[i], defaultMeasure);
                width = Math.Max(width, laneSize.Width);
                height += laneSize.Height;
            }
            return new NSize(width, height);
        }
        protected override void ArrangeContent(NRectangle contentArea)
        {
            // distribute widgets to lanes
            NList<NList<NWidget>> lanes = DistributeCells(contentArea.Size, false);
            // arrange the lanes
            double curY = contentArea.Y;
            for (int i = 0; i < lanes.Count; i++)
            {
                // measure the lane
                NList<NWidget> curLane = lanes[i];
                NSize curLaneSize = MeasureLane(curLane, false);
                // arrange the lane
                double curX = contentArea.X;
                for (int j = 0; j < curLane.Count; j++)
                {
                    NWidget curWidget = curLane[j];
                    NRectangle curSlot = new NRectangle(curX, curY, curWidget.DesiredWidth, curLaneSize.Height);
                    curWidget.ArrangeSlot = curSlot;
                    curX += curSlot.Width;
                }
                // increase cur Y
                curY += curLaneSize.Height;
            }
        }
        protected override void OnArrangeSlotChanged(NValueChangeData data)
        {
            // invalidate the box desired size measure, if the arrange slot has changed.
            if (ArrangeSlot.Width != ((NRectangle)data.OldValue).Width)
            {
                InvalidateMeasure(NBoxElement.InvalidateMeasureHint_DesiredSizeOnly);
            }
        }
        private NList<NList<NWidget>> DistributeCells(NSize layoutSize, bool defaultMeasure)
        {
            // start with one lane
            NList<NList<NWidget>> lanes = new NList<NList<NWidget>>();
            NList<NWidget> curLane = new NList<NWidget>();
            double curX = 0;
            for (int i = 0; i < Count; i++)
            {
                NWidget curWidget = this[i];
                NSize curWidgetSize = curWidget.GetMeasure(defaultMeasure);
                // get the current right side
                double curRight = curX + curWidgetSize.Width;
                // start a new lane if it breaks the contraint
                if (curLane.Count != 0 && (curRight > layoutSize.Width))
                {
                    // add cur lane, and make a new one
                    lanes.Add(curLane);
                    curLane = new NList<NWidget>();
                    curX = 0;
                }
                // add the cell to the cur lane
                curLane.Add(curWidget);
                curX += curWidgetSize.Width;
            }
            // add the last lane
            lanes.Add(curLane);
            return lanes;
        }
        private NSize MeasureLane(NList<NWidget> lane, bool defaultMeasure)
        {
            double width = 0;
            double height = 0;
            for (int i = 0; i < lane.Count; i++)
            {
                NWidget curWidget = lane[i];
                NSize curWidgetSize = curWidget.GetMeasure(defaultMeasure);
                // lane width is sum of children widths
                // lane height is max of children height
                width += curWidgetSize.Width;
                height = Math.Max(height, curWidgetSize.Height);
            }
            return new NSize(width, height);
        }
    
        public static readonly NSchema MyWrapLayoutWidgetSchema;
    }
    
     Visibiltiy

    The visibility of widgets can be controlled by their Visibility property, that takes values from the ENVisibility enumeration. The following tables describes the possible values and their effect on the widget:

    Value Description
    Visible The widget is visible and occupies layout space (default)
    Hidden The widget is not visible, but occupies the same layout space, as if it was visible.
    Collapsed The widget is not visible and does not occupy layout space.

    When a widget visibility it set to Collapsed, its default and desired size are automatically measured to be zero.
     Creating Widgets from Objects

    The NWidget.FromObject(object obj) method creates a widget from a given object depending on the type of the specified object, as shown by the following table:

    Object Type Widget Created
    Widget The provided widget is returned
    NImage An image box is created and returned
    Other A label with text equal to obj.ToString() is created and returned

    See Also