Code:
                         / 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Controls / TextSearch.cs / 1305600 / TextSearch.cs
                        
                        
                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 
using System;
using System.Diagnostics; 
using System.Windows; 
using System.Windows.Threading;
using System.Windows.Data; 
using System.ComponentModel;
using System.Windows.Input;
using System.Collections; 
using MS.Win32;
using System.Globalization; 
using System.Windows.Controls; 
using System.Windows.Controls.Primitives;
using System.Windows.Markup;    // for XmlLanguage 
using System.Windows.Media;
using System.Text;
using System.Collections.Generic;
using MS.Internal; 
using MS.Internal.Data;
 
namespace System.Windows.Controls 
{
    // 
 
 
 
    ///  
    ///     Text Search is a feature that allows the user to quickly access items in a set by typing prefixes of the strings.
    ///  
    public sealed class TextSearch : DependencyObject
 	{ 
        /// 
        ///     Make a new TextSearch instance attached to the given object. 
        ///     Create the instance in the same context as the given DO. 
        ///  
        ///  
        private TextSearch(ItemsControl itemsControl)
        {
            if (itemsControl == null)
            { 
                throw new ArgumentNullException("itemsControl");
            } 
 
            _attachedTo = itemsControl;
 
            ResetState();
        }
        ///  
        ///     Get the instance of TextSearch attached to the given ItemsControl or make one and attach it if it's not.
        ///   
        ///  
        ///  
        ///     Attached property to indicate which property on the item in the items collection to use for the "primary" text, 
        ///     or the text against which to search.
        ///   
        public static readonly DependencyProperty TextPathProperty
            = DependencyProperty.RegisterAttached("TextPath", typeof(string), typeof(TextSearch),
                                                  new FrameworkPropertyMetadata(String.Empty /* default value */));
 
        /// 
        ///     Writes the attached property to the given element. 
        ///   
        /// 
        ///  
        public static void SetTextPath(DependencyObject element, string path)
        {
            if (element == null)
            { 
                throw new ArgumentNullException("element");
            } 
 
            element.SetValue(TextPathProperty, path);
        } 
        /// 
        ///     Reads the attached property from the given element.
        ///   
        /// 
        ///  
        ///     Attached property to indicate the value to use for the "primary" text of an element.
        ///  
        public static readonly DependencyProperty TextProperty
            = DependencyProperty.RegisterAttached("Text", typeof(string), typeof(TextSearch), 
                                                  new FrameworkPropertyMetadata((string)String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
 
        ///  
        ///     Writes the attached property to the given element.
        ///   
        /// 
        /// 
        public static void SetText(DependencyObject element, string text)
        { 
            if (element == null)
            { 
                throw new ArgumentNullException("element"); 
            }
 
            element.SetValue(TextProperty, text);
        }
        ///  
        ///     Reads the attached property from the given element.
        ///   
        ///  
        ///  
        ///     Prefix that is currently being used in the algorithm. 
        ///  
        private static readonly DependencyProperty CurrentPrefixProperty = 
            DependencyProperty.RegisterAttached("CurrentPrefix", typeof(string), typeof(TextSearch),
                                                new FrameworkPropertyMetadata((string)null));
        ///  
        ///     If TextSearch is currently active.
        ///   
        private static readonly DependencyProperty IsActiveProperty = 
            DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(TextSearch),
                                                new FrameworkPropertyMetadata(false)); 
        #endregion
        #region Private Properties 
        ///  
        ///     The key needed set a read-only property. 
        ///  
        private static readonly DependencyPropertyKey TextSearchInstancePropertyKey = 
            DependencyProperty.RegisterAttachedReadOnly("TextSearchInstance", typeof(TextSearch), typeof(TextSearch),
                                                new FrameworkPropertyMetadata((object)null /* default value */));
        ///  
        ///     Instance of TextSearch -- attached property so that the instance can be stored on the element
        ///     which wants the service. 
        ///   
        private static readonly DependencyProperty TextSearchInstanceProperty =
            TextSearchInstancePropertyKey.DependencyProperty; 
        // used to retrieve the value of an item, according to the TextPath
        private static readonly BindingExpressionUncommonField TextValueBindingExpression = new BindingExpressionUncommonField(); 
        #endregion 
 
        #region Private Methods
 
        /// 
        ///     Called by consumers of TextSearch when a TextInput event is received
        ///     to kick off the algorithm.
        ///   
        /// 
        /// 
        ///     Called when the user presses backspace.
        ///   
        /// 
        ///     Searches through the given itemCollection for the first item matching the given prefix. 
        ///  
        ///  
        ///     ------------------------------------------------------------------------- 
        ///     Incremental Type Search algorithm
        ///     ------------------------------------------------------------------------- 
        ///
        ///     Given a prefix and new character, we loop through all items in the collection
        ///     and look for an item that starts with the new prefix.  If we find such an item,
        ///     select it.  If the new character is repeated, we look for the next item after 
        ///     the current one that begins with the old prefix**.  We can optimize by
        ///     performing both of these searches in parallel. 
        /// 
        ///     **NOTE: Win32 will only do this if the old prefix is of length 1 - in other
        ///             words, first-character-only matching.  The algorithm described here 
        ///             is an extension of ITS as implemented in Win32.  This variant was
        ///             described to me by JeffBog as what was done in AFC - but I have yet
        ///             to find a listbox which behaves this way.
        /// 
        ///     --------------------------------------------------------------------------
        ///   
        /// Item that matches the given prefix  
        private static int FindMatchingPrefix(ItemsControl itemsControl, string primaryTextPath, string prefix,
                                               string newChar, int startItemIndex, bool lookForFallbackMatchToo, ref bool wasNewCharUsed) 
        {
            ItemCollection itemCollection = itemsControl.Items;
            // Using indices b/c this is a better way to uniquely 
            // identify an element in the collection.
            int matchedItemIndex = -1; 
            int fallbackMatchIndex = -1; 
            int count = itemCollection.Count; 
            // Return immediately with no match if there were no items in the view.
            if (count == 0)
            { 
                return -1;
            } 
 
            string newPrefix = prefix + newChar;
 
            // With an empty prefix, we'd match anything
            if (String.IsNullOrEmpty(newPrefix))
            {
                return -1; 
            }
 
            // Hook up the binding we will apply to each object.  Get the 
            // PrimaryTextPath off of the attached instance and then make
            // a binding with that path. 
            BindingExpression primaryTextBinding = null;
            object item0 = itemsControl.Items[0]; 
            bool useXml = AssemblyHelper.IsXmlNode(item0);
 
            if (useXml || !String.IsNullOrEmpty(primaryTextPath)) 
            {
                primaryTextBinding = CreateBindingExpression(itemsControl, item0, primaryTextPath); 
                TextValueBindingExpression.SetValue(itemsControl, primaryTextBinding);
            }
            bool firstItem = true;
 
            wasNewCharUsed = false;
 
            CultureInfo cultureInfo = GetCulture(itemsControl); 
            // ISSUE: what about changing the collection while this is running? 
            for (int currentIndex = startItemIndex; currentIndex < count; )
            {
                object item = itemCollection[currentIndex];
 
                if (item != null)
                { 
                    string itemString = GetPrimaryText(item, primaryTextBinding, itemsControl); 
                    bool isTextSearchCaseSensitive = itemsControl.IsTextSearchCaseSensitive;
 
                    // See if the current item matches the newPrefix, if so we can
                    // stop searching and accept this item as the match.
                    if (itemString != null && itemString.StartsWith(newPrefix, !isTextSearchCaseSensitive, cultureInfo))
                    { 
                        // Accept the new prefix as the current prefix.
                        wasNewCharUsed = true; 
                        matchedItemIndex = currentIndex; 
                        break;
                    } 
                    // Find the next string that matches the last prefix.  This
                    // string will be used in the case that the new prefix isn't
                    // matched. This enables pressing the last character multiple 
                    // times and cylcing through the set of items that match that
                    // prefix. 
                    // 
                    // Unlike the above search, this search must start *after*
                    // the currently selected item.  This search also shouldn't 
                    // happen if there was no previous prefix to match against
                    if (lookForFallbackMatchToo)
                    {
                        if (!firstItem && prefix != String.Empty) 
                        {
                            if (itemString != null) 
                            { 
                                if (fallbackMatchIndex == -1 && itemString.StartsWith(prefix, !isTextSearchCaseSensitive, cultureInfo))
                                { 
                                    fallbackMatchIndex = currentIndex;
                                }
                            }
                        } 
                        else
                        { 
                            firstItem = false; 
                        }
                    } 
                }
                // Move next and wrap-around if we pass the end of the container.
                currentIndex++; 
                if (currentIndex >= count)
                { 
                    currentIndex = 0; 
                }
 
                // Stop where we started but only after the first pass
                // through the loop -- we should process the startItem.
                if (currentIndex == startItemIndex)
                { 
                    break;
                } 
            } 
            if (primaryTextBinding != null) 
            {
                // Clean up the binding for the primary text path.
                TextValueBindingExpression.ClearValue(itemsControl);
            } 
            // In the case that the new prefix didn't match anything and 
            // there was a fallback match that matched the old prefix, move 
            // to that one.
            if (matchedItemIndex == -1 && fallbackMatchIndex != -1) 
            {
                matchedItemIndex = fallbackMatchIndex;
            }
 
            return matchedItemIndex;
        } 
 
        /// 
        ///     Helper function called by Editable ComboBox to search through items. 
        ///  
        internal static int FindMatchingPrefix(ItemsControl itemsControl, string prefix)
        {
            bool wasNewCharUsed = false; 
            return FindMatchingPrefix(itemsControl, GetPrimaryTextPath(itemsControl), 
                                      prefix, String.Empty, 0, false, ref wasNewCharUsed); 
        }
 
        private void ResetTimeout()
        {
            // Called when we get some input. Start or reset the timer.
            // Queue an inactive priority work item and set its deadline. 
            if (_timeoutTimer == null)
            { 
                _timeoutTimer = new DispatcherTimer(DispatcherPriority.Normal); 
                _timeoutTimer.Tick += new EventHandler(OnTimeout);
            } 
            else
            {
                _timeoutTimer.Stop();
            } 
            // Schedule this operation to happen a certain number of milliseconds from now. 
            _timeoutTimer.Interval = TimeOut; 
            _timeoutTimer.Start();
        } 
        private void AddCharToPrefix(string newChar)
        {
            Prefix += newChar; 
            _charsEntered.Add(newChar);
        } 
 
        private static string GetPrimaryTextPath(ItemsControl itemsControl)
        { 
            string primaryTextPath = (string)itemsControl.GetValue(TextPathProperty);
            if (String.IsNullOrEmpty(primaryTextPath))
            { 
                primaryTextPath = itemsControl.DisplayMemberPath;
            } 
            return primaryTextPath; 
        }
 
        private static string GetPrimaryText(object item, BindingExpression primaryTextBinding, DependencyObject primaryTextBindingHome)
        {
            // Order of precedence for getting Primary Text is as follows:
            // 
            // 1) PrimaryText
            // 2) PrimaryTextPath (TextSearch.TextPath or ItemsControl.DisplayMemberPath) 
            // 3) GetPlainText() 
            // 4) ToString()
 
            DependencyObject itemDO = item as DependencyObject;
            if (itemDO != null)
            { 
                string primaryText = (string)itemDO.GetValue(TextProperty);
 
                if (!String.IsNullOrEmpty(primaryText)) 
                {
                    return primaryText; 
                }
            }
            // Here hopefully they've supplied a path into their object which we can use. 
            if (primaryTextBinding != null && primaryTextBindingHome != null)
            { 
                // Take the binding that we hooked up at the beginning of the search 
                // and apply it to the current item.  Then, read the value of the
                // ItemPrimaryText property (where the binding actually lives). 
                // Try to convert the resulting object to a string.
                primaryTextBinding.Activate(item);
                object primaryText = primaryTextBinding.Value; 
                return ConvertToPlainText(primaryText); 
            } 
            return ConvertToPlainText(item); 
        }
        private static string ConvertToPlainText(object o)
        { 
            FrameworkElement fe = o as FrameworkElement;
 
            // Try to return FrameworkElement.GetPlainText() 
            if (fe != null)
            { 
                string text = fe.GetPlainText();
                if (text != null)
                { 
                    return text;
                } 
            } 
            // Try to convert the item to a string 
            return (o != null) ? o.ToString() : String.Empty;
        }
        ///  
        ///     Internal helper method that uses the same primary text lookup steps but doesn't require
        ///     the user passing in all of the bindings that we need. 
        ///   
        /// 
        ///  
        /// (10);
            } 
            else 
            {
                _charsEntered.Clear(); 
            }
            if(_timeoutTimer != null)
            { 
                _timeoutTimer.Stop();
            } 
            _timeoutTimer = null; 
        } 
        /// 
        ///     Time until the search engine resets.
        ///   
        private TimeSpan TimeOut
        { 
            get 
            {
                // NOTE: NtUser does the following (file: windows/ntuser/kernel/sysmet.c) 
                //     gpsi->dtLBSearch = dtTime * 4;            // dtLBSearch   =  4  * gdtDblClk
                //     gpsi->dtScroll = gpsi->dtLBSearch / 5;  // dtScroll     = 4/5 * gdtDblClk
                //
                // 4 * DoubleClickSpeed seems too slow for the search 
                // So for now we'll do 2 * DoubleClickSpeed
 
                return TimeSpan.FromMilliseconds(SafeNativeMethods.GetDoubleClickTime() * 2); 
            }
        } 
        #endregion
        #region Testing API 
        // Being that this is a time-sensitive operation, it's difficult 
        // to get the timing right in a DRT.  I'll leave input testing up to BVTs here 
        // but this internal API is for the DRT to do basic coverage.
        private static TextSearch GetInstance(DependencyObject d) 
        {
            return EnsureInstance(d as ItemsControl);
        }
 
        private void TypeAKey(string c)
        { 
            DoSearch(c); 
        }
 
        private void CauseTimeOut()
        {
            if (_timeoutTimer != null)
            { 
                _timeoutTimer.Stop();
                OnTimeout(_timeoutTimer, EventArgs.Empty); 
            } 
        }
 
        internal string GetCurrentPrefix()
        {
            return Prefix;
        } 
        #endregion 
 
        #region Internal Accessibility API 
        internal static string GetPrimaryText(FrameworkElement element)
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element"); 
            } 
            string text = (string)element.GetValue(TextProperty); 
            if (text != null && text != String.Empty)
            {
                return text; 
            }
 
            return element.GetPlainText(); 
        }
 
        #endregion
        #region Private Fields
 
        private string Prefix
        { 
            get { return _prefix; } 
            set
            { 
                _prefix = value;
#if DEBUG
                // Also need to invalidate the property CurrentPrefixProperty on the instance to which we are attached. 
                Debug.Assert(_attachedTo != null);
 
                _attachedTo.SetValue(CurrentPrefixProperty, _prefix); 
#endif
            } 
        }
        private bool IsActive
        { 
            get { return _isActive; }
            set 
            { 
                _isActive = value;
 
#if DEBUG
                Debug.Assert(_attachedTo != null);
                _attachedTo.SetValue(IsActiveProperty, _isActive); 
#endif
            } 
        } 
        private int MatchedItemIndex 
        {
            get { return _matchedItemIndex; }
            set
            { 
                _matchedItemIndex = value;
            } 
        } 
        private static CultureInfo GetCulture(DependencyObject element) 
        {
            object o = element.GetValue(FrameworkElement.LanguageProperty);
            CultureInfo culture = null;
 
            if (o != null)
            { 
                XmlLanguage language = (XmlLanguage) o; 
                try
                { 
                    culture = language.GetSpecificCulture();
                }
                catch (InvalidOperationException)
                { 
                }
            } 
 
            return culture;
        } 
        // Element to which this TextSearch instance is attached.
        private ItemsControl _attachedTo;
 
        // String of characters matched so far.
        private string _prefix; 
 
        private List _charsEntered;
 
        private bool _isActive;
        private int _matchedItemIndex;
 
        private DispatcherTimer _timeoutTimer;
 
        #endregion 
    }
} 
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 
using System;
using System.Diagnostics; 
using System.Windows; 
using System.Windows.Threading;
using System.Windows.Data; 
using System.ComponentModel;
using System.Windows.Input;
using System.Collections; 
using MS.Win32;
using System.Globalization; 
using System.Windows.Controls; 
using System.Windows.Controls.Primitives;
using System.Windows.Markup;    // for XmlLanguage 
using System.Windows.Media;
using System.Text;
using System.Collections.Generic;
using MS.Internal; 
using MS.Internal.Data;
 
namespace System.Windows.Controls 
{
    // 
 
 
 
    ///  
    ///     Text Search is a feature that allows the user to quickly access items in a set by typing prefixes of the strings.
    ///  
    public sealed class TextSearch : DependencyObject
 	{ 
        /// 
        ///     Make a new TextSearch instance attached to the given object. 
        ///     Create the instance in the same context as the given DO. 
        ///  
        ///  
        private TextSearch(ItemsControl itemsControl)
        {
            if (itemsControl == null)
            { 
                throw new ArgumentNullException("itemsControl");
            } 
 
            _attachedTo = itemsControl;
 
            ResetState();
        }
        ///  
        ///     Get the instance of TextSearch attached to the given ItemsControl or make one and attach it if it's not.
        ///   
        ///  
        ///  
        ///     Attached property to indicate which property on the item in the items collection to use for the "primary" text, 
        ///     or the text against which to search.
        ///   
        public static readonly DependencyProperty TextPathProperty
            = DependencyProperty.RegisterAttached("TextPath", typeof(string), typeof(TextSearch),
                                                  new FrameworkPropertyMetadata(String.Empty /* default value */));
 
        /// 
        ///     Writes the attached property to the given element. 
        ///   
        /// 
        ///  
        public static void SetTextPath(DependencyObject element, string path)
        {
            if (element == null)
            { 
                throw new ArgumentNullException("element");
            } 
 
            element.SetValue(TextPathProperty, path);
        } 
        /// 
        ///     Reads the attached property from the given element.
        ///   
        /// 
        ///  
        ///     Attached property to indicate the value to use for the "primary" text of an element.
        ///  
        public static readonly DependencyProperty TextProperty
            = DependencyProperty.RegisterAttached("Text", typeof(string), typeof(TextSearch), 
                                                  new FrameworkPropertyMetadata((string)String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
 
        ///  
        ///     Writes the attached property to the given element.
        ///   
        /// 
        /// 
        public static void SetText(DependencyObject element, string text)
        { 
            if (element == null)
            { 
                throw new ArgumentNullException("element"); 
            }
 
            element.SetValue(TextProperty, text);
        }
        ///  
        ///     Reads the attached property from the given element.
        ///   
        ///  
        ///  
        ///     Prefix that is currently being used in the algorithm. 
        ///  
        private static readonly DependencyProperty CurrentPrefixProperty = 
            DependencyProperty.RegisterAttached("CurrentPrefix", typeof(string), typeof(TextSearch),
                                                new FrameworkPropertyMetadata((string)null));
        ///  
        ///     If TextSearch is currently active.
        ///   
        private static readonly DependencyProperty IsActiveProperty = 
            DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(TextSearch),
                                                new FrameworkPropertyMetadata(false)); 
        #endregion
        #region Private Properties 
        ///  
        ///     The key needed set a read-only property. 
        ///  
        private static readonly DependencyPropertyKey TextSearchInstancePropertyKey = 
            DependencyProperty.RegisterAttachedReadOnly("TextSearchInstance", typeof(TextSearch), typeof(TextSearch),
                                                new FrameworkPropertyMetadata((object)null /* default value */));
        ///  
        ///     Instance of TextSearch -- attached property so that the instance can be stored on the element
        ///     which wants the service. 
        ///   
        private static readonly DependencyProperty TextSearchInstanceProperty =
            TextSearchInstancePropertyKey.DependencyProperty; 
        // used to retrieve the value of an item, according to the TextPath
        private static readonly BindingExpressionUncommonField TextValueBindingExpression = new BindingExpressionUncommonField(); 
        #endregion 
 
        #region Private Methods
 
        /// 
        ///     Called by consumers of TextSearch when a TextInput event is received
        ///     to kick off the algorithm.
        ///   
        /// 
        /// 
        ///     Called when the user presses backspace.
        ///   
        /// 
        ///     Searches through the given itemCollection for the first item matching the given prefix. 
        ///  
        ///  
        ///     ------------------------------------------------------------------------- 
        ///     Incremental Type Search algorithm
        ///     ------------------------------------------------------------------------- 
        ///
        ///     Given a prefix and new character, we loop through all items in the collection
        ///     and look for an item that starts with the new prefix.  If we find such an item,
        ///     select it.  If the new character is repeated, we look for the next item after 
        ///     the current one that begins with the old prefix**.  We can optimize by
        ///     performing both of these searches in parallel. 
        /// 
        ///     **NOTE: Win32 will only do this if the old prefix is of length 1 - in other
        ///             words, first-character-only matching.  The algorithm described here 
        ///             is an extension of ITS as implemented in Win32.  This variant was
        ///             described to me by JeffBog as what was done in AFC - but I have yet
        ///             to find a listbox which behaves this way.
        /// 
        ///     --------------------------------------------------------------------------
        ///   
        /// Item that matches the given prefix  
        private static int FindMatchingPrefix(ItemsControl itemsControl, string primaryTextPath, string prefix,
                                               string newChar, int startItemIndex, bool lookForFallbackMatchToo, ref bool wasNewCharUsed) 
        {
            ItemCollection itemCollection = itemsControl.Items;
            // Using indices b/c this is a better way to uniquely 
            // identify an element in the collection.
            int matchedItemIndex = -1; 
            int fallbackMatchIndex = -1; 
            int count = itemCollection.Count; 
            // Return immediately with no match if there were no items in the view.
            if (count == 0)
            { 
                return -1;
            } 
 
            string newPrefix = prefix + newChar;
 
            // With an empty prefix, we'd match anything
            if (String.IsNullOrEmpty(newPrefix))
            {
                return -1; 
            }
 
            // Hook up the binding we will apply to each object.  Get the 
            // PrimaryTextPath off of the attached instance and then make
            // a binding with that path. 
            BindingExpression primaryTextBinding = null;
            object item0 = itemsControl.Items[0]; 
            bool useXml = AssemblyHelper.IsXmlNode(item0);
 
            if (useXml || !String.IsNullOrEmpty(primaryTextPath)) 
            {
                primaryTextBinding = CreateBindingExpression(itemsControl, item0, primaryTextPath); 
                TextValueBindingExpression.SetValue(itemsControl, primaryTextBinding);
            }
            bool firstItem = true;
 
            wasNewCharUsed = false;
 
            CultureInfo cultureInfo = GetCulture(itemsControl); 
            // ISSUE: what about changing the collection while this is running? 
            for (int currentIndex = startItemIndex; currentIndex < count; )
            {
                object item = itemCollection[currentIndex];
 
                if (item != null)
                { 
                    string itemString = GetPrimaryText(item, primaryTextBinding, itemsControl); 
                    bool isTextSearchCaseSensitive = itemsControl.IsTextSearchCaseSensitive;
 
                    // See if the current item matches the newPrefix, if so we can
                    // stop searching and accept this item as the match.
                    if (itemString != null && itemString.StartsWith(newPrefix, !isTextSearchCaseSensitive, cultureInfo))
                    { 
                        // Accept the new prefix as the current prefix.
                        wasNewCharUsed = true; 
                        matchedItemIndex = currentIndex; 
                        break;
                    } 
                    // Find the next string that matches the last prefix.  This
                    // string will be used in the case that the new prefix isn't
                    // matched. This enables pressing the last character multiple 
                    // times and cylcing through the set of items that match that
                    // prefix. 
                    // 
                    // Unlike the above search, this search must start *after*
                    // the currently selected item.  This search also shouldn't 
                    // happen if there was no previous prefix to match against
                    if (lookForFallbackMatchToo)
                    {
                        if (!firstItem && prefix != String.Empty) 
                        {
                            if (itemString != null) 
                            { 
                                if (fallbackMatchIndex == -1 && itemString.StartsWith(prefix, !isTextSearchCaseSensitive, cultureInfo))
                                { 
                                    fallbackMatchIndex = currentIndex;
                                }
                            }
                        } 
                        else
                        { 
                            firstItem = false; 
                        }
                    } 
                }
                // Move next and wrap-around if we pass the end of the container.
                currentIndex++; 
                if (currentIndex >= count)
                { 
                    currentIndex = 0; 
                }
 
                // Stop where we started but only after the first pass
                // through the loop -- we should process the startItem.
                if (currentIndex == startItemIndex)
                { 
                    break;
                } 
            } 
            if (primaryTextBinding != null) 
            {
                // Clean up the binding for the primary text path.
                TextValueBindingExpression.ClearValue(itemsControl);
            } 
            // In the case that the new prefix didn't match anything and 
            // there was a fallback match that matched the old prefix, move 
            // to that one.
            if (matchedItemIndex == -1 && fallbackMatchIndex != -1) 
            {
                matchedItemIndex = fallbackMatchIndex;
            }
 
            return matchedItemIndex;
        } 
 
        /// 
        ///     Helper function called by Editable ComboBox to search through items. 
        ///  
        internal static int FindMatchingPrefix(ItemsControl itemsControl, string prefix)
        {
            bool wasNewCharUsed = false; 
            return FindMatchingPrefix(itemsControl, GetPrimaryTextPath(itemsControl), 
                                      prefix, String.Empty, 0, false, ref wasNewCharUsed); 
        }
 
        private void ResetTimeout()
        {
            // Called when we get some input. Start or reset the timer.
            // Queue an inactive priority work item and set its deadline. 
            if (_timeoutTimer == null)
            { 
                _timeoutTimer = new DispatcherTimer(DispatcherPriority.Normal); 
                _timeoutTimer.Tick += new EventHandler(OnTimeout);
            } 
            else
            {
                _timeoutTimer.Stop();
            } 
            // Schedule this operation to happen a certain number of milliseconds from now. 
            _timeoutTimer.Interval = TimeOut; 
            _timeoutTimer.Start();
        } 
        private void AddCharToPrefix(string newChar)
        {
            Prefix += newChar; 
            _charsEntered.Add(newChar);
        } 
 
        private static string GetPrimaryTextPath(ItemsControl itemsControl)
        { 
            string primaryTextPath = (string)itemsControl.GetValue(TextPathProperty);
            if (String.IsNullOrEmpty(primaryTextPath))
            { 
                primaryTextPath = itemsControl.DisplayMemberPath;
            } 
            return primaryTextPath; 
        }
 
        private static string GetPrimaryText(object item, BindingExpression primaryTextBinding, DependencyObject primaryTextBindingHome)
        {
            // Order of precedence for getting Primary Text is as follows:
            // 
            // 1) PrimaryText
            // 2) PrimaryTextPath (TextSearch.TextPath or ItemsControl.DisplayMemberPath) 
            // 3) GetPlainText() 
            // 4) ToString()
 
            DependencyObject itemDO = item as DependencyObject;
            if (itemDO != null)
            { 
                string primaryText = (string)itemDO.GetValue(TextProperty);
 
                if (!String.IsNullOrEmpty(primaryText)) 
                {
                    return primaryText; 
                }
            }
            // Here hopefully they've supplied a path into their object which we can use. 
            if (primaryTextBinding != null && primaryTextBindingHome != null)
            { 
                // Take the binding that we hooked up at the beginning of the search 
                // and apply it to the current item.  Then, read the value of the
                // ItemPrimaryText property (where the binding actually lives). 
                // Try to convert the resulting object to a string.
                primaryTextBinding.Activate(item);
                object primaryText = primaryTextBinding.Value; 
                return ConvertToPlainText(primaryText); 
            } 
            return ConvertToPlainText(item); 
        }
        private static string ConvertToPlainText(object o)
        { 
            FrameworkElement fe = o as FrameworkElement;
 
            // Try to return FrameworkElement.GetPlainText() 
            if (fe != null)
            { 
                string text = fe.GetPlainText();
                if (text != null)
                { 
                    return text;
                } 
            } 
            // Try to convert the item to a string 
            return (o != null) ? o.ToString() : String.Empty;
        }
        ///  
        ///     Internal helper method that uses the same primary text lookup steps but doesn't require
        ///     the user passing in all of the bindings that we need. 
        ///   
        /// 
        ///  
        /// (10);
            } 
            else 
            {
                _charsEntered.Clear(); 
            }
            if(_timeoutTimer != null)
            { 
                _timeoutTimer.Stop();
            } 
            _timeoutTimer = null; 
        } 
        /// 
        ///     Time until the search engine resets.
        ///   
        private TimeSpan TimeOut
        { 
            get 
            {
                // NOTE: NtUser does the following (file: windows/ntuser/kernel/sysmet.c) 
                //     gpsi->dtLBSearch = dtTime * 4;            // dtLBSearch   =  4  * gdtDblClk
                //     gpsi->dtScroll = gpsi->dtLBSearch / 5;  // dtScroll     = 4/5 * gdtDblClk
                //
                // 4 * DoubleClickSpeed seems too slow for the search 
                // So for now we'll do 2 * DoubleClickSpeed
 
                return TimeSpan.FromMilliseconds(SafeNativeMethods.GetDoubleClickTime() * 2); 
            }
        } 
        #endregion
        #region Testing API 
        // Being that this is a time-sensitive operation, it's difficult 
        // to get the timing right in a DRT.  I'll leave input testing up to BVTs here 
        // but this internal API is for the DRT to do basic coverage.
        private static TextSearch GetInstance(DependencyObject d) 
        {
            return EnsureInstance(d as ItemsControl);
        }
 
        private void TypeAKey(string c)
        { 
            DoSearch(c); 
        }
 
        private void CauseTimeOut()
        {
            if (_timeoutTimer != null)
            { 
                _timeoutTimer.Stop();
                OnTimeout(_timeoutTimer, EventArgs.Empty); 
            } 
        }
 
        internal string GetCurrentPrefix()
        {
            return Prefix;
        } 
        #endregion 
 
        #region Internal Accessibility API 
        internal static string GetPrimaryText(FrameworkElement element)
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element"); 
            } 
            string text = (string)element.GetValue(TextProperty); 
            if (text != null && text != String.Empty)
            {
                return text; 
            }
 
            return element.GetPlainText(); 
        }
 
        #endregion
        #region Private Fields
 
        private string Prefix
        { 
            get { return _prefix; } 
            set
            { 
                _prefix = value;
#if DEBUG
                // Also need to invalidate the property CurrentPrefixProperty on the instance to which we are attached. 
                Debug.Assert(_attachedTo != null);
 
                _attachedTo.SetValue(CurrentPrefixProperty, _prefix); 
#endif
            } 
        }
        private bool IsActive
        { 
            get { return _isActive; }
            set 
            { 
                _isActive = value;
 
#if DEBUG
                Debug.Assert(_attachedTo != null);
                _attachedTo.SetValue(IsActiveProperty, _isActive); 
#endif
            } 
        } 
        private int MatchedItemIndex 
        {
            get { return _matchedItemIndex; }
            set
            { 
                _matchedItemIndex = value;
            } 
        } 
        private static CultureInfo GetCulture(DependencyObject element) 
        {
            object o = element.GetValue(FrameworkElement.LanguageProperty);
            CultureInfo culture = null;
 
            if (o != null)
            { 
                XmlLanguage language = (XmlLanguage) o; 
                try
                { 
                    culture = language.GetSpecificCulture();
                }
                catch (InvalidOperationException)
                { 
                }
            } 
 
            return culture;
        } 
        // Element to which this TextSearch instance is attached.
        private ItemsControl _attachedTo;
 
        // String of characters matched so far.
        private string _prefix; 
 
        private List _charsEntered;
 
        private bool _isActive;
        private int _matchedItemIndex;
 
        private DispatcherTimer _timeoutTimer;
 
        #endregion 
    }
} 
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                            
                        
                        
                        
                    Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RemotingClientProxy.cs
- FamilyMap.cs
- RtfFormatStack.cs
- _TimerThread.cs
- NotCondition.cs
- TransformedBitmap.cs
- RuntimeCompatibilityAttribute.cs
- OrderPreservingMergeHelper.cs
- ExitEventArgs.cs
- DbConnectionPoolGroup.cs
- WorkItem.cs
- JpegBitmapEncoder.cs
- MimeWriter.cs
- PasswordRecovery.cs
- HttpHeaderCollection.cs
- TemplateParser.cs
- WebPartTransformerCollection.cs
- EventHandlersStore.cs
- DeferredTextReference.cs
- StringInfo.cs
- DesignerValidationSummaryAdapter.cs
- StagingAreaInputItem.cs
- PkcsMisc.cs
- AdRotator.cs
- ClientRequest.cs
- ArrayExtension.cs
- ColorConvertedBitmapExtension.cs
- Win32PrintDialog.cs
- ProtectedConfiguration.cs
- ModuleBuilder.cs
- TempEnvironment.cs
- InputScope.cs
- XpsDigitalSignature.cs
- SHA1Managed.cs
- ApplicationInfo.cs
- Activator.cs
- RangeValuePattern.cs
- TreeWalker.cs
- SiteMapDataSourceView.cs
- MinimizableAttributeTypeConverter.cs
- FileFormatException.cs
- ObservableCollection.cs
- FileDetails.cs
- RuntimeComponentFilter.cs
- FacetChecker.cs
- ImpersonationContext.cs
- MULTI_QI.cs
- EncoderFallback.cs
- SurrogateEncoder.cs
- ConstructorArgumentAttribute.cs
- BitmapInitialize.cs
- OleDbCommand.cs
- AjaxFrameworkAssemblyAttribute.cs
- DataGridAutomationPeer.cs
- UshortList2.cs
- SharedPerformanceCounter.cs
- OdbcConnectionStringbuilder.cs
- Psha1DerivedKeyGenerator.cs
- MobileListItem.cs
- ThicknessAnimationBase.cs
- RoleManagerEventArgs.cs
- Misc.cs
- PropertyGridCommands.cs
- SourceFileBuildProvider.cs
- FixedHyperLink.cs
- ViewManagerAttribute.cs
- TemplateBindingExtension.cs
- ProfileGroupSettings.cs
- UIAgentAsyncBeginRequest.cs
- SplineQuaternionKeyFrame.cs
- XmlElementCollection.cs
- WebBrowser.cs
- DataGridDetailsPresenter.cs
- StringUtil.cs
- ZipIOLocalFileHeader.cs
- ListViewInsertEventArgs.cs
- TraceUtility.cs
- ChtmlImageAdapter.cs
- FontResourceCache.cs
- TextPatternIdentifiers.cs
- Context.cs
- Calendar.cs
- CompilerLocalReference.cs
- SignatureHelper.cs
- ValidationEventArgs.cs
- SamlDoNotCacheCondition.cs
- IncrementalHitTester.cs
- ToolTip.cs
- SourceSwitch.cs
- KoreanLunisolarCalendar.cs
- SoapCodeExporter.cs
- XmlCharType.cs
- BoundPropertyEntry.cs
- OracleCommandSet.cs
- FormsAuthenticationCredentials.cs
- DataSourceXmlAttributeAttribute.cs
- CommandField.cs
- State.cs
- SQLDecimalStorage.cs
- ServicePointManager.cs