Nevron Open Vision Documentation
User Interface / Windows / Top Level Windows
In This Topic
    Top Level Windows
    In This Topic
     My First Top Level Window

    A top level window is represented by the NTopLevelWindow class, that derives from the NWindow class. A top level window is associated with a native for the integration platform top-level window and as such, helps you popup NOV content in a native for the integration platform way. You can use the NApplication.CreateTopLevelWindow static method to create top-level windows. Use the generic overloads of this method to create top level windows of specific type. The following code displays a top-level window with a single button, which when clicked closes the window.

    My First Top Level Window
    Copy Code
    ...
    // create a new top-level window
    NTopLevelWindow topLevelWindow = NApplication.CreateTopLevelWindow();
    
    // set title to the window
    topLevelWindow.Title = "My First Top Level Window";
    
    // make a button, which when clicked closes its owner window
    NButton button = new NButton("Close Me");
    button.Click += new Function<NEventArgs>(button_Click);
    
    // make the button the content of the top level window
    topLevelWindow.Content = button;
    
    // make the top level window show in the center of the screen
    topLevelWindow.StartPosition = ENWindowStartPosition.CenterScreen;
    
    // open the top level window
    topLevelWindow.Open();
    ...
    private void button_Click(NEventArgs arg1)
    {
        // get the button which issued the click
        NButton button = (NButton)arg1.TargetNode;
    
        // get the button owner window - in our case it is a top level window
        NTopLevelWindow topLevelWindow = (NTopLevelWindow)button.OwnerWindow;
    
        // close it
        topLevelWindow.Close();
    }
    
     Window Title Bar

    The top level window title bar is represented by an instance of the NTitleBar widget, an instance of which can be obtained from the TitleBar property. Most of the time you will control the window title bar indirectly throw the following properties of the NTopLevelWindow:

    Name Description
    ShowTitleBar Controls the visibility of the title-bar
    Title Controls the title bar text
    ShowControlBox Controls the visibility of the title-bar control box. The control box contains the minimize, maximize-restore and close buttons
    AllowMinimize Controls whether the maximize/restore operation is allowed from the user interface of the window
    AllowMaximize Controls whether the minimize operation is allowed from the user interface of the window

    The title bar itself exposes the following items and properties:

    Name Description
    NearItems A collection of widgets that precedes the title bar header. By default appears on the left side of the header.
    Header The title bar header.
    FarItems A collection of widgets that succeeds the title bar header. By default appears on the right side of the header.
    ControlBox Exposes the title bar control box, that contains the minimize, maximize/restore and close window buttons.
    Inverted If true, the title bar elements will flow from right to left (ControlBox on the left). By default false. Used for Mac Windows styling.
    In NOV the window title bar is just another widget. Therefore nothing special is required to override the appearance and behavior of the title bar. You should however know that the update of the respective properties that counterpart the window shortcut properties is performed by the NTopLevelWindow-UpdateTitleBar() method.
     Window Frame

    Top level windows use the box model margins area to display the window title bar and the window frame (the box model is explained in the Box Elements topic). The window frame is implemented as a border that is controlled by two properties of the NTopLevelWindow:

    FrameBorderThickness - controls the thickness of the window frame.

    FrameBorder - controls the appearance of the frame border.

    Both properties are marked as stylable and are usually specified via styling.

    See the Borders topic for more info.

     Window Lifetime

    To make a top-level window visible to the user you must explicitly open it by calling the Open() method. You can use the IsOpen property to determine whether the window is currently opened or not. The window will raise the Opened event whenever it is opened.

    In order to successfully open a NOV top level window, it must already be a descendant of the NApllication.Desktop element, prior to calling its Open() method.

    By default a top-level window is automatically activated when opened, which is controlled by the ActivateOnOpen property. You can use the IsActive property to determine whether the window is currently active or not. The window will raise the Activated/Deactivated events when it is activated/deactivated respectively. You can manually activate a window by calling its Activate() method. You cannot manually deactivate a window.

    Typically the title bar of the active window is displayed in a different way to highlight the fact that it is active. However in many circumstances this is an undesired effect. For example drop downs typically want to highlight the owner window of the active window. This is achieved by setting the ActivateOwnerFrameOnActivate property to true. Another common requirement is to actually activate the owner window when the window is closed, that is achieved by setting the ActivateOwnerOnClose property to true.

    Windows Managers typically associate the active top-level window with the window that has the keyboard focus. For the entire desktop there can be only one active window.

    Finally you can close a window by calling its Close() method, which is equivalent to the user pressing the Close button in the title bar. The window will raise a pair of events - Closing and Closed when the closing procedure is initiated by the user or by code. The Closing event is a cancelable event, which when canceled aborts the window closing procedure. The Closed event is raised only when the window was effectively closed.

     Initial Positioning

    The initial positioning of the window on the desktop is controlled by the StartPosition property that accepts the following values:

    • CenterScreen - the window is positioned in the center of the screen.
    • CenterWorkingArea - the window is positioned in the center of the desktop working area.
    • CenterOwner - the window is positioned in the center of its owner window.
    • Manual - the window is manually positioned.

    When the window is manually positioned, it will raise the QueryManualStartPosition event, giving you control over its exact bounds. The following code manually positions a window:

    Manual Positioning
    Copy Code
    ...
    // instruct the window to use manual positioning and subscribe for the QueryManualStartPosition event
    topLevelWindow.StartPosition = ENWindowStartPosition.Manual;
    topLevelWindow.QueryManualStartPosition += new Function<NEventArgs>(topLevelWindow_QueryManualStartPosition);
    ...
    
    private void topLevelWindow_QueryManualStartPosition(NEventArgs arg1)
    {
        // get the top level window which queries for position
        NTopLevelWindow topLevelWindow = (NTopLevelWindow)arg1.TargetNode;
     
        // set the top level window bounds (in DIPs)
        topLevelWindow.SetBounds(new Nevron.GraphicsCore.NRectangleF(10, 10, topLevelWindow.DefaultWidth, topLevelWindow.DefaultHeight));
    }
    
    The NTopLevelWindow indirectly derives from the NBoxElement, which participates in the document measure and layout system. That is why its DefaultWidth and DefaultHeight indicate the natural size that it wants to have. The CenterScreen, CenterWorkingArea and CenterOwner initial positioning modes are all respecting this natural size.
     Resizable Windows

    By default top-level windows are not resizable. However you can allow users to resize the window both X and Y dimensions by setting the AllowXResize and/or AllowYResize properties to true.

    Users perform window resize by clicking on the rim of the window resize area, and dragging the mouse. The size of the resize area is controlled by the ResizeMargins property. Typically the resize margins are set through styling to match the size of the window frame.

    Another common requirement for windows is to make them auto sizable. In the Initial Positioning section, we have already mentioned that by default when a window is displayed it is sized to its default size - the size that the window desires to occupy provided that it has an unlimited arrange area (explained in the Box Elements topic). Because the window does not have a layout parent, the size of the window remains fixed after it is opened.

    If you allow for the window to be resized in the X or Y dimension, you may want to auto size the window in the opposite direction. Consider a X resizable window with a wrap panel inside - by default when displayed, the wrap flow panel will display its items in a single lane, because the window is displayed in its default size. If the user decreases the size of the window in the X dimension, the wrap flow panel will need another lane and therefore the window desired size will grow in the Y dimension. Now because the window does not have a layout parent, which to observe the change in its desired size, by default the window Height will remain unchanged, causing the wrap flow panel to be clipped. The solution is to bind the window Height to the window DesiredHeight via expressions (see Expressions for more info):

    Auto Y Resizable Window
    Copy Code
    NTopLevelWindow window = new NTopLevelWindow();
    // allow X resize to be performed by the user
    window.AllowXResize = true;
    // bind the window Height to its DesiredHeight.
    NBindingFx bindingFx = new NBindingFx(window, NTopLevelWindow.DesiredHeightProperty);
    bindingFx.Guard = true;
    window.SetFx(NTopLevelWindow.HeightProperty, bindingFx);
    

    The approach for a Y resizable window that auto sizes itself in the Y dimension is to respectively set AllowYResize to true and bind the Width to the DesiredWidth.

    There may be cases when may simply want the window to auto grow/shrink itself – i.e. always size to its default size. You do that by binding the Width to the DefaultWidth and the Height to the DefaultHeight.

    Now if you want to implement an auto center with auto size window, you will need some more expressions, like shown in the following example:

    Auto Sizable and Auto Centered Window
    Copy Code
    NTopLevelWindow window = new NTopLevelWindow();
    // open the window in the center of its parent,
    window.StartPosition = ENWindowStartPosition.CenterOwner;
    // implement auto width and height sizing
    {
     // bind the window Width to the DefaultWidth of the window
     NBindingFx widthBindingFx = new NBindingFx(window, NTopLevelWindow.DefaultWidthProperty);
     widthBindingFx.Guard = true;
     window.SetFx(NTopLevelWindow.WidthProperty, widthBindingFx);
     // bind the window Height to the DefaultHeight of the window
     NBindingFx heightBindingFx = new NBindingFx(window, NTopLevelWindow.DesiredHeightProperty);
     heightBindingFx.Guard = true;
     window.SetFx(NTopLevelWindow.HeightProperty, heightBindingFx);
    }
    // implement auto center
    {
     // scratch X and Y define the window center
     // they are implemented by simply calculating the center X and Y via formulas
     window.SetFx(NScratchPropertyEx.XPropertyEx, "X+Width/2");
     window.SetFx(NScratchPropertyEx.YPropertyEx, "Y+Height/2");
     // now that we have an automatic center, we need to write expressions that define the X and Y from that center.
     // These are cyclic expressions - CenterX depends on X, and X depends on CenterX.
     // The expressions that are assigned to X and Y are guarded and permeable.
     //    guard is needed because X and Y are updated when the user moves the window around.
     //    permeable is needed to allow the X and Y values to change when the user moves the window around.
     // When the the X and Y values change -> center changes -> X and Y expressions are triggered but they produce the same X and Y results and the cycle ends.
     // When the Width and Height change -> center changes -> X and Y expression are triggered but they produce the same X and Y results and the cycle ends.
     NFormulaFx xfx = new NFormulaFx(NScratchPropertyEx.XPropertyEx.Name + "-Width/2");
     xfx.Guard = true;
     xfx.Permeable = true;
     window.SetFx(NTopLevelWindow.XProperty, xfx);
     NFormulaFx yfx = new NFormulaFx(NScratchPropertyEx.YPropertyEx.Name + "-Height/2");
     yfx.Guard = true;
     yfx.Permeable = true;
     window.SetFx(NTopLevelWindow.YProperty, yfx);
    }
    

     

     Modal Windows

    Often it is required to display top-level windows with modal behavior (typical example being dialogs). You can do that by setting the Modal property to true prior to opening the window. The following example shows a modal dialog, and inspects window result after it has been closed.

    Example Title
    Copy Code
    ...
    // make left-to-right stack as content of the window
    NStackPanel hstack = new NStackPanel();
    hstack.Direction = Nevron.Layout.ENHVDirection.LeftToRight;
    topLevelWindow.Content = hstack;
    
    // place two button - OK and Cancel which when clicked change the window result
    NButton okButton = new NButton("OK");
    okButton.WindowResult = ENWindowResult.OK;
    okButton.Click += new Function<NEventArgs>(okCancelButton_Click);
    hstack.AddChild(okButton);
    
    NButton cancelButton = new NButton("Cancel");
    cancelButton.WindowResult = ENWindowResult.Cancel;
    cancelButton.Click += new Function<NEventArgs>(okCancelButton_Click);
    hstack.AddChild(cancelButton);
    
    // subscribe for window closed event
    topLevelWindow.Closed += new Function<NEventArgs>(topLevelWindow_Closed);
    
    // instruct the top level window to be opened as modal
    topLevelWindow.Modal = true;
    
    // open the top level window
    topLevelWindow.Open();
    ...
    
    private void okCancelButton_Click(NEventArgs arg1)
    {
        // get the button which issued the click
        NButton button = (NButton)arg1.TargetNode;
    
        // get the button owner window - in our case it is a top level window
        NTopLevelWindow topLevelWindow = (NTopLevelWindow)button.OwnerWindow;
    
        // close it
        topLevelWindow.Close();
    }
    
    private void topLevelWindow_Closed(NEventArgs arg1)
    {
        // get the top level window which was closed
        NTopLevelWindow topLevelWindow = (NTopLevelWindow)arg1.TargetNode;
    
        if (topLevelWindow.Result == ENWindowResult.OK)
        {
            // OK button was clicked
        }
        else if (topLevelWindow.Result == ENWindowResult.Cancel)
        {
            // Cancel button was clicked
        }
    }
    

    It is important to know that the modal behavior of NOV top level windows is not identical to the modal behavior of the WinForms-Form or WPF-Window class.

    Modal windows in terms of traditional MS Windows programming are such windows, which when displayed place a modal block on all currently opened top-level windows and return from the opening method only after the modal window has been closed. The second part (blocking the caller) is in great contradiction with event driven programming and even with the native MS Windows - Windows Manager, which of course does not provide such functionality. The implementation involves running a new message pump to do the Windows messages dispatching, which is a dirty hack, the behavior of which is impossible to be replicated on Windowing Systems, that do not expose control over the message queue.

    To be event driven from start to end, NOV modal windows are always performing only the first part of the traditional Windows modal dialog - place a modal block on all currently opened windows, that is lifted only after the window has been closed. This achieves the same modal behavior as far as the end user is concerned, but never directly blocks the caller on open - meaning NOV modal windows can be shown in Windowing Systems without a message queue (or where it is not accessible).

    The same architectural principle applies to all NOV features, that are backed by platform specific implementations that typically "block the caller" (such as MS Windows Drag and Drop, Common Dialogs etc.). NOV is always event driven and never blocks the caller.

    See Also