none
WPF AutoComplete Dropodown RRS feed

  • Question

  • Hello,

    I have developed an autocomplete dropdown that is based on Textblock displaying the values relevant to keyup event from Textbox control above it. All is working fine, except that I cannot navigate with Up and Down keys through the TextBlock results. I have set the Textblock property to 'Focusable", but the only key it will fire on key-down event is "Tab" button. The MouseEnter, MouseOver are working fine(where I am defining user interaction with the results in TextBlock via mouse). I need to be able to use the key.Up and key.Down in a similar way. What can I do.

    Further notes: I have overcome many issues while developing this, and it seems to be working fine, but I cannot get the keyboard to work on Textblock(created programmatically).The results in Textblock are single line text values(this is working fine) and the Xaml associate with autocomplete control looks like this:

                    <StackPanel FocusManager.FocusedElement="{Binding ElementName=textBox}" > 

                        <TextBox GotMouseCapture="GotMouse_tb" MinWidth="280" MaxWidth="280"   Text="{Binding ProjectName}"  Padding="5, 3, 5, 2" Name="textBox" KeyUp="TextBox_KeyUp"  GotFocus="Tb_GotFocus" LostFocus="Tb_LostFocus"  PreviewMouseDown="Tb_Mouseup_text"    />

                        <Border MaxWidth="280"  MaxHeight="250" BorderBrush="DarkGray" BorderThickness="1" CornerRadius="3" Margin="0,-5,0,0">

                            <ScrollViewer  Opacity="1" Focusable="True" IsTabStop="True" VerticalScrollBarVisibility="Auto" Name="_scrollViewer"   MouseUp="Tb_Mouseup" Background="white" >

                                        <StackPanel Focusable="True"  Opacity="1" Name="resultStack"></StackPanel>    

      -----------textblock is created programmatically on key.up event  --------------------------------------------------

                            </ScrollViewer>

                        </Border>

                </StackPanel>


    Regards,


    Nemnaja Sovic

    Tuesday, February 11, 2020 5:02 PM

Answers

  • Hi,

    Do you want a control to get focus?

    this.Dispatcher.BeginInvoke(DispatcherPriority.Background,
    (Action)(() => { Keyboard.Focus(ControlName; }));

    Best Regards,

    Alex


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by Nemanja011 Monday, February 17, 2020 1:59 AM
    Thursday, February 13, 2020 9:30 AM
    Moderator

All replies

  • Hi,

    Do you want an auto-filled Textbox like this?

      public partial class AutoCompleteTextBox : Canvas
        {
            
            private VisualCollection controls;
            private TextBox textBox;
            private ComboBox comboBox;
            private ObservableCollection<AutoCompleteEntry> autoCompletionList;
            private Timer keypressTimer;
            private delegate void TextChangedCallback();
            private bool insertText;
            private int delayTime;
            private int searchThreshold;
    
           
    
    
            public AutoCompleteTextBox()
            {
                controls = new VisualCollection(this);
    
    
                autoCompletionList = new ObservableCollection<AutoCompleteEntry>();
                searchThreshold = 0;        // default threshold to 2 char
                delayTime = 100;
    
                // set up the key press timer
                keypressTimer = new System.Timers.Timer();
                keypressTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent);
    
                // set up the text box and the combo box
                comboBox = new ComboBox();
                comboBox.IsSynchronizedWithCurrentItem = true;
                comboBox.IsTabStop = false;
                Panel.SetZIndex(comboBox, -1);
                comboBox.SelectionChanged += new SelectionChangedEventHandler(comboBox_SelectionChanged);
    
                textBox = new TextBox();
                textBox.TextChanged += new TextChangedEventHandler(textBox_TextChanged);
                textBox.GotFocus += new RoutedEventHandler(textBox_GotFocus);
                textBox.KeyUp += new KeyEventHandler(textBox_KeyUp);
                textBox.KeyDown += new KeyEventHandler(textBox_KeyDown);
                textBox.VerticalContentAlignment = VerticalAlignment.Center;
    
                controls.Add(comboBox);
                controls.Add(textBox);
            }
    
     
    
       
    
            public string Text
            {
                get { return textBox.Text; }
                set
                {
                    insertText = true;
                    textBox.Text = value;
                }
            }
    
            public int DelayTime
            {
                get { return delayTime; }
                set { delayTime = value; }
            }
    
            public int Threshold
            {
                get { return searchThreshold; }
                set { searchThreshold = value; }
            }
    
     
            public void AddItem(AutoCompleteEntry entry)
            {
                autoCompletionList.Add(entry);
            }
    
    
            public void ClearItem()
            {
                autoCompletionList.Clear();
            }
    
            private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                if (null != comboBox.SelectedItem)
                {
                    insertText = true;
                    ComboBoxItem cbItem = (ComboBoxItem)comboBox.SelectedItem;
                    textBox.Text = cbItem.Content.ToString();
                }
            }
    
            private void TextChanged()
            {
                try
                {
                    comboBox.Items.Clear();
                    if (textBox.Text.Length >= searchThreshold)
                    {
                        foreach (AutoCompleteEntry entry in autoCompletionList)
                        {
                            foreach (string word in entry.KeywordStrings)
                            {
                                if (word.Contains(textBox.Text))
                                {
                                    ComboBoxItem cbItem = new ComboBoxItem();
                                    cbItem.Content = entry.ToString();
                                    comboBox.Items.Add(cbItem);
                                    break;
                                }
                                //if (word.StartsWith(textBox.Text, StringComparison.CurrentCultureIgnoreCase))
                                //{
                                //    ComboBoxItem cbItem = new ComboBoxItem();
                                //    cbItem.Content = entry.ToString();
                                //    comboBox.Items.Add(cbItem);
                                //    break;
                                //}
                            }
                        }
                        comboBox.IsDropDownOpen = comboBox.HasItems;
                    }
                    else
                    {
                        comboBox.IsDropDownOpen = false;
                    }
                }
                catch { }
            }
    
            private void OnTimedEvent(object source, System.Timers.ElapsedEventArgs e)
            {
                keypressTimer.Stop();
                Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                    new TextChangedCallback(this.TextChanged));
            }
    
            private void textBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                // text was not typed, do nothing and consume the flag
                if (insertText == true) insertText = false;
    
                // if the delay time is set, delay handling of text changed
                else
                {
                    if (delayTime > 0)
                    {
                        keypressTimer.Interval = delayTime;
                        keypressTimer.Start();
                    }
                    else TextChanged();
                }
            }
    
            //获得焦点时
            public void textBox_GotFocus(object sender, RoutedEventArgs e)
            {
                // text was not typed, do nothing and consume the flag
                if (insertText == true) insertText = false;
    
                // if the delay time is set, delay handling of text changed
                else
                {
                    if (delayTime > 0)
                    {
                        keypressTimer.Interval = delayTime;
                        keypressTimer.Start();
                    }
                    else TextChanged();
                }
            }
    
            public void textBox_KeyDown(object sender, KeyEventArgs e)
            {
                if (textBox.IsInputMethodEnabled == true)
                {
                    comboBox.IsDropDownOpen = false;
                }
            }
    
       
            public void textBox_KeyUp(object sender, KeyEventArgs e)
            {
                if (e.Key == Key.Down && comboBox.IsDropDownOpen == true)
                {
                    comboBox.Focus();
                }
            }
    
            protected override Size ArrangeOverride(Size arrangeSize)
            {
                textBox.Arrange(new Rect(arrangeSize));
                comboBox.Arrange(new Rect(arrangeSize));
                return base.ArrangeOverride(arrangeSize);
            }
    
            protected override Visual GetVisualChild(int index)
            {
                return controls[index];
            }
    
            protected override int VisualChildrenCount
            {
                get { return controls.Count; }
            }
    
    
        }
        public class AutoCompleteEntry
        {
            private string[] keywordStrings;
            private string displayString;
    
            public string[] KeywordStrings
            {
                get
                {
                    if (keywordStrings == null)
                    {
                        keywordStrings = new string[] { displayString };
                    }
                    return keywordStrings;
                }
            }
    
            public string DisplayName
            {
                get { return displayString; }
                set { displayString = value; }
            }
    
            public AutoCompleteEntry(string name, params string[] keywords)
            {
                displayString = name;
                keywordStrings = keywords;
            }
    
            public override string ToString()
            {
                return displayString;
            }
        }

    Best Regards,

    Alex


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, February 12, 2020 3:39 AM
    Moderator
  • Hi.

    Thank you.

    I have that behavior but my code is different then yours. The only thing that is NOT working is a keyboard scrolling through the results. I have realized that pressing tab key TWICE in the textbox focuses on the textblock control  and lets me scroll through the items, which is great. How do I get "tab clicking" via code from the textbox to propagate down to Scrollbar then TextBlock control.

    Pseudo:

    Key.Down in textbox --> Click tab twice and focus on textBlock(control that displays results) - at this point keyboard works.

    Xaml Structure:

     <TextBox ...   />
                        <Border...>
                            <ScrollViewer ...>
                                <StackPanel ></StackPane...l>
                                    ---<Textblock> added through the code dynamically
                            </ScrollViewer>
                        </Border>


    Nemnaja Sovic

    Wednesday, February 12, 2020 5:49 PM
  • Hi,

    Do you want a control to get focus?

    this.Dispatcher.BeginInvoke(DispatcherPriority.Background,
    (Action)(() => { Keyboard.Focus(ControlName; }));

    Best Regards,

    Alex


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by Nemanja011 Monday, February 17, 2020 1:59 AM
    Thursday, February 13, 2020 9:30 AM
    Moderator
  • HI Alex,

    Thank you.

    I still have a small issue that is breaking the control. Key down event is handled, but since I am dealing with embedded controls -  to get the correct behavior, I need the code on key down to simulate another key down key press in order to highlight the first option in ensuing textblock control. As you can see, I am setting focus on the scrollviwer, if I physically press key.down - I get the correct behavior - but the latter part of the code, that is supposed to programmatically key press is not working. Please let me know how can I press.Key down using code to get this to work

     case Key.Down:
                        {                      

                            string value_text = textbox.Text;
                        
                            this.Dispatcher.BeginInvoke(DispatcherPriority.Background,
                           (Action)(() => { Keyboard.Focus(resultStack); }));  // working, as it focuses 

                              KeyEventArgs args = new KeyEventArgs(Keyboard.PrimaryDevice,  // not working

                                 Keyboard.PrimaryDevice.ActiveSource, 0, Key.Down);

                                args.RoutedEvent = Keyboard.KeyDownEvent;

                             InputManager.Current.ProcessInput(args); }


    Nemnaja Sovic

    Saturday, February 15, 2020 12:12 AM