When using an onscreen keyboard with the autocomplete box, when the dropdown is open you need to click twice on the keyboard to press a key. Once to close the dropdown and once to press the key.
I have figured out that the MouseLeave event is fired when the dropdown is open and you click outside of it or when closed and leave the watermarktextbox. So, I am able to resend the mouse event to avoid clicking twice, but the drop down still closes. This is ok when the search text changes as it will reopen with the new values. However, when the up/down arrow keys are pressed it closes and doesn't reopen with no way to know what is highlighted in the list.
Is there an event or method that could be overridden to keep the dropdown open if focus doesn't change? (The on screen keyboard doesn't take focus)
8 Answers, 1 is accepted
May I ask you some questions in order to better assist you? Can you specify when and how you are opening the onscreen keyboard? Additionally, may I ask you to explain what you meant by "resend the mouse event to avoid clicking twice"? If you are able to share some code samples as to how you are opening the onscreen keyboard and handling the MouseLeave event I will be able to better understand your scenario. Also, providing a video of how the process looks on your side or any other details you find relevant would be of help as well.
I am looking forward to your reply.
Regards,
Vladimir Stoyanov
Progress Telerik

We are using a custom on screen keyboard from mindfusion.eu. I am unable to attach video wav files here, is there somewhere I could uploaded or send them.
What I am really interested in is having the dropdown stay open when clicking on the keyboard. Adding IsDropDownOpen = true to the OnMouseLeave doesn't do it.
We are using asynchronous filtering and here the drop down xaml.
<
telerik:RadAutoCompleteBox
x:Name
=
"EmailBox"
Grid.Row
=
"1"
IsTabStop
=
"True"
VerticalAlignment
=
"Center"
WatermarkContent
=
"Enter email address"
MinWidth
=
"350"
FontSize
=
"18"
SelectedItems
=
"{Binding Path=AdditionalParticipantEmails, Mode=TwoWay}"
Margin
=
"0,10,10,10"
SelectionMode
=
"Multiple"
TextSearchMode
=
"Contains"
AutoCompleteMode
=
"Suggest"
FilteringBehavior
=
"{StaticResource EmailFilteringBehavior}"
>
<
i:Interaction.Behaviors
>
<
behaviors:EmailRadAutoCompleteBoxBehavior
/>
</
i:Interaction.Behaviors
>
</
telerik:RadAutoCompleteBox
>
the code for the filtering
public
class
EmailFilteringBehavior : IFilteringBehavior, IAsyncItemSearch, IDisposable
{
#region Fields
private
CancellationTokenSource _CurrentCancellationTokenSource;
private
string
_CurrentSearchText;
private
AccountSettingsViewModel _Account;
#endregion
#region IAsyncItemSearch Members
public
void
FindItems(Predicate<
object
> match)
{
//Here we ignore the match variable since we don't need this
if
(_CurrentCancellationTokenSource ==
null
)
{
_CurrentCancellationTokenSource =
new
CancellationTokenSource();
}
else
{
_CurrentCancellationTokenSource.Cancel(
true
);
}
_CurrentCancellationTokenSource =
new
CancellationTokenSource();
CancellationToken cancellationToken = _CurrentCancellationTokenSource.Token;
Task.Factory.StartNew(() => GetEmailAddressesFromExchange(_CurrentSearchText, _Account, cancellationToken), cancellationToken, TaskCreationOptions.LongRunning, TaskSchedulerPriority.Normal)
.ContinueWith(task => ProcessFilteredItems(task.Result), cancellationToken, TaskContinuationOptions.AttachedToParent, TaskScheduler.FromCurrentSynchronizationContext());
}
public
object
GetValue(
object
item)
{
throw
new
NotImplementedException();
}
public
event
EventHandler<AsyncItemSearchEventArgs> ItemsFound;
#endregion
#region IDisposable Members
public
void
Dispose()
{
if
(_CurrentCancellationTokenSource ==
null
)
{
return
;
}
_CurrentCancellationTokenSource.Dispose();
}
#endregion
#region IFilteringBehavior Members
public
IEnumerable<
object
> FindMatchingItems(
string
searchText, IList items, IEnumerable<
object
> escapedItems,
string
textSearchPath, TextSearchMode textSearchMode)
{
_CurrentSearchText = searchText;
_Account = MainViewModel.Instance.SecurityManager.CurrentUser;
if
(MainViewModel.Instance.NewSettings.Calendar.EmailAutoComplete)
{
FindItems(itemText =>
true
);
}
return
Enumerable.Empty<
object
>();
}
#endregion
#region Non-Public Methods
protected
List<
string
> GetEmailAddressesFromExchange(
string
searchText, AccountSettingsViewModel account, CancellationToken cancellationToken)
{
var searchItems =
new
List<
string
>();
if
(cancellationToken.IsCancellationRequested)
{
return
searchItems;
}
if
(
string
.IsNullOrWhiteSpace(searchText))
{
return
searchItems;
}
try
{
if
(MainViewModel.Instance.ProductManager.CurrentEdition.Features.CalendarProviders.IsAvailable)
{
using
(IEmailProviderAdapter emailProvider = MainViewModel.Instance.ProductManager.CreateEmailProviderAdapter(account))
{
IReadOnlyList<Address> resolvedNames = emailProvider.ResolveName(searchText);
searchItems.AddRange(resolvedNames.Select(p => p.Email));
}
}
}
catch
(Exception ex)
{
ex.LogErrorDeep();
}
return
searchItems;
}
private
void
ProcessFilteredItems(List<
string
> items)
{
if
(ItemsFound !=
null
)
{
ItemsFound((
object
)
null
,
new
AsyncItemSearchEventArgs(items));
}
}
#endregion
}
And the code for the AutoCompleteBehaviour
public
class
EmailRadAutoCompleteBoxBehavior : DelayRadAutoCompleteBoxBehavior
{
#region Non-Public Methods
protected
override
void
OnAttached()
{
var autoComplete = AssociatedObject
as
RadAutoCompleteBox;
if
(autoComplete !=
null
)
{
autoComplete.KeyDown += OnAutoCompleteBoxKeyDown;
autoComplete.LostFocus += OnAutoCompleteBoxLostFocus;
autoComplete.SelectionChanged += OnSelectionChanged;
autoComplete.IsVisibleChanged += OnIsVisibleChanged;
autoComplete.MouseLeave += AutoCompleteOnMouseLeave;
}
}
private
void
AutoCompleteOnMouseLeave(
object
sender, MouseEventArgs mouseEventArgs)
{
var autoComplete = sender
as
RadAutoCompleteBox;
if
(autoComplete.IsDropDownOpen)
{
Point m = Mouse.GetPosition(autoComplete);
Point p = autoComplete.PointToScreen(m);
User32.LeftMouseClick(Convert.ToInt32(p.X), Convert.ToInt32(p.Y));
}
}
private
void
OnAutoCompleteBoxKeyDown(
object
sender, KeyEventArgs e)
{
var autoComplete = AssociatedObject
as
RadAutoCompleteBox;
if
(e.Key == Key.OemSemicolon)
{
if
(RegexExtensions.IsValidEmail(autoComplete.SearchText) && autoComplete.DataContext !=
null
)
{
var selectedItems =
new
ObservableCollection<
string
>(autoComplete.SelectedItems.Cast<
string
>())
{
autoComplete.SearchText
};
autoComplete.SelectedItems = selectedItems;
autoComplete.SearchText =
string
.Empty;
}
e.Handled =
true
;
//Debug.WriteLine(string.Format("EmailRadAutoCompleteBoxBehavior.OnAutoCompleteBoxKeyDown e.Key: {0} SearchText:{1}", e.Key, autoComplete.SearchText));
}
}
private
void
OnAutoCompleteBoxLostFocus(
object
sender, RoutedEventArgs e)
{
var autoComplete = AssociatedObject
as
RadAutoCompleteBox;
if
(RegexExtensions.IsValidEmail(autoComplete.SearchText) && autoComplete.DataContext !=
null
)
{
var selectedItems =
new
ObservableCollection<
string
>(autoComplete.SelectedItems.Cast<
string
>())
{
autoComplete.SearchText
};
autoComplete.SelectedItems = selectedItems;
autoComplete.SearchText =
string
.Empty;
//Debug.WriteLine(string.Format("EmailRadAutoCompleteBoxBehavior.OnAutoCompleteBoxLostFocus SearchText:{0}", autoComplete.SearchText));
}
}
protected
override
void
OnDetaching()
{
var autoComplete = AssociatedObject
as
RadAutoCompleteBox;
if
(autoComplete !=
null
)
{
autoComplete.KeyDown -= OnAutoCompleteBoxKeyDown;
autoComplete.LostFocus -= OnAutoCompleteBoxLostFocus;
autoComplete.IsVisibleChanged -= OnIsVisibleChanged;
autoComplete.MouseLeave -= AutoCompleteOnMouseLeave;
}
}
private
void
OnIsVisibleChanged(
object
sender, DependencyPropertyChangedEventArgs e)
{
var autoComplete = AssociatedObject
as
RadAutoCompleteBox;
if
(autoComplete.Visibility == Visibility.Visible)
{
autoComplete.SearchText =
string
.Empty;
//Debug.WriteLine(string.Format("EmailRadAutoCompleteBoxBehavior.OnIsVisibleChanged SearchText:{0}", autoComplete.SearchText));
}
}
private
void
OnSelectionChanged(
object
sender, SelectionChangedEventArgs e)
{
var autoComplete = AssociatedObject
as
RadAutoCompleteBox;
var selectedItems =
new
ObservableCollection<
string
>(autoComplete.SelectedItems.Cast<
string
>());
autoComplete.SelectedItems = selectedItems;
autoComplete.SearchText =
string
.Empty;
}
#endregion
}
Thank you for the provided code snippets.
I am afraid I am still unable to reproduce the described scenario on my side. I am attaching a sample project which shows the windows onscreen keyboard in the GotFocus event of the RadAutoCompleteBox. I was able to type in the RadAutoCompleteBox from the onscreen keyboard with a single click. Can you share what you are doing differently on your side? This way I will be able to investigate further.
Regards,
Vladimir Stoyanov
Progress Telerik

By default the RadAutoCompleteBox's drop down is closed when the mouse is captured away from the RadWatermarkTextBox which is inside it. This happens when the up/down keys of the on screen keyboard are pressed.
The only suggestion I can offer in order to prevent this is to handle the PreviewMouseDownEvent event of the RadAutocompleteBox. Then you can set the e.Handled="true" if the up/down button of the virtual keyboard was pressed and the dropdown is open. Please note that this would require knowing that the virtual keyboard's up/down keys were pressed in the PreviewMouseDownEvent.
Regards,
Vladimir Stoyanov
Progress Telerik

Vladimir, I am able to process successfully with PreviewMouseDownEvent and PreviewTouchDownEvent. I was able to check if the event occurs over our custom on screen keyboard, press the correct key and mark the event as handled.
Thanks for all the help.

Hi Damien,
Good to hear that you were able to resolve the issue.
I am facing same issue with mind fusion key board with rad auto complete. Can u please elaborate how did u solve this issue.
Thanks in advance
Regards,
MReddy

Thanks to Vladimir & Damien, your inputs helped me to resolve the issue.
Regards,
MReddy