Binding exceptions with AutoCompleteBox and highlighted text block, datagrid selection adapter

Mar 11, 2011 at 5:29 PM
Edited Mar 11, 2011 at 6:46 PM

We actually created our own HightlighedTextBlock before I found out about Jeff Wilcox's, but it is very similar.

We are using the HightlightedTextBlock in WPF and binding to the SearchText property with XAML; note we are using the DataGridSelectionAdapter to show results in a grid -- we do not seem to get the problem with the default ListBox:

 

        <DataTemplate x:Key="AutoCompleteConfirmItemTemplate">

            <StackPanel Orientation="Horizontal">

                <controls:HighlightedTextBlock

                                 Highlight="{Binding Path=SearchText,RelativeSource={RelativeSource AncestorType={x:Type wpftoolkit:AutoCompleteBox}}}"

                                 RawText="{Binding Path=Name}" 

                                  />

            </StackPanel>

        </DataTemplate>

 

Note that in our HightlightedTextBlock, SearchText is WHAT to highlight, and RawText is the text that we are displaying. The problem appears to be with our binding and virtualization (which we need, since we have thousands of items, so we do not want to turn this off). We find that we get many exceptions such as these, and when we scroll through, many items will not have the proper highlighting accordingly:

 

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='X.Client.Controls.AutoCompleteBoxEx', AncestorLevel='1''. BindingExpression:Path=SearchText; DataItem=null; target element is 'HighlightedTextBlock' (Name=''); target property is 'Highlight' (type 'String')

 

Any ideas on how we should properly implement binding to SearchText taking into account virtualization?

 

Again, we are using the DataGridSelectionAdapter. 

Any thoughts on how to resolve these?

 

The exceptions seems to be related to performance problems with the control (they slow it down).

 

We would happily pay a vendor for a supported control that fixed these issues, but we haven’t found one yet. We would very much appreciate any help anyone might be able to provide.

 

For reference, here is our implementation of a HightlightedTextBlock:

 

    public class HighlightedTextBlock : TextBlock
    {
        public string Highlight
        {
            get { return (string)this.GetValue(HighlightProperty); }
            set { this.SetValue(HighlightProperty, value); }
        }
        public static readonly DependencyProperty HighlightProperty = DependencyProperty.Register(
          "Highlight"typeof(string), typeof(HighlightedTextBlock), new PropertyMetadata(string.Empty, OnTextOrHighlightChanged));
 
        public string RawText
        {
            get { return (string)this.GetValue(RawTextProperty); }
            set { this.SetValue(RawTextProperty, value); }
        }
        public static readonly DependencyProperty RawTextProperty = DependencyProperty.Register(
          "RawText"typeof(string), typeof(HighlightedTextBlock), new PropertyMetadata(string.Empty, OnTextOrHighlightChanged));
 
 
        static HighlightedTextBlock()
        {
            //TextBlock.TextProperty.AddOwner(typeof(HighlightedTextBlock), new FrameworkPropertyMetadata(string.Empty, OnTextOrHighlightChanged));
            DefaultStyleKeyProperty.OverrideMetadata(typeof(HighlightedTextBlock), new FrameworkPropertyMetadata(typeof(HighlightedTextBlock)));
        }
 
        private static void OnTextOrHighlightChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            HighlightedTextBlock textBlock = (HighlightedTextBlock)obj;
            textBlock.Inlines.Clear();
 
            string _matchPre = string.Empty;
            string _match = string.Empty;
            string _matchPost = string.Empty;
            string _highlight = textBlock.Highlight;
            string _text = textBlock.RawText; // textBlock.Text;
            int index = -1;
 
            if (_highlight != null && _text != null)
                index =  _text.IndexOf(_highlight, StringComparison.CurrentCultureIgnoreCase);
 
            if (index < 0)
                _matchPre = _text;
            else
            {
                _matchPre = _text.Substring(0, index);
                _match = _text.Substring(index, _highlight.Length);
                _matchPost = _text.Substring(index + _highlight.Length);
            }
 
            if ( _matchPre != null && _matchPre.Length > 0)
                textBlock.Inlines.Add(new Run(_matchPre));
            if ( _match != null && _match.Length > 0)
                textBlock.Inlines.Add(new Run(_match) { FontWeight = FontWeights.Bold });
            if (_matchPost != null && _matchPost.Length > 0)
                textBlock.Inlines.Add(new Run(_matchPost));
        }
 
    }

 

We experience the problem with both AutoCompleteBox and AutoCompleteBoxEx, which is a slight variation to allow the arrows keys to scroll through the default ListBox and also to allow the user to start typing after hitting an Access Key if the AutoCompleteBox is the Target of a label:

 

    public class AutoCompleteBoxEx : AutoCompleteBox
    {
        private bool _selectorIsListBox = false;
        private bool _selectorSelectionChangedHandlerRegisterd = false;
        private const int selectorMaxHeight = 250;
        private TextBox _textBox;
        private ListBox _selector;
 
        public AutoCompleteBoxEx()
        {
            this.Loaded +=new System.Windows.RoutedEventHandler(AutoCompleteBoxEx_Loaded);            
        }
 
        protected override void OnGotFocus(System.Windows.RoutedEventArgs e)
        {
            base.OnGotFocus(e);
 
            if (_textBox == null)
                _textBox = Template.FindName("Text"thisas TextBox;
 
            if (_textBox != null)
            {
                _textBox.Focus();
            }
        }
 
        protected void  AutoCompleteBoxEx_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            if (_selector == null)
            {
                _selector = Template.FindName("Selector"thisas ListBox;
 
                if (_selector != null)
                    _selectorIsListBox = true;
            }
 
            if (!_selectorSelectionChangedHandlerRegisterd && _selectorIsListBox && _selector != null)
            {
                _selector.SelectionChanged += listBox_SelectionChanged;
                _selector.MaxHeight = selectorMaxHeight;
                _selectorSelectionChangedHandlerRegisterd = true;
            }
        }
        
        void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ListBox box = ((ListBox)sender); 
            box.ScrollIntoView(box.SelectedItem); 
            e.Handled = true
        }
    }