Table of Contents

Get Started With Toolbars

This tutorial shows how to use the Eremex Toolbars library to create a toolbar UI from scratch. It introduces controls to implement the toolbar UI, and demonstrates main toolbar settings.

toolbar-ui-tutorial

The tutorial creates a toolbar UI for two text editors placed in the center of the window. The toolbar UI consists of the main menu, status bar, and regular toolbars that display various items: buttons, check buttons, in-place editors, sub-menus, and text items.

All but one of the toolbars are docked to the edges of the window. These toolbars have commands that work with the first text editor. One toolbar (standalone toolbar) is placed between the text editors. It provides commands for the second text editor.

The tutorial also shows how to associate a text editor with a context menu from the Toolbars&Menu library.

1. Create a ToolbarManager Component

Start by defining a ToolbarManager component in XAML.

xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

<mxb:ToolbarManager IsWindowManager="True">
    <Grid RowDefinitions="Auto, *, Auto, *, Auto" ColumnDefinitions="Auto, *, Auto">
        <TextBox Grid.Row="1" Grid.Column="1" x:Name="textBox1"  
         Text="Text Editor" AcceptsReturn="True"/>
        <TextBox x:Name="textBox2"  Grid.Row="3" Grid.Column="1" 
         Text="Text Editor #2" AcceptsReturn="True"/>
    </Grid>
</mxb:ToolbarManager>            

ToolbarManager is the main component that manages toolbars, context menus, and menu items. The component processes keyboard shortcuts, invokes commands associated with corresponding items, maintains toolbar runtime customization, and performs bar UI serialization and deserialization.

The ToolbarManager component should wrap the client control (controls) for which a toolbar UI is created.

2. Create Toolbar Containers

To allow a toolbar to be docked at a specific position in a window/UserControl, first create a toolbar container (ToolbarContainerControl). A toolbar container is a control that displays toolbars in the docked state, and maintains toolbar drag-and-drop operations.

In XAML, create four toolbar containers (ToolbarContainerControl objects) along the top, bottom, left and right edges of the window. You will then be able to dock toolbars at these positions.

toolbars-get-started-empty=toolbarcontainers

xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"

<mxb:ToolbarManager IsWindowManager="True">
    <Grid RowDefinitions="Auto, *, Auto, *, Auto" ColumnDefinitions="Auto, *, Auto">
        <mxb:ToolbarContainerControl DockType="Top" Grid.ColumnSpan="3"/>

        <mxb:ToolbarContainerControl DockType="Left" Grid.Row="1" 
         Grid.Column="0" Grid.RowSpan="3" />

        <TextBox Grid.Row="1" Grid.Column="1" x:Name="textBox1"  
         Text="Text Editor" AcceptsReturn="True"/>
        <TextBox x:Name="textBox2"  Grid.Row="3" Grid.Column="1" 
         Text="Text Editor #2" AcceptsReturn="True"/>

        <mxb:ToolbarContainerControl DockType="Right" Grid.Row="1" 
         Grid.Column="2" Grid.RowSpan="3"/>

        <mxb:ToolbarContainerControl DockType="Bottom" 
         Grid.Row="4" Grid.ColumnSpan="3"/>
    </Grid>
</mxb:ToolbarManager>

Toolbar Container Options

A ToolbarContainerControl's main setting is ToolbarContainerControl.DockType, which specifies how the container is docked to its parent. You can set the DockType property to Left, Right, Top, Bottom, and Standalone.

The DockType setting determines the container's border visibility, and default alignment of nested toolbars. For instance, if a container's DockType option is Left, the container draws a border at its right edge, and arranges nested toolbars vertically. The image below demonstrates the toolbar container that has its DockType option set to Left. The child toolbars are oriented vertically according to the DockType setting.

toolbars-get-started-toolbarcontainer-docktype-left

3. Create Toolbars

Add toolbars (Toolbar objects) to required toolbar containers.

toolbars-get-started-empty-toolbars

xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"

<mxb:ToolbarManager IsWindowManager="True">
    <Grid RowDefinitions="Auto, *, Auto, *, Auto" ColumnDefinitions="Auto, *, Auto">
        <mxb:ToolbarContainerControl DockType="Top" Grid.ColumnSpan="3">
            <mxb:Toolbar x:Name="MainMenu" ToolbarName="Main Menu" DisplayMode="MainMenu">
            </mxb:Toolbar>

            <mxb:Toolbar x:Name="EditToolbar" ToolbarName="Edit" 
             ShowCustomizationButton="True">
            </mxb:Toolbar>

            <mxb:Toolbar x:Name="FontToolbar" ToolbarName="Font" 
             ShowCustomizationButton="True">
            </mxb:Toolbar>
        </mxb:ToolbarContainerControl>

        <mxb:ToolbarContainerControl DockType="Left" Grid.Row="1" 
         Grid.Column="0" Grid.RowSpan="3">
            <mxb:Toolbar x:Name="TextEditingToolbar" ToolbarName="Text Editing" 
             ShowCustomizationButton="True" >
            </mxb:Toolbar>
        </mxb:ToolbarContainerControl>
                
        <TextBox Grid.Row="1" Grid.Column="1" x:Name="textBox1"  Text="Text Editor" 
         AcceptsReturn="True" CornerRadius="0" FontFamily="Arial" FontSize="20"/>
        <TextBox x:Name="textBox2"  Grid.Row="3" Grid.Column="1" Text="Text Editor #2" 
         AcceptsReturn="True" CornerRadius="0" FontFamily="Arial" FontSize="20"/>

        <mxb:ToolbarContainerControl DockType="Right" Grid.Row="1" 
         Grid.Column="2" Grid.RowSpan="3"/>

        <mxb:ToolbarContainerControl DockType="Bottom" Grid.Row="4" Grid.ColumnSpan="3">
            <mxb:Toolbar DisplayMode="StatusBar" ToolbarName="Status Bar" x:Name="StatusBar">
            </mxb:Toolbar>
        </mxb:ToolbarContainerControl>
    </Grid>
</mxb:ToolbarManager>

The snippet above populates three toolbar containers with toolbars, and leaves one toolbar container empty. Users will be able to drag and drop toolbars to any of the four toolbar containers at runtime.

Specify the Main Menu and Status Bar

To indicate that a toolbar is the main menu or status bar, set its Toolbar.DisplayMode property to MainMenu and StatusBar, respectively.

<mxb:Toolbar x:Name="MainMenu" ToolbarName="Main Menu" DisplayMode="MainMenu">
</mxb:Toolbar>

The main menu and status bar have distinctive appearance settings and behavior. For instance, they do not contain a drag handle, so they cannot be dragged by users. A user cannot hide the main menu and status bar at runtime.

toolbars-get-started-mainmenu-statusbar

Toolbar Options

Toolbar objects expose many options to customize their view, layout, and behavior settings. Some of these options include:

  • ToolbarName — The toolbar's display name. Toolbar names are displayed in the Customization window and also when a toolbar is in the floating state.

    toolbars-get-started-toolbarname

  • ShowCustomizationButton — Specifies the visibility of the Customization button used to activate customization mode and open the Customization window.

    toolbars-get-started-customization-button

  • AllowDragToolbar — Specifies the visibility of a drag handle that enables users to drag the toolbar.

    toolbars-get-started-customization-drag-thumb

  • DockType — This property allows you to move a toolbar to a specific toolbar container in code-behind, or make the toolbar floating.

  • StretchToolbar — Enables toolbar stretching. In this mode, no other toolbar can be displayed in the same row.

  • WrapItems — Enables a multiple row layout for a toolbar.

4. Create Toolbar Items

The next step is to populate toolbars with toolbar items: regular buttons, check buttons, in-place editors, sub-menus, and text items. Toolbar items are encapsulated by classes derived from the ToolbarItem class, which exposes common toolbar item options.

This tutorial creates the following toolbar items:

ToolbarButtonItem

A regular button that fires a command specified by the Command property.

toolbars-get-started-ToolbarButtonItem

<mxb:Toolbar x:Name="EditToolbar" ToolbarName="Edit" ShowCustomizationButton="True"  >
    <mxb:ToolbarButtonItem Header="Cut" Command="{Binding #textBox1.Cut}" 
     IsEnabled="{Binding #textBox1.CanCut}" 
     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditCut.svg'}" 
     Category="Edit"/>
    <mxb:ToolbarButtonItem Header="Copy" Command="{Binding #textBox1.Copy}" 
     IsEnabled="{Binding #textBox1.CanCopy}" 
     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditCopy.svg'}" 
     Category="Edit"/>
</mxb:Toolbar>

Common Toolbar Item Options

The base ToolbarItem class provides common options inherited by all toolbar items. Some of these options include:

  • Alignment — The item's alignment within the toolbar.
  • Category — A category to which the item belongs. Categories are used to organize items into logical groups within the Customization window.
  • Command — A command executed when the button is clicked.
  • CommandParameter — A command parameter passed to the specified command.
  • DisplayMode — Gets whether to display only the glyph, the header, or both.
  • Header — The item's display text.
  • Glyph — The item's image.
  • GlyphAlignment — The glyph alignment relative to the item's header.
  • GlyphSize — The glyph display size.
  • ShowSeparator — Allows you to display a separator before the item.

Assign a Dropdown Control/Menu to a ToolbarButtonItem

You can associate a dropdown control/menu with a ToolbarButtonItem object. The dropdown is activated by a click on the built-in dropdown arrow or the item itself (see the DropDownArrowVisibility option below for more information).

Let's associate the Paste button (ToolbarButtonItem) with a dropdown menu. The dropdown menu will display the Paste and Paste As commands.

toolbars-get-started-pastebutton-with-dropdown-menu

<mxb:ToolbarButtonItem Header="Paste" Command="{Binding #textBox1.Paste}" 
 IsEnabled="{Binding #textBox1.CanPaste}" 
 Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditPaste.svg'}" 
 Category="Edit">
    <mxb:ToolbarButtonItem.DropDownControl>
        <mxb:PopupMenu>
            <mxb:ToolbarButtonItem Header="Paste" Command="{Binding #textBox1.Paste}" 
             IsEnabled="{Binding #textBox1.CanPaste}" 
             Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditPaste.svg'}"/>
            <mxb:ToolbarButtonItem Header="Paste As" 
             Command="{Binding PasteAsCommand}" 
             IsEnabled="{Binding #textBox1.CanPaste}"/>
        </mxb:PopupMenu>
    </mxb:ToolbarButtonItem.DropDownControl>
</mxb:ToolbarButtonItem>

The following properties are used to specify a dropdown control and the way it is displayed:

  • DropDownControl — Gets or sets a dropdown control/menu associated with the item. This property accepts PopupContainer and PopupMenu objects.

  • DropDownArrowVisibility — Specifies whether the item displays a dropdown arrow used to invoke the associated dropdown control. Supported options include:

    • ShowArrow — The dropdown arrow is visible. The item and arrow act as a single button. A click on them displays an associated dropdown control.

    • ShowSplitArrow or Default — The dropdown arrow is visible. It acts as a separate button embedded in the item. A click on the dropdown arrow invokes the associated dropdown control. A click on the item invokes its command.

    • Hide — The dropdown arrow is hidden. A click on the item invokes the dropdown control.

ToolbarMenuItem

A button that invokes a sub-menu. To add items to the sub-menu, define them between the start and end <ToolbarMenuItem> tags in XAML markup, or add them to the Items collection.

toolbars-get-started-ToolbarMenuItem

<mxb:Toolbar x:Name="MainMenu" ToolbarName="Main Menu" DisplayMode="MainMenu">
    <mxb:ToolbarMenuItem Header="File" Category="File">
        <mxb:ToolbarButtonItem Header="New" 
         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FileNew.svg'}" 
         Category="File" Command="{Binding NewFileCommand}"/>
        <mxb:ToolbarButtonItem Header="Open" 
         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FileOpen.svg'}" 
         Category="File" Command="{Binding OpenFileCommand}"/>
        <mxb:ToolbarButtonItem Header="Print" 
         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FilePrint.svg'}" 
         Category="File" Command="{Binding PrintCommand}" ShowSeparator="True"/>
    </mxb:ToolbarMenuItem>
</mxb:Toolbar>

You can add all supported item types to the sub-menu.

ToolbarCheckItem

A check button that can be either in the normal or pressed state.

toolbars-get-started-ToolbarCheckItem

<mxb:Toolbar x:Name="FontToolbar" ToolbarName="Font" ShowCustomizationButton="False">
    <mxb:ToolbarCheckItem Header="Bold" 
     IsChecked="{Binding #textBox1.FontWeight, 
      Converter={local:BoolToFontWeightConverter}, Mode=TwoWay}" 
     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FontBold.svg'}" 
     Category="Font"/>
    ...
</mxb:Toolbar>

ToolbarCheckItem Options

  • IsChecked — Gets or sets the button's check state.
  • CheckedChanged — The event that fires when the checked state changes.

ToolbarEditorItem

An item that allows you to embed an Eremex editor in a toolbar or menu.

toolbars-get-started-ToolbarEditorItem

<mxb:Toolbar x:Name="FontToolbar" ToolbarName="Font" ShowCustomizationButton="False">
    <mxb:ToolbarEditorItem Header="Font:" EditorWidth="150" Category="Font" 
     EditorValue="{Binding #textBox1.FontFamily, 
      Converter={local:FontNameToFontFamilyConverter}}">
        <mxb:ToolbarEditorItem.EditorProperties>
            <mxe:ComboBoxEditorProperties 
             ItemsSource="{Binding $parent[local:MainWindow].Fonts}" 
             IsTextEditable="False"/>
        </mxb:ToolbarEditorItem.EditorProperties>
    </mxb:ToolbarEditorItem>
    ...
</mxb:Toolbar>

ToolbarEditorItem Options

  • EditorValue — Allows you to set and read the inplace editor's value.
  • EditorProperties — Specifies the type of the editor to be embedded in a toolbar/menu. In the code snippet above, the EditorProperties property is set to a ComboBoxEditorProperties object. This object contains settings specific to the ComboBoxEditor control. A toolbar will automatically create a ComboBoxEditor control at runtime from the specified ComboBoxEditorProperties object.

ToolbarTextItem

A text label. A click on a text label does not raise any action (command).

toolbars-get-started-ToolbarTextItem

<mxb:Toolbar DisplayMode="StatusBar" ToolbarName="Status Bar" 
 x:Name="StatusBar" ShowCustomizationButton="False">
    <mxb:ToolbarTextItem  Name="tbTextItem1" Alignment="Far" 
     ShowSeparator="True" ShowBorder="False" Category="Info" 
     CustomizationName="Position Info" 
     Header="{Binding $parent[local:MainWindow].LineNumber}"/>
</mxb:Toolbar>

ToolbarTextItem Options

  • SizeMode — Gets or sets whether the item is auto-sized to fit its content, or stretched to occupy the available space in the toolbar.
  • ShowBorder — Gets or sets whether to display a border around the item.

Other Toolbar Item Types

The Toolbars library also supports other toolbar item types that are not demonstrated in this tutorial:

  • ToolbarItemGroup — A group of toolbar items.
  • ToolbarCheckItemGroup — A group of check buttons. Use it to create a group of mutually exclusive check items, or a group that supports selecting multiple items at a time.

See the following topic for more information: Toolbar Items.

5. Create a Standalone Toolbar

You can place toolbars at any position within a window, not only along its edges. For instance, you can place toolbars with commands next to a target control. These toolbars are called 'standalone', because they reside within 'standalone' toolbar containers.

toolbars-get-started-standalone-toolbar

To create a standalone toolbar, do the following:

  1. Create a toolbar container (ToolbarContainerControl) at the required position. Set its DockType property to Standalone.

    Standalone toolbar containers do not have borders.

  2. Add a toolbar with commands to this toolbar container.

The code below displays a standalone toolbar between two text editors. The toolbar's Select All command selects text in the second text editor.

<mxb:ToolbarManager Name="toolbarManager1" IsWindowManager="True" >
    <Grid RowDefinitions="Auto, *, Auto, *, Auto" ColumnDefinitions="Auto, *, Auto">
        ...
        <TextBox Grid.Row="1" Grid.Column="1" x:Name="textBox1" Text="Text Editor" 
         AcceptsReturn="True"/>

        <mxb:ToolbarContainerControl DockType="Standalone" Grid.Row="2" Grid.Column="1">
            <mxb:Toolbar x:Name="TextEditor2Toolbar" ToolbarName="Standalone Toolbar" 
             ShowCustomizationButton="True" AllowDragToolbar="true"  >
                <mxb:ToolbarButtonItem Header="Select All" 
                 Command="{Binding #textBox2.SelectAll}" Category="TextEditor2 Toolbar" />
                <mxb:ToolbarButtonItem Header="Make Toolbar Floating" 
                 Command="{Binding $parent[local:MainWindow].MakeToolbar2Floating}" 
                 ShowSeparator="True"
                 Category="TextEditor2 Toolbar"/>
            </mxb:Toolbar>
        </mxb:ToolbarContainerControl>

        <TextBox x:Name="textBox2"  Grid.Row="3" Grid.Column="1" Text="Text Editor #2" 
         AcceptsReturn="True"/>
    </Grid>
</mxb:ToolbarManager>

6. Assign a Context Menu to a Text Editor

To specify a context menu for a control, create a PopupMenu object and assign it to the target control using the ToolbarManager.ContextPopup attached property. Toolbar items of any type can be added to popup menus.

toolbars-get-started-context-menu

<TextBox Grid.Row="1" Grid.Column="1" x:Name="textBox1"  Text="Text Editor" 
 AcceptsReturn="True" CornerRadius="0" FontFamily="Arial" FontSize="20" >
    <mxb:ToolbarManager.ContextPopup>
        <mxb:PopupMenu ShowIconStrip="True" Header="Text Box Menu" ShowHeader="True">
            <mxb:ToolbarButtonItem Header="Undo" HotKeyDisplayString="Ctrl+Z" 
             Command="{Binding #textBox1.Undo}" IsEnabled="{Binding #textBox1.CanUndo}"
             Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditUndo.svg'}"  
             Category="Edit"/>
            <mxb:ToolbarButtonItem Header="Redo" HotKeyDisplayString="Ctrl+Y"  
             Command="{Binding #textBox1.Redo}" IsEnabled="{Binding #textBox1.CanRedo}"
             Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditRedo.svg'}"  
             Category="Edit"/>
            <mxb:ToolbarSeparatorItem/>
            <mxb:ToolbarButtonItem Header="Clear" Command="{Binding #textBox1.Clear}" 
             HotKeyDisplayString="Ctrl+Q"  Category="Edit"
             Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditDelete.svg'}"/>
        </mxb:PopupMenu>
    </mxb:ToolbarManager.ContextPopup>
</TextBox>

PopupMenu Options

  • ContentRightIndent — Specifies the width of the empty space to the right of menu items' text.

    toolbars-popupmenu-contentrightindent

  • Header — Allows you to specify a menu header.

  • ShowHeader — Gets or sets whether the menu header is visible.

  • ShowIconStrip — Gets or sets whether to display a vertical strip of icons for menu items. A menu item's icon is specified by the item's Glyph property.

7. Specify Hotkeys for Toolbar Items

Use the ToolbarItem.HotKey property to assign shortcuts to items.

<mxb:ToolbarButtonItem
    Header="Clear" Command="{Binding #textBox1.Clear}" HotKey="Ctrl+Q"
    Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditDelete.svg'}"  Category="Edit"/>

The ToolbarManager component's bounds define the default hotkey scope. If focus is within the hotkey scope, the ToolbarManager can intercept and process hotkeys. You can set the ToolbarManager.IsWindowManager property to true to expand the hotkey scope to the entire window. In this case, the ToolbarManager registers hotkeys in the window, and it can handle hotkeys even if focus is beyond the ToolbarManager's bounds.

See the following topic for more information: Toolbar Item Hotkeys.

8. Assign Tooltips to Toolbar Items

The ToolTip property allows you to specify tooltips for toolbar items.

toolbars-item-tooltip

<mxb:ToolbarButtonItem Header="Cut" Command="{Binding #textBox1.Cut}" 
 IsEnabled="{Binding #textBox1.CanCut}"
 Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditCut.svg'}"
 Category="Edit"
 ToolTip.Tip="Cut Selection"/>

9. Make a Toolbar Floating

Let's make a toolbar floating in code-behind. Ensure that the target toolbar has a name, so you can access it. After you get the toolbar object, set its Toolbar.DockType property to Floating. Use the Toolbar.FloatingPosition property to set the floating toolbar's location.

EditToolbar.DockType = Eremex.AvaloniaUI.Controls.Bars.MxToolbarDockType.Floating;
EditToolbar.FloatingPosition = new PixelPoint(200, 200);

To create a floating toolbar in XAML, define a Toolbar object in the ToolbarManager.Toolbars collection, and set the Toolbar.DockType property to Floating.

See the following topic for more information: Floating Toolbars.

10. Runtime

The Toolbars library supports toolbar customization by users at runtime. Run the application to see these features in action:

  • Bar drag-and-drop — Toolbars display drag handles that allow you to rearrange bars.

toolbars-get-started-customization-drag-thumb

  • Quick toolbar customization — You can quickly move items within and between bars using drag-and-drop by holding the Alt key down.

toolbars-get-started-customization-with-ALT

  • Customization Mode and Customization Window — Click a toolbar's Customization button ('...'), and then select the 'Customize' command. Activation of customization mode displays the Customization Window:

toolbars-get-started-customization-window

In customization mode, you can do the following:

  • Hide and restore toolbars.
  • Create and manage user toolbars.
  • Hide, restore and rearrange toolbar items between bars and sub-menus.

11. Complete Code

Below you can find the complete code of this tutorial.

The SVG images used in this example are placed in the bars_sample/Images/Toolbars folder. They have the Build Action property set to AvaloniaResource.

MainWindow.axaml:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:mxe="https://schemas.eremexcontrols.net/avalonia/editors"
        xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"
        
        xmlns:mx="https://schemas.eremexcontrols.net/avalonia"
        xmlns:col="using:System.Collections"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:BarsSample"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="BarsSample.MainWindow"
        Title="Toolbars Sample"
        Width="600" Height="400"
        >
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <mxb:ToolbarManager Name="toolbarManager1" IsWindowManager="True" >
        <Grid RowDefinitions="Auto, *, Auto, *, Auto" ColumnDefinitions="Auto, *, Auto">
            <mxb:ToolbarContainerControl DockType="Top" Grid.ColumnSpan="3">
                <mxb:Toolbar x:Name="MainMenu" ToolbarName="Main Menu" DisplayMode="MainMenu" >
                    <mxb:ToolbarMenuItem Header="File" Category="File">
                        <mxb:ToolbarButtonItem Header="New" 
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FileNew.svg'}" 
                         Category="File"
                         Command="{Binding NewFileCommand}"/>
                        <mxb:ToolbarButtonItem Header="Open" 
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FileOpen.svg'}" 
                         Category="File"
                         Command="{Binding OpenFileCommand}"/>
                        <mxb:ToolbarButtonItem Header="Print" 
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FilePrint.svg'}" 
                         Category="File"
                         Command="{Binding PrintCommand}" ShowSeparator="True"/>
                    </mxb:ToolbarMenuItem>

                    <mxb:ToolbarMenuItem Header="Edit" Category="Edit" >
                        <mxb:ToolbarButtonItem Header="Cut" 
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditCut.svg'}" 
                         Category="Edit"
                         Command="{Binding #textBox1.Cut}" IsEnabled="{Binding #textBox1.CanCut}"/>
                        <mxb:ToolbarButtonItem Header="Copy" 
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditCopy.svg'}" 
                         Category="Edit"
                         Command="{Binding #textBox1.Copy}" IsEnabled="{Binding #textBox1.CanCopy}"/>
                        <mxb:ToolbarButtonItem Header="Paste" 
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditPaste.svg'}" 
                         Category="Edit"
                         Command="{Binding #textBox1.Paste}" IsEnabled="{Binding #textBox1.CanPaste}"/>
                    </mxb:ToolbarMenuItem>
                    <mxb:ToolbarButtonItem Header="About" Category="Options" ShowSeparator="True" 
                     Alignment="Far" Command="{Binding AboutCommand}"/>
                </mxb:Toolbar>

                <mxb:Toolbar x:Name="EditToolbar" ToolbarName="Edit" ShowCustomizationButton="True"  >
                    <mxb:ToolbarButtonItem Header="Cut" Command="{Binding #textBox1.Cut}" 
                     IsEnabled="{Binding #textBox1.CanCut}" 
                     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditCut.svg'}" 
                     Category="Edit"/>
                    <mxb:ToolbarButtonItem Header="Copy" Command="{Binding #textBox1.Copy}" 
                     IsEnabled="{Binding #textBox1.CanCopy}" 
                     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditCopy.svg'}" 
                     Category="Edit"/>
                    <mxb:ToolbarButtonItem Header="Paste"
                     Command="{Binding #textBox1.Paste}"
                     IsEnabled="{Binding #textBox1.CanPaste}"
                     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditPaste.svg'}"
                     Category="Edit"
                     DropDownArrowVisibility="ShowArrow" DropDownArrowAlignment="Default">
                        <mxb:ToolbarButtonItem.DropDownControl>
                            <mxb:PopupMenu>
                                <mxb:ToolbarButtonItem Header="Paste" 
                                 Command="{Binding #textBox1.Paste}" 
                                 IsEnabled="{Binding #textBox1.CanPaste}"/>
                                <mxb:ToolbarButtonItem Header="Paste As" 
                                 Command="{Binding PasteAsCommand}" 
                                 IsEnabled="{Binding #textBox1.CanPaste}"/>
                            </mxb:PopupMenu>
                        </mxb:ToolbarButtonItem.DropDownControl>
                    </mxb:ToolbarButtonItem>
                </mxb:Toolbar>

                <mxb:Toolbar x:Name="FontToolbar" ToolbarName="Font" ShowCustomizationButton="True" >
                    <mxb:ToolbarCheckItem Header="Bold" 
                     IsChecked="{Binding #textBox1.FontWeight, 
                      Converter={local:BoolToFontWeightConverter}, Mode=TwoWay}" 
                     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FontBold.svg'}" 
                     Category="Font"/>
                    <mxb:ToolbarCheckItem Header="Italic" 
                     IsChecked="{Binding #textBox1.FontStyle, 
                      Converter={local:BoolToFontStyleConverter}, Mode=TwoWay}" 
                     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/FontItalic.svg'}" 
                     Category="Font"/>
                    <mxb:ToolbarEditorItem Header="Font:" IsVisible="" EditorWidth="150" Category="Font" 
                    EditorValue="{Binding #textBox1.FontFamily, 
                     Converter={local:FontNameToFontFamilyConverter}}">
                        <mxb:ToolbarEditorItem.EditorProperties>
                            <mxe:ComboBoxEditorProperties 
                             ItemsSource="{Binding $parent[local:MainWindow].Fonts}"
                             IsTextEditable="False" PopupMaxHeight="145"/>
                        </mxb:ToolbarEditorItem.EditorProperties>
                    </mxb:ToolbarEditorItem>
                </mxb:Toolbar>
            </mxb:ToolbarContainerControl>

            <mxb:ToolbarContainerControl DockType="Left" Grid.Row="1" Grid.Column="0" 
             Grid.RowSpan="3">
                <mxb:Toolbar x:Name="TextEditingToolbar" ToolbarName="Text Editing" 
                 ShowCustomizationButton="True" >
                    <mxb:ToolbarButtonItem Header="Undo" HotKeyDisplayString="Ctrl+Z" 
                     Command="{Binding #textBox1.Undo}" IsEnabled="{Binding #textBox1.CanUndo}"
                     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditUndo.svg'}" 
                     Category="Edit"/>
                    <mxb:ToolbarButtonItem Header="Redo" HotKeyDisplayString="Ctrl+Y"  
                     Command="{Binding #textBox1.Redo}" IsEnabled="{Binding #textBox1.CanRedo}"
                     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditRedo.svg'}"  
                     Category="Edit"/>
                    <mxb:ToolbarButtonItem Header="Clear" Command="{Binding #textBox1.Clear}" 
                     HotKey="Ctrl+Q"
                     Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditDelete.svg'}" 
                     Category="Edit"/>
                </mxb:Toolbar>
            </mxb:ToolbarContainerControl>

            <TextBox Grid.Row="1" Grid.Column="1" x:Name="textBox1"  Text="Text Editor" 
            AcceptsReturn="True" CornerRadius="0" FontFamily="Arial" FontSize="20">
                <mxb:ToolbarManager.ContextPopup>
                    <mxb:PopupMenu ShowIconStrip="True" Header="Text Box Menu" ShowHeader="True">
                        <mxb:ToolbarButtonItem Header="Undo" HotKeyDisplayString="Ctrl+Z" 
                         Command="{Binding #textBox1.Undo}" IsEnabled="{Binding #textBox1.CanUndo}"
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditUndo.svg'}" 
                         Category="Edit"/>
                        <mxb:ToolbarButtonItem Header="Redo" HotKeyDisplayString="Ctrl+Y" 
                         Command="{Binding #textBox1.Redo}" IsEnabled="{Binding #textBox1.CanRedo}"
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditRedo.svg'}"  
                         Category="Edit"/>
                        <mxb:ToolbarSeparatorItem/>
                        <mxb:ToolbarButtonItem Header="Clear" Command="{Binding #textBox1.Clear}" 
                         HotKeyDisplayString="Ctrl+Q"  Category="Edit"
                         Glyph="{SvgImage 'avares://bars_sample/Images/Toolbars/EditDelete.svg'}"/>
                    </mxb:PopupMenu>
                </mxb:ToolbarManager.ContextPopup>
            </TextBox>

            <mxb:ToolbarContainerControl DockType="Standalone" Grid.Row="2" Grid.Column="1">
                <mxb:Toolbar x:Name="TextEditor2Toolbar" ToolbarName="Standalone Toolbar" 
                 ShowCustomizationButton="True" AllowDragToolbar="true"  >
                    <mxb:ToolbarButtonItem Header="Select All" 
                     Command="{Binding #textBox2.SelectAll}" Category="TextEditor2 Toolbar"/>
                    <mxb:ToolbarButtonItem Header="Make Toolbar Floating" 
                     Command="{Binding $parent[local:MainWindow].MakeToolbar2Floating}" 
                     ShowSeparator="True"
                     Category="TextEditor2 Toolbar"/>
                </mxb:Toolbar>
            </mxb:ToolbarContainerControl>

            <TextBox x:Name="textBox2"  Grid.Row="3" Grid.Column="1" Text="Text Editor #2" 
             AcceptsReturn="True" CornerRadius="0" FontFamily="Arial" FontSize="20"/>

            <mxb:ToolbarContainerControl DockType="Right" Grid.Row="1" Grid.Column="2" 
             Grid.RowSpan="3"/>

            <mxb:ToolbarContainerControl DockType="Bottom" Grid.Row="4" Grid.ColumnSpan="3">
                <mxb:Toolbar DisplayMode="StatusBar" ToolbarName="Status Bar" x:Name="StatusBar">
                    <mxb:ToolbarTextItem  Name="tbTextItem1" Alignment="Far" 
                     ShowSeparator="True" ShowBorder="False" Category="Info" 
                     CustomizationName="Position Info"
                     Header="{Binding $parent[local:MainWindow].LineNumber}"/>

                </mxb:Toolbar>
            </mxb:ToolbarContainerControl>
        </Grid>
    </mxb:ToolbarManager>
</Window>

App.axaml:

<Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             RequestedThemeVariant="Light"
             x:Class="BarsSample.App">
    <Application.Resources>
        <ResourceDictionary>
        </ResourceDictionary>
    </Application.Resources>
    <Application.Styles>
        <FluentTheme/>
        <StyleInclude Source="avares://Eremex.Avalonia.Controls/Themes/Light/Theme.axaml"/>
        <StyleInclude Source="avares://Eremex.Avalonia.Controls/Themes/Generic.axaml"/>
    </Application.Styles>
</Application>

MainWindow.axaml.cs:

using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;

namespace BarsSample
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            textBox1.PropertyChanged += TextBox_PropertyChanged;
        }

        private void TextBox_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
        {
            if (e.Property == TextBox.CaretIndexProperty)
            {
                NotifyPropertyChanged("LineNumber");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        IReadOnlyList<string> fonts;
        public IReadOnlyList<string> Fonts => fonts ?? 
        (
            fonts = FontManager.Current.SystemFonts.Select(x => x.Name).OrderBy(x => x).ToList()
        );

        [RelayCommand]
        public void MakeToolbar2Floating()
        {
            TextEditor2Toolbar.DockType = Eremex.AvaloniaUI.Controls.Bars.MxToolbarDockType.Floating;
            TextEditor2Toolbar.FloatingPosition = new PixelPoint(200, 200);
        }

        public string LineNumber
        {
            get
            {
                TextBox textBox = this.textBox1;
                string text = textBox.Text;
                string newLine = textBox.NewLine;

                int currentIndex = 0;
                int lineNumber = 0;
                while (currentIndex <= textBox.CaretIndex)
                {
                    lineNumber++;
                    int newLineIndex = text.IndexOf(newLine, currentIndex);
                    if (newLineIndex >= 0)
                        currentIndex = newLineIndex + newLine.Length;
                    else
                        break;
                }
                return "Line Number: " + lineNumber;
            }
        }
    }
}

MainViewModel.cs:

using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using CommunityToolkit.Mvvm.Input;
using Eremex.AvaloniaUI.Controls.Common;
using System;
using System.Globalization;

namespace BarsSample
{
    public partial class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            
        }
        
        [RelayCommand]
        void About()
        {
            
        }

        [RelayCommand]
        void NewFile()
        {

        }

        [RelayCommand]
        void OpenFile()
        {

        }

        [RelayCommand]
        void Print()
        {

        }

    }

    public class BoolToFontWeightConverter : MarkupExtension, IValueConverter
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return ((FontWeight)value) == FontWeight.Bold;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? FontWeight.Bold : FontWeight.Normal;
        }
    }

    public class BoolToFontStyleConverter : MarkupExtension, IValueConverter
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (FontStyle)value == FontStyle.Italic;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? FontStyle.Italic : FontStyle.Normal;
        }
    }

    public class FontNameToFontFamilyConverter : MarkupExtension, IValueConverter
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return ((FontFamily)value).Name;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return new FontFamily((string)value);
        }
    }
}

See Also