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