Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Here's how I would go about creating this control. </p> <h2>High Level Overview:</h2> <p>This control will contain the following main components: (1) an <code>AutoCompleteTextBox</code> or <code>AutoCompleteComboBox</code> (2) the "button" control that you describe (3) A UI collection to hold applied tags. The <code>AutoCompleteTextBox</code> and the collection to hold applied tags would be positioned ahead of time in a layout container of your choice.</p> <p>First, we can leverage an <code>AutoCompleteTextBox</code> or <code>AutoCompleteComboBox</code> to give us the Intellisense-style options as the user types. Next, we "listen" for the user selecting a presented tag from the drop-down list and dynamically create a new "button" control (we could create a UserControl/CustomControl for it ahead of time but we'd need to "new" one up at least). The "button" will contain as its text the text of the <code>AutoCompleteTextBox</code>. Finally, we insert the new "button" into a ListBox (or other relavant UI collection type) that holds all the currently applied tags.</p> <h2>Details:</h2> <p>There are a few <code>AutoCompleteTextBox</code> controls out there, but I'll describe how you could use <a href="http://wpfactb.codeplex.com/releases/view/42340" rel="nofollow noreferrer"><strong>this CodeProject one</strong></a>. This sample project shows how you can use either a regular <code>TextBox</code>, an <code>AutoCompleteComboBox</code> or <code>AutoCompleteTextBox</code> to achieve the intellisense-style options. The core pieces of this project are really the <code>AutoCompleteManager</code> and a DataProvider, of type <code>IAutoCompleteDataProvider</code>, along with the <code>IAutoAppendDataProvider</code>.</p> <p>Before describing further details, here's some screenshots of this <code>AutoCompleteTextBox</code> control in action (note I'm using a different Style for the ListBoxItems than the original author supplies). The <code>AutoAppend</code> property of this control is a nice touch (it is turned on for this example so after I start typing the current match automatically "finishes" my word for me). After typing just an "I":</p> <p><img src="https://i.stack.imgur.com/OZo2V.jpg" alt="After first typing a letter."></p> <p>After hovering my mouse over "Indiana": </p> <p><img src="https://i.stack.imgur.com/yDT2w.jpg" alt="After hovering mouse over &quot;Indiana&quot;"></p> <p>After clicking on "Indiana":</p> <p><img src="https://i.stack.imgur.com/KbjSF.jpg" alt="After clicking on &quot;Indiana&quot;"></p> <p>Since the code from this project handles the drop-down options for us, we now need to "listen" for when the user selects an item from the drop-down list and create the new "button" control accordingly. There are <strong>two main cases</strong> that I'm thinking of for this that we need to handle. </p> <p><strong>The first case</strong> is when the user selects an item from the list with their mouse. To handle this, we could insert the code to create the new "button" control in the <code>MouseLeftButtonUp</code> handler in the <code>AutoCompleteManager.cs</code>, which is around line 451:</p> <pre><code> private void ListBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { ListBoxItem item = null; var d = e.OriginalSource as DependencyObject; while (d != null) { if (d is ListBoxItem) { item = d as ListBoxItem; break; } d = VisualTreeHelper.GetParent(d); } if (item != null) { _popup.IsOpen = false; UpdateText(item.Content as string, true); // User has selected an item with the mouse... // ** Add your new code HERE... something like: // // TagButton tagButton = new TagButton(_textBox.Text); // _textBox is the TextBox to which the AutoCompleteManager has been applied // _autoCompleteTagControl.TagContainer.Add(tagButton); // _autoCompleteTagControl would be the control that we're making... it contains out other controls - I'm assuming we've passed it in or made it available. } } </code></pre> <p><strong>The second case</strong> is when the user selects an item from the list by hitting the enter key. To handle this we could insert similar new code in the <code>TextBox_PreviewKeyDown</code> handler in <code>AutoCompleteManager.cs</code>, around line 291:</p> <pre><code> private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e) { _supressAutoAppend = e.Key == Key.Delete || e.Key == Key.Back; if (!_popup.IsOpen) { return; } if (e.Key == Key.Enter) { _popup.IsOpen = false; _textBox.SelectAll(); // User has selected an item by hitting the enter key... // ** Add your new code HERE to create new TagButton, etc. } // ... } </code></pre> <h2>Fine-Print Details</h2> <p>If you decide to use the <code>AutoCompleteTextBox</code> from the CodeProject that I mentioned, you may need to apply a couple of fixes. I ran into two small things after I imported everything into a larger project (they did not happen when just running the included sample project). The first one was this Binding error:</p> <pre><code>System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'ListBoxItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment') </code></pre> <p>Others have experienced this problem with ListBoxes, described <strong><a href="https://stackoverflow.com/questions/160391/listbox-with-grid-as-itemspaneltemplate-produces-weird-binding-errors?answertab=votes#tab-top">here</a></strong>. As one of the answers to that post suggests, I was able to include an explicit style setting for the HorizontalContentAligment and VerticalContentAlignment in my Style to solve this problem.</p> <p>The second issue occurred after the <code>AutoCompleteTextBox</code> was in an app that included tabs. When you have controls in a TabControl, the nested controls will get their Loaded event raised quite often - at least once for every time that the tab that contains the control is clicked on. This caused unexpected extra calls to the <code>AutoCompleteManager's AttachTextBox()</code> method, causing <code>debug.Assert()</code> to fail and exceptions to occur (the original author was assuming that the Loaded event would only be raised one time). So far the only fix I needed to do to handle that has been in <code>AutoCompeteTextBox.cs</code>. I just added an <code>_isInitialized</code> flag to ensure that <code>AttachTextBox</code> only gets called once:</p> <pre><code> void AutoCompleteTextBox_Loaded(object sender, RoutedEventArgs e) { if (! _isInitialized) { _acm.AttachTextBox(this); _isInitialized = true; } } </code></pre> <p>Using this approach should allow you to create a control that behaves like what you describe.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload