NullReferenceException is thrown when the RadVirtualKeyboard control gets loaded, but it is not added to the visual tree. This skips the initialization of an internal collection which is used on Loaded, thus throwing the error.
To work this around, you can manually call the OnApplyTemplate method of RadVirtualKeyboard before it gets loaded.
virtualKeyboard.OnApplyTemplate();
A memory leak occurs when you remove the RadVirtualKeyboard from the view. The control's reference is hold by an internal static collection, which prevents it from being garbage collected.
To work this around, you can use reflection to access the NativeMethods.callbacks static collection and clear it. If you have the SynchronizeCultureWithSystem propety set to True (the default value) you should also cancel the task listens for system culture change.
private void RemoveKeyboardFromView(RadVirtualKeyboard keyboard)
{
var assembly = Assembly.LoadFrom("Telerik.Windows.Controls.Navigation.dll");
var type = assembly.GetType("Telerik.Windows.Controls.VirtualKeyboard.NativeMethods");
var field = type.GetField("callbacks", BindingFlags.Static | BindingFlags.NonPublic);
var cancelationTokenFieldInfo = typeof(RadVirtualKeyboard).GetField("systemCultureTrackingCancellationTokenSource", BindingFlags.Instance | BindingFlags.NonPublic);
var keyPressedMethodInfo = typeof(RadVirtualKeyboard).GetMethod("KeyPressed", BindingFlags.Instance | BindingFlags.NonPublic);
var keyPressedCallback = (Action<int, bool>)keyPressedMethodInfo.CreateDelegate(typeof(Action<int, bool>), keyboard);
var callbacks = (List<Action<int, bool>>)field.GetValue(null);
callbacks.Remove(keyPressedCallback);
var cancelationToken = cancelationTokenFieldInfo.GetValue(keyboard) as CancellationTokenSource;
cancelationToken.Cancel();
}
Hello,
I found a bug where the EmptySpace element breaks the layout. When I use a Key-Button instead, the layout shifts to the correct position. However, I don't actually want to have any button in this position at all.
EmptySpace Element:
<RadVirtualKeyboard xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Grid>
<KeysLayout KeySpacing="0.1">
<Rows>
<Row>
<Keys>
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="81" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="87" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="69" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="82" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="84" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="89" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="85" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="73" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="79" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="80" />
<Key DisplayText="" KeyType="Special" Width="2" Height="1" VirtualKey="8" />
</Keys>
</Row>
<Row>
<Keys>
<EmptySpace Width="0.5" Height="1" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="65" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="83" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="68" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="70" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="71" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="72" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="74" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="75" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="76" />
<EmptySpace Width="1" Height="1" />
<Key DisplayText="" KeyType="Special" Width="1.5" Height="2" VirtualKey="13" />
</Keys>
</Row>
<Row>
<Keys>
<Key DisplayText="" KeyType="Lock" Width="1.5" Height="1" VirtualKey="20" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="90" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="88" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="67" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="86" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="66" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="78" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="77" />
<Key DisplayText="," KeyType="Normal" Width="1" Height="1" />
<Key DisplayText="." KeyType="Normal" Width="1" Height="1" />
</Keys>
</Row>
<Row>
<Keys>
<Key DisplayText="&123" KeyType="Special" Width="2" Height="1" VirtualKey="113" />
<Key DisplayText="" KeyType="Special" Width="10" Height="1" VirtualKey="32" />
</Keys>
</Row>
</Rows>
</KeysLayout>
</Grid>
</RadVirtualKeyboard>
Key-Element (Layout desired with EmptySpace):
<RadVirtualKeyboard xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Grid>
<KeysLayout KeySpacing="0.1">
<Rows>
<Row>
<Keys>
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="81" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="87" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="69" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="82" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="84" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="89" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="85" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="73" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="79" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="80" />
<Key DisplayText="" KeyType="Special" Width="2" Height="1" VirtualKey="8" />
</Keys>
</Row>
<Row>
<Keys>
<EmptySpace Width="0.5" Height="1" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="65" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="83" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="68" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="70" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="71" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="72" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="74" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="75" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="76" />
<Key DisplayText="'" KeyType="Normal" Width="1" Height="1" />
<Key DisplayText="" KeyType="Special" Width="1.5" Height="2" VirtualKey="13" />
</Keys>
</Row>
<Row>
<Keys>
<Key DisplayText="" KeyType="Lock" Width="1.5" Height="1" VirtualKey="20" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="90" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="88" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="67" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="86" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="66" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="78" />
<Key KeyType="Normal" Width="1" Height="1" VirtualKey="77" />
<Key DisplayText="," KeyType="Normal" Width="1" Height="1" />
<Key DisplayText="." KeyType="Normal" Width="1" Height="1" />
</Keys>
</Row>
<Row>
<Keys>
<Key DisplayText="&123" KeyType="Special" Width="2" Height="1" VirtualKey="113" />
<Key DisplayText="" KeyType="Special" Width="10" Height="1" VirtualKey="32" />
</Keys>
</Row>
</Rows>
</KeysLayout>
</Grid>
</RadVirtualKeyboard>
The CapsLock key should toggle only the letters. Currently, it behaves as Shift because it shows the secondary text of other keys as well (for example the number keys and their special characters). To reproduce this the ShowSecondaryText attribute of the corresponding key should be set to False.
To work this around, you can create a custom view model for the numeric keys that updates the DisplayText manually.
public class CustomKeyFactory : DefaultKeyFactory
{
public override BaseKeyViewModel CreateKey(int virtualKey, KeyType keyType = KeyType.Normal, string displayText = null, double width = 1, double height = 1, int alternateVirtualKey = -1, string alternateText = null, bool showSecondaryText = false)
{
if (keyType == KeyType.Normal)
{
return new CustomRegularKeyViewModel(virtualKey, width, height, showSecondaryText, displayText);
}
return base.CreateKey(virtualKey, keyType, displayText, width, height, alternateVirtualKey, alternateText, showSecondaryText);
}
}
public class CustomRegularKeyViewModel : RegularKeyViewModel
{
public CustomRegularKeyViewModel(int virtualKey, double keyWidth, double keyHeight, bool showSecondaryText, string displayText = null) : base(virtualKey, keyWidth, keyHeight, showSecondaryText, displayText)
{
}
public override void Update(IKeyUpdateContext context)
{
base.Update(context);
if (this.VirtualKey >= 48 && this.VirtualKey <= 57) // you may need to extend the if-statement to check for other keys as well
{
this.DisplayText = context.IsShiftActive ? this.ShiftText : this.LowerText;
}
}
}
The Header property setting is replaced with a default value when the RadVirtualKeyboardWindowAutomationPeer is created. This happens on environments where the automation peers creation kicks-in. Like, on touch devices, when a screen narrator application is used, on visual elements inspection with some tools (like UISpy or Snoop) or any other automation testing that requires automation peers.
In that case, instead of the user defined Header value, the window's header is replaced with "RadVirtualKeyboardWindow" string.
To work this around, disable the automation peers:
public partial class App : Application
{
public App()
{
AutomationManager.AutomationMode = AutomationMode.Disabled;
this.InitializeComponent();
}
}
Our company would like to use in a greenfield project this new UI component.
On our machine, in both our sample projects and even running your official WPF Demo App, the virtualkeyboard component does not render its buttons correctly.
This is our sample xaml:
<Window x:Class="TelerikWpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
Title="MainWindow" Height="350" Width="525">
<Grid>
<telerik:RadVirtualKeyboard DefaultKeyboardLayout="Compact" HorizontalAlignment="Stretch" />
</Grid>
</Window>
In the attachment you can see how the virtualkeyboard renders ootb. Are we missing something?