TabTip.Avalonia
2.0.0
dotnet add package TabTip.Avalonia --version 2.0.0
NuGet\Install-Package TabTip.Avalonia -Version 2.0.0
<PackageReference Include="TabTip.Avalonia" Version="2.0.0" />
<PackageVersion Include="TabTip.Avalonia" Version="2.0.0" />
<PackageReference Include="TabTip.Avalonia" />
paket add TabTip.Avalonia --version 2.0.0
#r "nuget: TabTip.Avalonia, 2.0.0"
#:package TabTip.Avalonia@2.0.0
#addin nuget:?package=TabTip.Avalonia&version=2.0.0
#tool nuget:?package=TabTip.Avalonia&version=2.0.0
<p align="center"> <img src="icon.png" width="25%" style="max-width: 150px;" /> </p>
Tabtip.Avalonia (Tablet Text Input Panel)
Avalonia-based and cross-platform re-imagining of WPFTabTip that came to be after I stumbled upon this discussion in the Avalonia repo
The Software Keyboard is only supported on Windows. Two trigger policies ship out of the
box: PointerOnlyTriggerPolicy (default — gate on PointerType) and
KeyboardDetectionTriggerPolicy (skip the keyboard when a hardware keyboard is detected,
open on any focus otherwise). See Software Keyboard Trigger.
Usage
Just Install the NuGet package and decide whether you want to use the Global Integration or the Targeted Integration.
Gloabl Integration
Add the following to App.xaml.cs:
// At the top of your file
using TabTip.Avalonia;
// ... start of App.xaml.cs
public override void OnFrameworkInitializationCompleted()
{
// ...
// Integrate the tabtip manager into the entire app.
TabTipManager.Integrate(new PointerOnlyTriggerPolicy()); // Global = true is the default.
// ...
}
This will integrate the TabTipManager into the entire app, causing it to trigger the TabTip for any TextBox that is clicked.
Targeted Integration
Just like for the Global Integration, we need to add a line to App.xaml.cs, but this time, we set
Global = false on the policy. This will only integrate the TabTipManager but prevent it from triggering the TabTip
indiscriminately for any TextBox that is clicked:
// At the top of your file
using TabTip.Avalonia;
// ... start of App.xaml.cs
public override void OnFrameworkInitializationCompleted()
{
// ...
// Only TextBoxes whose ancestor was passed to TabTipManager.Register(...) trigger.
TabTipManager.Integrate(new PointerOnlyTriggerPolicy { Global = false });
// ...
}
Once the TabTipManager has been integrated into the app, you need to explicitly register the controls that you want to trigger the TabTip for. You can do this in two ways:
Using Attached Property (Recommended)
You can register controls directly in XAML using the TabTipManager.IsRegistered attached property:
<Window xmlns="https://github.com/avaloniaui"
xmlns:tabTip="clr-namespace:TabTip.Avalonia;assembly=TabTip.Avalonia">
<TextBox tabTip:TabTipManager.IsRegistered="True" />
<NumericUpDown tabTip:TabTipManager.IsRegistered="True" />
<StackPanel tabTip:TabTipManager.IsRegistered="True">
<TextBox />
<AutoCompleteBox />
</StackPanel>
</Window>
Using Code-Behind
Alternatively, you can register controls programmatically by calling TabTipManager.Register(Control):
// In this example, this is inside of a `Control`, so we need to override `OnApplyTemplate`
// to register the TextBox so that we're sure that it has been initialized.
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
TabTipManager.Register(IntegratedTextBox);
// Even though `NumericUpDown` is not itself a TextBox, we can still register it as the `Register` command will allow
// any child TextBox of the registered control to trigger the TabTip. This applies to any control.
TabTipManager.Register(IntegratedNumericUpDown);
}
Software Keyboard Trigger
TabTipManager.Integrate(...) requires a TabTipTriggerPolicy that decides how the keyboard
gets triggered. Two subclasses ship with the library:
PointerOnlyTriggerPolicy (Legacy method)
opens on configured pointer types (Touch + Pen by default).
This method depends on a pointer pressed event, which means that focusing a TextBox from code-behind will NOT trigger the TabTip to open.
Customize the pointer set via the Triggers property:
TabTipManager.Integrate(new PointerOnlyTriggerPolicy
{
Triggers = [PointerType.Touch, PointerType.Mouse, PointerType.Pen],
});
KeyboardDetectionTriggerPolicy
— "just integrate" mode. Opens the keyboard on any
This method is the preferred way since V2.0 and supports programmatic focusing of TextBoxes.
TextBox focus (pointer or programmatic) unless a keyboard the caller cares about is attached. Best for convertible / 2-in-1 devices that switch modes at runtime.
// Default: only suppress when an external (USB / Bluetooth / Type Cover) keyboard is
// attached. Built-in laptop keyboards are ignored (so a 2-in-1 in tablet mode still
// pops the OSK even though its hinge keyboard is technically still enumerated).
TabTipManager.Integrate(new KeyboardDetectionTriggerPolicy());
// Strict: also suppress when a built-in keyboard is detected.
TabTipManager.Integrate(new KeyboardDetectionTriggerPolicy
{
SuppressOn = HardwareKeyboardType.Physical | HardwareKeyboardType.BuiltIn,
});
Remote sessions (RDP, RemoteApp) are suppressed by default — the remote user's local input
devices aren't visible from inside the session. Set SuppressOnRemoteSession = false to
decide purely on attached keyboards.
Both expose Global (default true) — set to false to require explicit registration.
Note:
PointerOnlyTriggerPolicyignores programmatic focus by design. A code-drivencontrol.Focus()produces no pointer event, and there is no pointer-type signal to gate on, so this policy doesn't subscribe to focus events at all. UseKeyboardDetectionTriggerPolicyif you need the keyboard to open on programmatic focus.
TabTip Factory
The ITabTipFactory interface allows you to provide your own implementation of the ITabTip interface, giving you the flexibility to create it based
on which OS the host is running on. The current default is DefaultTabTipFactory which creates a TabTip instance for Windows or, if not running on Windows,
simply creates a NullTabTip instance, which has an empty Toggle method so that we don't do anything on other OSs.
Overriding TabTip Factory
To override the default factory,
provide an implementation of ITabTipFactory to TabTipManager.OverrideTabTipFactory as follows:
TabTipManager.OverrideTabTipFactory(myCustomFactory);
This will change the factory used and set the TabTip used by the manager to the one returned by myCustomFactory.
TabTip
The TabTip is the platform-specific implementation of the ITabTip interface. It is responsible for opening and closing
the software keyboard. We currently have only two built-in implementations:
- NullTabTip: Does nothing.
- WindowsTabTip: Contains logic to open the Software Keyboard on Windows.
Overriding TabTip
The recommended way to override the TabTip used it to override the ITabTipFactory as described above.
TabTip Integration
The ITabTipIntegration interface defines the methods used to integrate the TabTip into the application. The implementing
class is responsible for when to trigger the TabTip as well as setting up the appropriate event handlers to know
when a TextBox has been clicked.
Overriding TabTip Integration
Before fully replacing the default integration, it is recommended to override the TabTipIntegration class.
The Integrate method is purposefully virtual so that it can be overridden. Simply create a new class that inherits from
TabTipIntegration and override the Integrate method. If you specifically call base.Integrate() in your override,
it will call the default implementation of Integrate which will set up the default event handlers while allowing you to
add any new ones you want. This is useful if all you want to do is integrate controls other than TextBox.
Regardless of whether you create a brand new class that implements ITabTipIntegration or extends TabTipIntegration,
you need to set it as the one that is used by calling:
TabTipManager.OverrideIntegration(myCustomIntegration);
OS Support
| OS | Supported | Notes |
|---|---|---|
| Windows | ✅ | |
| Mac | ❌⚠️ | Macs don't currently have touchscreens so... I don't see the benefit of adding support. Let me know if you disagree! |
| Linux | ❌ | I might look into adding support in future versions but I suspect this will require distro-specific handling. |
| Android | ❌⚠️ | I have not added specific support for this library as I THINK Avalonia already supports Android properly. |
| iOs/iPadOS | ❌⚠️ | I have not added specific support for this library as I THINK Avalonia already supports Android properly. |
Why the name TabTip?
"TabTip" refers to the Touch Keyboard and Handwriting Panel, also known as the Tablet Text Input Panel, which is a virtual keyboard in Windows. Since this project was heavily inspired by the WPF version, the name is also inspired by the WPF version.
Roadmap (non-binding 🙃)
- Make it possible to detect if a Hardware Keyboard is connected.
Add a method likeIntegratewhich allows us to specify a specific control so that it integrates it and all its children only.- ex:
Integrate(MyCustomControlReference)would trigger for that control and all its children, but not any other controls in the app.
- ex:
Add an AttachedProperty or something that will integrate the control it's attached to. Not sure if this is an attached property or not but basically, the same way ToolTip works.✅ Completed - AddedTabTipManager.IsRegisteredattached property- Add code to add the relevant interfaces/classes to DI using
Microsoft.Extensions.DependencyInjection - Add support for Linux?
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Avalonia (>= 12.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- System.Reactive (>= 6.0.1)
-
net8.0
- Avalonia (>= 12.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- System.Reactive (>= 6.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.