ComboBox.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Controls / ComboBox.cs / 1 / ComboBox.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 
using System;
 
using System.ComponentModel; 

using System.Collections; 
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Threading; 

using System.Windows; 
using System.Windows.Automation.Peers; 
using System.Security;
using System.Security.Permissions; 
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Documents;
using System.Windows.Interop; 
using System.Windows.Controls.Primitives;
using System.Windows.Markup; 
using System.Windows.Shapes; 

using MS.Internal.KnownBoxes; 

namespace System.Windows.Controls
{
    ///  
    /// ComboBox control
    ///  
    [Localizability(LocalizationCategory.ComboBox)] 
    [TemplatePart(Name = "PART_EditableTextBox", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] 
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(ComboBoxItem))]
    public class ComboBox : Selector
    {
        #region Constructors 

        static ComboBox() 
        { 
            KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));
            KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.None)); 
            KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.None));

            // Disable tooltips on combo box when it is open
            ToolTipService.IsEnabledProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceToolTipIsEnabled))); 

            DefaultStyleKeyProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(typeof(ComboBox))); 
            _dType = DependencyObjectType.FromSystemTypeInternal(typeof(ComboBox)); 

            IsTextSearchEnabledProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); 

            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture));
            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.MouseDownEvent, new MouseButtonEventHandler(OnMouseButtonDown), true); // call us even if the transparent button in the style gets the click.
            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.MouseMoveEvent, new MouseEventHandler(OnMouseMove)); 
            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.PreviewMouseDownEvent, new MouseButtonEventHandler(OnPreviewMouseButtonDown));
            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.MouseWheelEvent, new MouseWheelEventHandler(OnMouseWheel), true); // call us even if textbox in the style gets the click. 
            EventManager.RegisterClassHandler(typeof(ComboBox), UIElement.GotFocusEvent, new RoutedEventHandler(OnGotFocus)); // call us even if textbox in the style get focus 

            // Listen for ContextMenu openings/closings 
            EventManager.RegisterClassHandler(typeof(ComboBox), ContextMenuService.ContextMenuOpeningEvent, new ContextMenuEventHandler(OnContextMenuOpen), true);
            EventManager.RegisterClassHandler(typeof(ComboBox), ContextMenuService.ContextMenuClosingEvent, new ContextMenuEventHandler(OnContextMenuClose), true);
        }
 
        /// 
        /// Default DependencyObject constructor 
        ///  
        public ComboBox() : base()
        { 
            Initialize();
        }

        #endregion 

        #region Properties 
 
        /// 
        ///     DependencyProperty for MaxDropDownHeight 
        /// 
        //
        public static readonly DependencyProperty MaxDropDownHeightProperty
            = DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(ComboBox), 
                                          new FrameworkPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3));
 
        ///  
        ///     The maximum height of the popup
        ///  
        [Bindable(true), Category("Layout")]
        [TypeConverter(typeof(LengthConverter))]
        public double MaxDropDownHeight
        { 
            get
            { 
                return (double)GetValue(MaxDropDownHeightProperty); 
            }
            set 
            {
                SetValue(MaxDropDownHeightProperty, value);
            }
        } 

        ///  
        /// DependencyProperty for IsDropDownOpen 
        /// 
        public static readonly DependencyProperty IsDropDownOpenProperty = 
                DependencyProperty.Register(
                        "IsDropDownOpen",
                        typeof(bool),
                        typeof(ComboBox), 
                        new FrameworkPropertyMetadata(
                                BooleanBoxes.FalseBox, 
                                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
                                new PropertyChangedCallback(OnIsDropDownOpenChanged),
                                new CoerceValueCallback(CoerceIsDropDownOpen))); 

        /// 
        /// Whether or not the "popup" for this control is currently open
        ///  
        [Bindable(true), Browsable(false), Category("Appearance")]
        public bool IsDropDownOpen 
        { 
            get { return (bool) GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, BooleanBoxes.Box(value)); } 
        }

        private static object CoerceIsDropDownOpen(DependencyObject d, object value)
        { 
            if ((bool) value)
            { 
                ComboBox cb = (ComboBox) d; 
                if (!cb.IsLoaded)
                { 
                    cb.RegisterToOpenOnLoad();
                    return BooleanBoxes.FalseBox;
                }
            } 

            return value; 
        } 

        private static object CoerceToolTipIsEnabled(DependencyObject d, object value) 
        {
            ComboBox cb = (ComboBox) d;
            return cb.IsDropDownOpen ? BooleanBoxes.FalseBox : value;
        } 

        private void RegisterToOpenOnLoad() 
        { 
            Loaded += new RoutedEventHandler(OpenOnLoad);
        } 

        private void OpenOnLoad(object sender, RoutedEventArgs e)
        {
            // Open combobox after it has rendered (Loaded is fired before 1st render) 
            Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param)
            { 
                CoerceValue(IsDropDownOpenProperty); 

                return null; 
            }), null);
        }

        ///  
        ///
        ///  
        ///  
        protected virtual void OnDropDownOpened(EventArgs e)
        { 
            RaiseClrEvent(DropDownOpenedKey, e);
        }

        ///  
        ///
        ///  
        ///  
        protected virtual void OnDropDownClosed(EventArgs e)
        { 
            RaiseClrEvent(DropDownClosedKey, e);
        }

        private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ComboBox comboBox = (ComboBox)d; 
 
            comboBox.HasMouseEnteredItemsHost = false;
 
            bool newValue = (bool) e.NewValue;
            bool oldValue = !newValue;

            // Fire accessibility event 
            ComboBoxAutomationPeer peer = UIElementAutomationPeer.FromElement(comboBox) as ComboBoxAutomationPeer;
            if(peer != null) 
            { 
                peer.RaiseExpandCollapseAutomationEvent(oldValue, newValue);
            } 

            if (newValue)
            {
                // When the drop down opens, take capture 
                Mouse.Capture(comboBox, CaptureMode.SubTree);
 
                // Select text if editable 
                if (comboBox.IsEditable && comboBox.EditableTextBoxSite != null)
                    comboBox.EditableTextBoxSite.SelectAll(); 


                // Popup.IsOpen is databound to IsDropDownOpen.  We can't know
                // if IsDropDownOpen will be invalidated before Popup.IsOpen. 
                // If we are invalidated first and we try to focus the item, we
                // might succeed (b/c there's a logical link from the item to 
                // a PresentationSource).  When the popup finally opens, Focus 
                // will be sent to null because Core doesn't know what else to do.
                // So, we must focus the element only after we are sure the popup 
                // has opened.  We will queue an operation (at Send priority) to
                // do this work -- this is the soonest we can make this happen.
                comboBox.Dispatcher.BeginInvoke(
                    DispatcherPriority.Send, 
                    (DispatcherOperationCallback) delegate(object arg)
                    { 
                        ComboBox cb = (ComboBox)arg; 
                        if (cb.IsItemsHostVisible)
                        { 
                            cb.NavigateToItem(cb.InternalSelectedItem, ItemNavigateArgs.Empty, true /* alwaysAtTopOfViewport */);
                        }
                        return null;
                    }, 
                    comboBox);
 
                comboBox.OnDropDownOpened(EventArgs.Empty); 
            }
            else 
            {
                // If focus is within the subtree, make sure we have the focus so that focus isn't in the disposed hwnd
                if (comboBox.IsKeyboardFocusWithin)
                { 
                    if (comboBox.IsEditable)
                    { 
                        if (comboBox.EditableTextBoxSite != null && !comboBox.EditableTextBoxSite.IsKeyboardFocusWithin) 
                        {
                            comboBox.Focus(); 
                        }
                    }
                    else
                    { 
                        // It's not editable, make sure the combobox has focus
                        comboBox.Focus(); 
                    } 
                }
 
                // Make sure to clear the highlight when the dropdown closes
                comboBox.HighlightedElement = null;

                if (comboBox.HasCapture) 
                {
                    Mouse.Capture(null); 
                } 

                // No Popup in the style so fire closed now 
                if (comboBox._dropDownPopup == null)
                {
                    comboBox.OnDropDownClosed(EventArgs.Empty);
                } 
            }
 
            comboBox.CoerceValue(IsSelectionBoxHighlightedProperty); 
            comboBox.CoerceValue(ToolTipService.IsEnabledProperty);
        } 

        private void OnPopupClosed(object source, EventArgs e)
        {
            OnDropDownClosed(EventArgs.Empty); 
        }
 
        ///  
        /// DependencyProperty for IsEditable
        ///  
        public static readonly DependencyProperty IsEditableProperty =
                DependencyProperty.Register(
                        "IsEditable",
                        typeof(bool), 
                        typeof(ComboBox),
                        new FrameworkPropertyMetadata( 
                                BooleanBoxes.FalseBox, 
                                new PropertyChangedCallback(OnIsEditableChanged)));
 

        /// 
        ///     True if this ComboBox is editable.
        ///  
        /// 
        public bool IsEditable 
        { 
            get { return (bool) GetValue(IsEditableProperty); }
            set { SetValue(IsEditableProperty, BooleanBoxes.Box(value)); } 
        }

        private static void OnIsEditableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ComboBox cb = d as ComboBox;
 
            cb.Update(); 
        }
 
        /// 
        ///     DependencyProperty for Text
        /// 
        public static readonly DependencyProperty TextProperty = 
                DependencyProperty.Register(
                        "Text", 
                        typeof(string), 
                        typeof(ComboBox),
                        new FrameworkPropertyMetadata( 
                                String.Empty,
                                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
                                new PropertyChangedCallback(OnTextChanged)));
 
        /// 
        ///     The text of the currently selected item.  When there is no SelectedItem and IsEditable is true 
        ///     this is the text entered in the text box.  When IsEditable is false, this value represent the string version of the selected item. 
        /// 
        ///  
        public string Text
        {
            get { return (string) GetValue(TextProperty); }
            set { SetValue(TextProperty, value); } 
        }
 
        ///  
        ///     DependencyProperty for the IsReadOnlyProperty
        ///  
        public static readonly DependencyProperty IsReadOnlyProperty =
                TextBox.IsReadOnlyProperty.AddOwner(typeof(ComboBox));

        ///  
        ///     When the ComboBox is Editable, if the TextBox within it is read only.
        ///  
        public bool IsReadOnly 
        {
            get { return (bool) GetValue(IsReadOnlyProperty); } 
            set { SetValue(IsReadOnlyProperty, BooleanBoxes.Box(value)); }
        }

        private static readonly DependencyPropertyKey SelectionBoxItemPropertyKey = 
            DependencyProperty.RegisterReadOnly("SelectionBoxItem", typeof(object), typeof(ComboBox),
                                                new FrameworkPropertyMetadata(String.Empty)); 
 
        // This property is used as a Style Helper.
        // When the SelectedItem is a UIElement a VisualBrush is created and set to the Fill property 
        // of a Rectangle. Then we set SelectionBoxItem to that rectangle.
        // For data items, SelectionBoxItem is set to a string.
        /// 
        /// The DependencyProperty for the SelectionBoxItemProperty 
        /// 
        public static readonly DependencyProperty SelectionBoxItemProperty = SelectionBoxItemPropertyKey.DependencyProperty; 
 
        /// 
        /// Used to display the selected item 
        /// 
        public object SelectionBoxItem
        {
            get { return GetValue(SelectionBoxItemProperty); } 
            private set { SetValue(SelectionBoxItemPropertyKey, value); }
        } 
 
        private static readonly DependencyPropertyKey SelectionBoxItemTemplatePropertyKey =
            DependencyProperty.RegisterReadOnly("SelectionBoxItemTemplate", typeof(DataTemplate), typeof(ComboBox), 
                                                new FrameworkPropertyMetadata((DataTemplate)null));

        /// 
        /// The DependencyProperty for the SelectionBoxItemProperty 
        /// 
        public static readonly DependencyProperty SelectionBoxItemTemplateProperty = SelectionBoxItemTemplatePropertyKey.DependencyProperty; 
 
        /// 
        /// Used to set the item DataTemplate 
        /// 
        public DataTemplate SelectionBoxItemTemplate
        {
            get { return (DataTemplate) GetValue(SelectionBoxItemTemplateProperty); } 
            private set { SetValue(SelectionBoxItemTemplatePropertyKey, value); }
        } 
 
        private static readonly DependencyPropertyKey SelectionBoxItemStringFormatPropertyKey =
            DependencyProperty.RegisterReadOnly("SelectionBoxItemStringFormat", typeof(String), typeof(ComboBox), 
                                                new FrameworkPropertyMetadata((String)null));

        /// 
        /// The DependencyProperty for the SelectionBoxItemProperty 
        /// 
        public static readonly DependencyProperty SelectionBoxItemStringFormatProperty = SelectionBoxItemStringFormatPropertyKey.DependencyProperty; 
 
        /// 
        /// Used to set the item DataStringFormat 
        /// 
        public String SelectionBoxItemStringFormat
        {
            get { return (String) GetValue(SelectionBoxItemStringFormatProperty); } 
            private set { SetValue(SelectionBoxItemStringFormatPropertyKey, value); }
        } 
 
        /// 
        ///     DependencyProperty for StaysOpenOnEdit 
        /// 
        public static readonly DependencyProperty StaysOpenOnEditProperty
            = DependencyProperty.Register("StaysOpenOnEdit", typeof(bool), typeof(ComboBox),
                                          new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); 

        ///  
        ///     Determines whether the ComboBox will remain open when clicking on 
        ///     the text box when the drop down is open
        ///  
        /// 
        public bool StaysOpenOnEdit
        {
            get 
            {
                return (bool)GetValue(StaysOpenOnEditProperty); 
            } 
            set
            { 
                SetValue(StaysOpenOnEditProperty, BooleanBoxes.Box(value));
            }
        }
 

        private static readonly DependencyPropertyKey IsSelectionBoxHighlightedPropertyKey = 
                    DependencyProperty.RegisterReadOnly("IsSelectionBoxHighlighted", typeof(bool), typeof(ComboBox), 
                                        new FrameworkPropertyMetadata(BooleanBoxes.FalseBox,
                                                                      null, 
                                                                      new CoerceValueCallback(CoerceIsSelectionBoxHighlighted)));

        /// 
        /// The DependencyProperty for the IsSelectionBoxHighlighted Property 
        /// 
        private static readonly DependencyProperty IsSelectionBoxHighlightedProperty = IsSelectionBoxHighlightedPropertyKey.DependencyProperty; 
 
        /// 
        /// Indicates the SelectionBox area should be highlighted 
        /// 
        public bool IsSelectionBoxHighlighted
        {
            get { return (bool)GetValue(IsSelectionBoxHighlightedProperty); } 
        }
 
        private static object CoerceIsSelectionBoxHighlighted(object o, object value) 
        {
            ComboBox comboBox = (ComboBox)o; 
            return (!comboBox.IsDropDownOpen && comboBox.IsKeyboardFocusWithin) ||
                   (comboBox.HighlightedElement != null && comboBox.HighlightedElement.Content == comboBox._clonedElement);
        }
 
        #endregion
 
        #region Events 

        ///  
        ///     DropDown Open event
        /// 
        public event EventHandler DropDownOpened
        { 
            add { EventHandlersStoreAdd(DropDownOpenedKey, value); }
            remove { EventHandlersStoreRemove(DropDownOpenedKey, value); } 
        } 
        private static readonly EventPrivateKey DropDownOpenedKey = new EventPrivateKey();
 
        /// 
        ///     DropDown Close event
        /// 
        public event EventHandler DropDownClosed 
        {
            add { EventHandlersStoreAdd(DropDownClosedKey, value); } 
            remove { EventHandlersStoreRemove(DropDownClosedKey, value); } 
        }
        private static readonly EventPrivateKey DropDownClosedKey = new EventPrivateKey(); 

        #endregion

        #region Selection Changed 

        // Combo Box has several methods of input for selecting items 
        //   * Selector.OnSelectionChanged 
        //   * ComboBox.Text Changed
        //   * Editable Text Box changed 
        // When any one of these inputs change, the other two must be updated to reflect
        // the third.
        //
        // When Text changes, TextUpdated() tries searching (if TextSearch is enabled) for an 
        //   item that exactly matches the new value of Text.  If it finds one, it sets
        //   selected index to that item.  This will cause OnSelectionChanged to update 
        //   the SelectionBoxItem.  Finally TextUpdated() updates the TextBox. 
        //
        // When the TextBox text changes, TextUpdated() tries searching (if TextSearch is enabled) 
        //   for an item that partially matches the new value of Text.  If it finds one, it updates
        //   The textbox and selects the remaining portion of text.  Then it sets the selected index
        //   which will cause OnSelectionChanged to update the SelectionBoxItem.  Finally
        //   TextUpdated() updates ComboBox.Text property. 
        //
        // When Selection changes, SelectedItemUpdated() will update the ComboBox.Text property 
        //   and then update the SelectionBoxItem or EditableTextBox.Text depending on edit mode 
        //
 
        /// 
        /// A virtual function that is called when the selection is changed. Default behavior
        /// is to raise a SelectionChangedEvent
        ///  
        /// The inputs for this event. Can be raised (default behavior) or processed
        ///   in some other way. 
        protected override void OnSelectionChanged(SelectionChangedEventArgs e) 
        {
            base.OnSelectionChanged(e); 

            SelectedItemUpdated();

            if (IsDropDownOpen) 
            {
                object selectedItem = InternalSelectedItem; 
                if (selectedItem != null) 
                {
                    NavigateToItem(selectedItem, ItemNavigateArgs.Empty); 
                }
            }

            if (    AutomationPeer.ListenerExists(AutomationEvents.SelectionPatternOnInvalidated) 
                ||  AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected)
                ||  AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) 
                ||  AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)   ) 
            {
                ComboBoxAutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this) as ComboBoxAutomationPeer; 
                if (peer != null)
                    peer.RaiseSelectionEvents(e);
            }
        } 

        // When the selected item (or its content) changes, update 
        // The SelectedItem property and the Text properties 
        // ComboBoxItem also calls this method when its content changes
        internal void SelectedItemUpdated() 
        {
            try
            {
                UpdatingSelectedItem = true; 

                // If the selection changed as a result of Text or the TextBox 
                // changing, don't update the Text property - TextUpdated() will 
                if (!UpdatingText)
                { 
                    // Don't change the text in the TextBox unless there is an item selected.
                    string text = TextSearch.GetPrimaryTextFromItem(this, InternalSelectedItem);

                    if (Text != text) 
                    {
                        Text = text; 
                    } 
                }
 
                // Update SelectionItem/TextBox
                Update();
            }
            finally 
            {
                UpdatingSelectedItem = false; 
            } 
        }
 
        // When the Text Property changes, search for an item exactly
        // matching the new text and set the selected index to that item
        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ComboBox cb = (ComboBox)d;
 
            cb.TextUpdated((string)e.NewValue, false); 
        }
 
        // When the user types in the TextBox, search for an item that partially
        // matches the new text and set the selected index to that item
        private void OnEditableTextBoxTextChanged(object sender, TextChangedEventArgs e)
        { 
            Debug.Assert(_editableTextBoxSite == sender);
 
            if (!IsEditable) 
            {
                // Don't do any work if we're not editable. 
                return;
            }

            TextUpdated(EditableTextBoxSite.Text, true); 
        }
 
        // When selection changes, save the location of the selection start 
        private void OnEditableTextBoxSelectionChanged(object sender, RoutedEventArgs e)
        { 
            _textBoxSelectionStart = EditableTextBoxSite.SelectionStart;
        }

        // If TextSearch is enabled search for an item matching the new text 
        // (partial search if user is typing, exact search if setting Text)
        private void TextUpdated(string newText, bool textBoxUpdated) 
        { 
            // Only process this event if it is coming from someone outside setting Text directly
            if (!UpdatingText && !UpdatingSelectedItem) 
            {
                try
                {
                    // Set the updating flags so we don't reenter this function 
                    UpdatingText = true;
 
                    // Try searching for an item matching the new text 
                    if (IsTextSearchEnabled)
                    { 
                        int matchedIndex = TextSearch.FindMatchingPrefix(this, newText);

                        if (matchedIndex >= 0)
                        { 
                            // Allow partial matches when updating textbox
                            if (textBoxUpdated) 
                            { 
                                int selectionStart = EditableTextBoxSite.SelectionStart;
                                // Perform type search when the selection is at the end 
                                // of the textbox and the selection start increased
                                if (selectionStart == newText.Length &&
                                    selectionStart > _textBoxSelectionStart)
                                { 
                                    // Replace the currently typed text with the text
                                    // from the matched item 
                                    string matchedText = TextSearch.GetPrimaryTextFromItem(this, Items[matchedIndex]); 

                                    // Replace the TextBox's text with the matched text and 
                                    // select the text beyond what the user typed
                                    EditableTextBoxSite.Text = matchedText;
                                    EditableTextBoxSite.SelectionStart = newText.Length;
                                    EditableTextBoxSite.SelectionLength = matchedText.Length - newText.Length; 

                                    // ComboBox's text property should be updated with the matched text 
                                    newText = matchedText; 
                                }
                            } 
                            else //Text Property Set
                            {
                                // Require exact matches when setting TextProperty
                                string matchedText = TextSearch.GetPrimaryTextFromItem(this, Items[matchedIndex]); 
                                if (!String.Equals(newText, matchedText, StringComparison.CurrentCulture))
                                { 
                                    // Strings not identical, no match 
                                    matchedIndex = -1;
                                } 
                            }
                        }

                        // Update SelectedIndex if it changed 
                        if (matchedIndex != SelectedIndex)
                        { 
                            // OnSelectionChanged will update the SelectedItem 
                            SelectedIndex = matchedIndex;
                        } 
                    }

                    // Update TextProperty when TextBox changes and TextBox when TextProperty changes
                    if (textBoxUpdated) 
                    {
                        Text = newText; 
                    } 
                    else if (EditableTextBoxSite != null)
                    { 
                        EditableTextBoxSite.Text = newText;
                    }
                }
                finally 
                {
                    // Clear the updating flag 
                    UpdatingText = false; 
                }
            } 
        }

        // Updates:
        //    SelectionBox if not editable 
        //    EditableTextBox.Text if editable
        private void Update() 
        { 
            if (IsEditable)
            { 
                UpdateEditableTextBox();
            }
            else
            { 
                UpdateSelectionBoxItem();
            } 
        } 

        // Update the editable TextBox to match combobox text 
        private void UpdateEditableTextBox()
        {
            if (!UpdatingText)
            { 
                try
                { 
                    UpdatingText = true; 

                    string text = Text; 

                    // Copy ComboBox.Text to the editable TextBox
                    if (EditableTextBoxSite != null && EditableTextBoxSite.Text != text)
                    { 
                        EditableTextBoxSite.Text = text;
                        EditableTextBoxSite.SelectAll(); 
                    } 
                }
                finally 
                {
                    UpdatingText = false;
                }
            } 
        }
 
        ///  
        /// This function updates the selected item in the "selection box".
        /// This is called when selection changes or when the combobox 
        /// switches from editable to non-editable or vice versa.
        /// This will also get called in ApplyTemplate in case selection
        /// is set prior to the control being measured.
        ///  
        private void UpdateSelectionBoxItem()
        { 
            // propagate the new selected item to the SelectionBoxItem property; 
            // this displays it in the selection box
            object item = InternalSelectedItem; 
            DataTemplate itemTemplate = ItemTemplate;
            string stringFormat = ItemStringFormat;

            // if Items contains an explicit ContentControl, use its content instead 
            // (this handles the case of ComboBoxItem)
            ContentControl contentControl = item as ContentControl; 
 
            if (contentControl != null)
            { 
                item = contentControl.Content;
                itemTemplate = contentControl.ContentTemplate;
                stringFormat = contentControl.ContentStringFormat;
            } 

            if (_clonedElement != null) 
            { 
                _clonedElement.LayoutUpdated -= CloneLayoutUpdated;
                _clonedElement = null; 
            }

            if (itemTemplate == null && ItemTemplateSelector == null && stringFormat == null)
            { 
                // if the item is a logical element it cannot be displayed directly in
                // the selection box because it already belongs to the tree (in the dropdown box). 
                // Instead, try to extract some useful text from the visual. 
                DependencyObject logicalElement = item as DependencyObject;
 
                if (logicalElement != null)
                {
                    // If the item is a UIElement, create a copy using a visual brush
                    _clonedElement = logicalElement as UIElement; 

                    if (_clonedElement != null) 
                    { 
                        // Create visual copy of selected element
                        VisualBrush visualBrush = new VisualBrush(_clonedElement); 
                        visualBrush.Stretch = Stretch.None;

                        //Set position and dimension of content
                        visualBrush.ViewboxUnits = BrushMappingMode.Absolute; 
                        visualBrush.Viewbox = new Rect(_clonedElement.RenderSize);
 
                        //Set position and dimension of tile 
                        visualBrush.ViewportUnits = BrushMappingMode.Absolute;
                        visualBrush.Viewport = new Rect(_clonedElement.RenderSize); 

                        // If the FlowDirection on cloned element doesn't match the combobox's apply a mirror
                        // If the FlowDirection on cloned element doesn't match its parent's apply a mirror
                        // If both are true, they cancel out so no mirror should be applied 
                        FlowDirection elementFD = (FlowDirection)_clonedElement.GetValue(FlowDirectionProperty);
                        DependencyObject parent = VisualTreeHelper.GetParent(_clonedElement); 
                        FlowDirection parentFD = parent == null ? FlowDirection : (FlowDirection)parent.GetValue(FlowDirectionProperty); 
                        if ((elementFD != this.FlowDirection) != (elementFD != parentFD))
                        { 
                            visualBrush.Transform = new MatrixTransform(new Matrix(-1.0, 0.0, 0.0, 1.0, _clonedElement.RenderSize.Width, 0.0));
                        }

                        // Apply visual brush to a rectangle 
                        Rectangle rect = new Rectangle();
                        rect.Fill = visualBrush; 
                        rect.Width = _clonedElement.RenderSize.Width; 
                        rect.Height = _clonedElement.RenderSize.Height;
 
                        _clonedElement.LayoutUpdated += CloneLayoutUpdated;

                        item = rect;
                        itemTemplate = null; 
                    }
                    else 
                    { 
                        item = ExtractString(logicalElement);
                        itemTemplate = ContentPresenter.StringContentTemplate; 
                    }
                }
            }
 
            // display a null item by an empty string
            if (item == null) 
            { 
                item = String.Empty;
                itemTemplate = ContentPresenter.StringContentTemplate; 
            }

            SelectionBoxItem = item;
            SelectionBoxItemTemplate = itemTemplate; 
            SelectionBoxItemStringFormat = stringFormat;
        } 
 
        // Update our clone's size to match the actual object's size
        private void CloneLayoutUpdated(object sender, EventArgs e) 
        {
            Rectangle rect = (Rectangle)SelectionBoxItem;
            rect.Width = _clonedElement.RenderSize.Width;
            rect.Height = _clonedElement.RenderSize.Height; 

            VisualBrush visualBrush = (VisualBrush)rect.Fill; 
            visualBrush.Viewbox = new Rect(_clonedElement.RenderSize); 
            visualBrush.Viewport = new Rect(_clonedElement.RenderSize);
        } 

        #endregion

        #region Protected Methods 

        ///  
        ///     If control has a scrollviewer in its style and has a custom keyboard scrolling behavior when HandlesScrolling should return true. 
        /// Then ScrollViewer will not handle keyboard input and leave it up to the control.
        ///  
        protected internal override bool HandlesScrolling
        {
            get { return true; }
        } 

        ///  
        /// Prepare the element to display the item.  This may involve 
        /// applying styles, setting bindings, etc.
        ///  
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
 
            if (item is Separator)
                Separator.PrepareContainer(element as Control); 
        } 

        ///  
        ///     Called when this element or any below gets focus.
        /// 
        private static void OnGotFocus(object sender, RoutedEventArgs e)
        { 
            // When ComboBox gets logical focus, select the text inside us.
            ComboBox comboBox = (ComboBox)sender; 
 
            // If we're an editable combobox, forward focus to the TextBox element
            if (!e.Handled) 
            {
                if (comboBox.IsEditable && comboBox.EditableTextBoxSite != null)
                {
                    if (e.OriginalSource == comboBox) 
                    {
                        comboBox.EditableTextBoxSite.Focus(); 
                        e.Handled = true; 
                    }
                    else if (e.OriginalSource == comboBox.EditableTextBoxSite) 
                    {
                        comboBox.EditableTextBoxSite.SelectAll();
                    }
                } 
            }
        } 
 
        /// 
        ///     Called when an item is being focused 
        /// 
        internal override void FocusItem(object item, ItemNavigateArgs itemNavigateArgs)
        {
            // The base implementation sets focus, and we don't want to do that 
            // if we're editable.
            if (!IsEditable) 
            { 
                base.FocusItem(item, itemNavigateArgs);
            } 

            ComboBoxItem highlightedElement = ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
            HighlightedElement = highlightedElement;
 
            // When IsEditable is 'true', we'll always want to commit the selection. e.g. when user press KeyUp/Down.
            // However, when IsEditable is 'false' and Dropdown is open, we could get here when user navigate to 
            // the item using ITS. In this case, we don't want to commit the selection. 
            if ((IsEditable || (!IsDropDownOpen)) && itemNavigateArgs.DeviceUsed is KeyboardDevice)
            { 
                if (highlightedElement != null)
                {
                    SelectedIndex = ItemContainerGenerator.IndexFromContainer(highlightedElement);
                } 
                else
                { 
                    SelectedIndex = Items.IndexOf(item); 
                }
            } 
        }


        ///  
        ///     An event reporting that the IsKeyboardFocusWithin property changed.
        ///  
        protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e) 
        {
            base.OnIsKeyboardFocusWithinChanged(e); 

            // This is for the case when focus goes elsewhere and the popup is still open; make sure it is closed.
            if (IsDropDownOpen && !IsKeyboardFocusWithin)
            { 
                // IsKeyboardFocusWithin still flickers under certain conditions.  The case
                // we care about is focus going from the ComboBox to a ComboBoxItem. 
                // Here we can just check if something has focus and if it's a child 
                // of ours or is a context menu that opened below us.
                DependencyObject currentFocus = Keyboard.FocusedElement as DependencyObject; 
                if (currentFocus == null || (!IsContextMenuOpen && ItemsControlFromItemContainer(currentFocus) != this))
                {
                    Close();
                } 
            }
 
            CoerceValue(IsSelectionBoxHighlightedProperty); 
        }
 
        /// 
        ///     An event reporting a mouse wheel rotation.
        /// 
        private static void OnMouseWheel(object sender, MouseWheelEventArgs e) 
        {
            ComboBox comboBox = (ComboBox)sender; 
 
            // If we get a mouse wheel event we should scroll when the
            // drop down is closed and eat the mouse wheel when it's open. 
            // (If the drop down is open and has a scrollviewer, the scrollviewer
            // will handle it before we get here).
            // We should only do this when focus is within the combobox,
            // otherwise we could severely confuse the user. 
            if (comboBox.IsKeyboardFocusWithin)
            { 
                if (!comboBox.IsDropDownOpen) 
                {
                    // Negative delta means "down", which means we should move next in that case. 
                    if (e.Delta < 0)
                    {
                        comboBox.SelectNext();
                    } 
                    else
                    { 
                        comboBox.SelectPrev(); 
                    }
                } 

                e.Handled = true;
            }
            else 
            {
                // If focus isn't within the combobox (say, we're not focusable) 
                // but we get a mouse wheel event, we should do nothing unless 
                // the drop down is open, in which case we should eat it.
                if (comboBox.IsDropDownOpen) 
                {
                    e.Handled = true;
                }
            } 
        }
 
        private static void OnContextMenuOpen(object sender, ContextMenuEventArgs e) 
        {
            ((ComboBox)sender).IsContextMenuOpen = true; 
        }

        private static void OnContextMenuClose(object sender, ContextMenuEventArgs e)
        { 
            ((ComboBox)sender).IsContextMenuOpen = false;
        } 
 
        /// 
        /// Called when IsMouseCaptured changes on this element. 
        /// 
        /// 
        protected override void OnIsMouseCapturedChanged(DependencyPropertyChangedEventArgs e)
        { 
            // When we take capture, we should start a timer to call
            // us back and do auto scrolling behavior. 
            if (IsMouseCaptured) 
            {
                Debug.Assert(_autoScrollTimer == null, "IsMouseCaptured went from true to true"); 
                if (_autoScrollTimer == null)
                {
                    _autoScrollTimer = new DispatcherTimer(DispatcherPriority.SystemIdle);
                    _autoScrollTimer.Interval = AutoScrollTimeout; 
                    _autoScrollTimer.Tick += new EventHandler(OnAutoScrollTimeout);
                    _autoScrollTimer.Start(); 
                } 
            }
            else 
            {
                if (_autoScrollTimer != null)
                {
                    _autoScrollTimer.Stop(); 
                    _autoScrollTimer = null;
                } 
            } 

            base.OnIsMouseCapturedChanged(e); 
        }

        #endregion
 
        #region Internal Methods
 
        // Helper function called by ComboBoxItem when it receives a MouseDown 
        internal void NotifyComboBoxItemMouseDown(ComboBoxItem comboBoxItem)
        { 
        }

        // Helper function called by ComboBoxItem when it receives a MouseUp
        internal void NotifyComboBoxItemMouseUp(ComboBoxItem comboBoxItem) 
        {
            object item = ItemContainerGenerator.ItemFromContainer(comboBoxItem); 
            if (item != null) 
            {
                SelectionChange.SelectJustThisItem(item, true /* assumeInItemsCollection */); 
            }

            Close();
        } 

        // Called when Item is entered via mouse or keyboard focus 
        internal void NotifyComboBoxItemEnter(ComboBoxItem item) 
        {
            // When a ComboBoxItem is entered, it should be highlighted (and focused). 
            // Note: We may reach this before a nested combo box can grab capture
            //       if one of its items releases capture.  In this case, ignore the
            //       enter event
            if (IsDropDownOpen && Mouse.Captured == this && DidMouseMove()) 
            {
                HighlightedElement = item; 
                if (!IsEditable && !item.IsKeyboardFocusWithin) 
                {
                    item.Focus(); 
                }
            }
        }
 
        /// 
        /// Return true if the item is (or is eligible to be) its own ItemUI 
        ///  
        protected override bool IsItemItsOwnContainerOverride(object item)
        { 
            return (item is ComboBoxItem);
        }

        ///  Create or identify the element used to display the given item.  
        protected override DependencyObject GetContainerForItemOverride()
        { 
            return new ComboBoxItem(); 
        }
 
        #endregion

        #region Private Methods
 
        private void Initialize()
        { 
            CanSelectMultiple = false; 
        }
 
        /// 
        ///     An event reporting a key was pressed
        /// 
        protected override void OnPreviewKeyDown(KeyEventArgs e) 
        {
            // Only process preview key events if they going to our editable text box 
            if (IsEditable && e.OriginalSource == EditableTextBoxSite) 
            {
                KeyDownHandler(e); 
            }
        }

        ///  
        ///     An event reporting a key was pressed
        ///  
        protected override void OnKeyDown(KeyEventArgs e) 
        {
            KeyDownHandler(e); 
        }

        private void KeyDownHandler(KeyEventArgs e)
        { 
            bool handled = false;
            Key key = e.Key; 
 
            // We want to handle Alt key. Get the real key if it is Key.System.
            if (key == Key.System) 
            {
                key = e.SystemKey;
            }
 
            // In Right to Left mode we switch Right and Left keys
            bool isRTL = (FlowDirection == FlowDirection.RightToLeft); 
 
            switch (key)
            { 
                case Key.Up:
                    handled = true;
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
                    { 
                        KeyboardToggleDropDown(true /* commitSelection */);
                    } 
                    else 
                    {
                        // When the drop down isn't open then focus is on the ComboBox 
                        // and we can't use KeyboardNavigation.
                        if (IsItemsHostVisible)
                        {
                            NavigateByLine(HighlightedItem, FocusNavigationDirection.Up, new ItemNavigateArgs(e.Device, Keyboard.Modifiers)); 
                        }
                        else 
                        { 
                            SelectPrev();
                        } 
                    }

                    break;
 
                case Key.Down:
                    handled = true; 
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) 
                    {
                        KeyboardToggleDropDown(true /* commitSelection */); 
                    }
                    else
                    {
                        if (IsItemsHostVisible) 
                        {
                            NavigateByLine(HighlightedItem, FocusNavigationDirection.Down, new ItemNavigateArgs(e.Device, Keyboard.Modifiers)); 
                        } 
                        else
                        { 
                            SelectNext();
                        }
                    }
 
                    break;
 
                case Key.F4: 
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == 0)
                    { 
                        KeyboardToggleDropDown(true /* commitSelection */);
                        handled = true;
                    }
                    break; 

                case Key.Escape: 
                    if (IsDropDownOpen) 
                    {
                        KeyboardCloseDropDown(false /* commitSelection */); 
                        handled = true;
                    }
                    break;
 
                case Key.Enter:
                    if (IsDropDownOpen) 
                    { 
                        KeyboardCloseDropDown(true /* commitSelection */);
                        handled = true; 
                    }
                    break;

                case Key.Home: 
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt && !IsEditable)
                    { 
                        if (IsItemsHostVisible) 
                        {
                            NavigateToStart(new ItemNavigateArgs(e.Device, Keyboard.Modifiers)); 
                        }
                        else
                        {
                            SelectFirst(); 
                        }
                        handled = true; 
                    } 
                    break;
 
                case Key.End:
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt && !IsEditable)
                    {
                        if (IsItemsHostVisible) 
                        {
                            NavigateToEnd(new ItemNavigateArgs(e.Device, Keyboard.Modifiers)); 
                        } 
                        else
                        { 
                            SelectLast();
                        }
                        handled = true;
                    } 
                    break;
 
                case Key.Right: 
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt && !IsEditable)
                    { 
                        if (IsItemsHostVisible)
                        {
                            NavigateByLine(HighlightedItem, FocusNavigationDirection.Right, new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
                        } 
                        else
                        { 
                            if (!isRTL) 
                            {
                                SelectNext(); 
                            }
                            else
                            {
                                // If it's RTL then Right should go backwards 
                                SelectPrev();
                            } 
                        } 
                        handled = true;
                    } 
                    break;

                case Key.Left:
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt && !IsEditable) 
                    {
                        if (IsItemsHostVisible) 
                        { 
                            NavigateByLine(HighlightedItem, FocusNavigationDirection.Left, new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
                        } 
                        else
                        {
                            if (!isRTL)
                            { 
                                SelectPrev();
                            } 
                            else 
                            {
                                // If it's RTL then Left should go the other direction 
                                SelectNext();
                            }
                        }
                        handled = true; 
                    }
                    break; 
 
                case Key.PageUp:
                    if (IsItemsHostVisible) 
                    {
                        NavigateByPage(HighlightedItem, FocusNavigationDirection.Up, new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
                        handled = true;
                    } 
                    break;
 
                case Key.PageDown: 
                    if (IsItemsHostVisible)
                    { 
                        NavigateByPage(HighlightedItem, FocusNavigationDirection.Down, new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
                        handled = true;
                    }
                    break; 

                case Key.Oem5: 
                    if ((Keyboard.Modifiers & ModifierKeys.Control) == (ModifierKeys.Control)) 
                    {
                        // Scroll into view the selected item -- we want to highlight the item 
                        // that we scroll in, so we should navigate to it.
                        NavigateToItem(InternalSelectedItem, ItemNavigateArgs.Empty);
                        handled = true;
                    } 
                    break;
 
                default: 
                    handled = false;
                    break; 
            }
            if (handled)
            {
                e.Handled = true; 
            }
        } 
 
        private void SelectPrev()
        { 
            if (!Items.IsEmpty)
            {
                int selectedIndex = InternalSelectedIndex;
 
                // Search backwards from SelectedIndex - 1 but don't start before the beginning.
                // If SelectedIndex is less than 0, there is nothing to select before this item. 
                if (selectedIndex > 0) 
                {
                    SelectItemHelper(selectedIndex - 1, -1, -1); 
                }
            }
        }
 
        private void SelectNext()
        { 
            int count = Items.Count; 
            if (count > 0)
            { 
                int selectedIndex = InternalSelectedIndex;

                // Search forwards from SelectedIndex + 1 but don't start past the end.
                // If SelectedIndex is before the last item then there is potentially 
                // something afterwards that we could select.
                if (selectedIndex < count - 1) 
                { 
                    SelectItemHelper(selectedIndex + 1, +1, count);
                } 
            }
        }

        private void SelectFirst() 
        {
            SelectItemHelper(0, +1, Items.Count); 
        } 

        private void SelectLast() 
        {
            SelectItemHelper(Items.Count - 1, -1, -1);
        }
 
        // Walk in the specified direction until we get to a selectable
        // item or to the stopIndex. 
        // NOTE: stopIndex is not inclusive (it should be one past the end of the range) 
        private void SelectItemHelper(int startIndex, int increment, int stopIndex)
        { 
            Debug.Assert((increment > 0 && startIndex <= stopIndex) || (increment < 0 && startIndex >= stopIndex), "Infinite loop detected");

            for (int i = startIndex; i != stopIndex; i += increment)
            { 
                // If the item is selectable and the wrapper is selectable, select it.
                // Need to check both because the user could set any combination of 
                // IsSelectable and IsEnabled on the item and wrapper. 
                object item = Items[i];
                if (IsSelectableHelper(item) && IsSelectableHelper(ItemContainerGenerator.ContainerFromIndex(i))) 
                {
                    SelectionChange.SelectJustThisItem(item, true /* assumeInItemsCollection */);
                    break;
                } 
            }
        } 
 
        private bool IsSelectableHelper(object o)
        { 
            DependencyObject d = o as DependencyObject;
            // If o is not a DependencyObject, it is just a plain
            // object and must be selectable and enabled.
            if (d == null) 
            {
                return true; 
            } 
            // It's selectable if IsSelectable is true and IsEnabled is true.
            return (bool)d.GetValue(FrameworkElement.IsEnabledProperty); 
        }

        private static string ExtractString(DependencyObject d)
        { 
            TextBlock text;
            Visual visual; 
            TextElement textElement; 
            string strValue = String.Empty;
 
            if ((text = d as TextBlock) != null)
            {
                strValue = text.Text;
            } 
            else if ((visual = d as Visual) != null)
            { 
                int count = VisualTreeHelper.GetChildrenCount(visual); 
                for(int i = 0; i < count; i++)
                { 
                    strValue += ExtractString((DependencyObject)(VisualTreeHelper.GetChild(visual, i)));
                }
            }
            else if ((textElement = d as TextElement) != null) 
            {
                strValue += TextRangeBase.GetTextInternal(textElement.ContentStart, textElement.ContentEnd); 
            } 

            return strValue; 
        }


        ///  
        /// Called when the Template's tree has been generated
        ///  
        public override void OnApplyTemplate() 
        {
            base.OnApplyTemplate(); 

            if (_dropDownPopup != null)
            {
                _dropDownPopup.Closed -= OnPopupClosed; 
            }
 
            EditableTextBoxSite = GetTemplateChild(EditableTextBoxTemplateName) as TextBox; 
            _dropDownPopup = GetTemplateChild(PopupTemplateName) as Popup;
 
            // EditableTextBoxSite should have been set by now if it's in the visual tree
            if (EditableTextBoxSite != null)
            {
                EditableTextBoxSite.TextChanged += new TextChangedEventHandler(OnEditableTextBoxTextChanged); 
                EditableTextBoxSite.SelectionChanged += new RoutedEventHandler(OnEditableTextBoxSelectionChanged);
            } 
 
            if (_dropDownPopup != null)
            { 
                _dropDownPopup.Closed += OnPopupClosed;
            }

            Update(); 
        }
 
        internal override void OnTemplateChangedInternal(FrameworkTemplate oldTemplate, FrameworkTemplate newTemplate) 
        {
            base.OnTemplateChangedInternal(oldTemplate, newTemplate); 

            // This is called when a template is applied but before the new template has been inflated.

            // If we had a style before, detach from event handlers 
            if (EditableTextBoxSite != null)
            { 
                EditableTextBoxSite.TextChanged -= new TextChangedEventHandler(OnEditableTextBoxTextChanged); 
                EditableTextBoxSite.SelectionChanged -= new RoutedEventHandler(OnEditableTextBoxSelectionChanged);
            } 
        }

        #region Capture
 
        private static void OnLostMouseCapture(object sender, MouseEventArgs e)
        { 
            ComboBox comboBox = (ComboBox)sender; 

            // ISSUE (jevansa) -- task 22022: 
            //        We need a general mechanism to do this, or at the very least we should
            //        share it amongst the controls which need it (Popup, MenuBase, ComboBox).
            if (Mouse.Captured != comboBox)
            { 
                if (e.OriginalSource == comboBox)
                { 
                    // If capture is null or it's not below the combobox, close. 
                    // More workaround for task 22022 -- check if it's a descendant (following Logical links too)
                    if (Mouse.Captured == null || !MenuBase.IsDescendant(comboBox, Mouse.Captured as DependencyObject)) 
                    {
                        comboBox.Close();
                    }
                } 
                else
                { 
                    if (MenuBase.IsDescendant(comboBox, e.OriginalSource as DependencyObject)) 
                    {
                        // Take capture if one of our children gave up capture (by closing their drop down) 
                        if (comboBox.IsDropDownOpen && Mouse.Captured == null && MS.Win32.SafeNativeMethods.GetCapture() == IntPtr.Zero)
                        {
                            Mouse.Capture(comboBox, CaptureMode.SubTree);
                            e.Handled = true; 
                        }
                    } 
                    else 
                    {
                        comboBox.Close(); 
                    }
                }
            }
        } 

        private static void OnMouseButtonDown(object sender, MouseButtonEventArgs e) 
        { 
            ComboBox comboBox = (ComboBox)sender;
 
            // If we (or one of our children) are clicked, claim the focus (don't steal focus if our context menu is clicked)
            if (!comboBox.IsContextMenuOpen && !comboBox.IsKeyboardFocusWithin)
            {
                comboBox.Focus(); 
            }
 
            e.Handled = true;   // Always handle so that parents won't take focus away 

            // Note: This half should be moved into OnMouseDownOutsideCapturedElement 
            // When we have capture, all clicks off the popup will have the combobox as
            // the OriginalSource.  So when the original source is the combobox, that
            // means the click was off the popup and we should dismiss.
            if (Mouse.Captured == comboBox && e.OriginalSource == comboBox) 
            {
                comboBox.Close(); 
                Debug.Assert(!comboBox.CheckAccess() || Mouse.Captured != comboBox, "On the dispatcher thread, ComboBox should not have capture after closing the dropdown"); 
            }
        } 

        private static void OnPreviewMouseButtonDown(object sender, MouseButtonEventArgs e)
        {
            ComboBox comboBox = (ComboBox)sender; 

            if (comboBox.IsEditable) 
            { 
                Visual originalSource = e.OriginalSource as Visual;
                Visual textBox = comboBox.EditableTextBoxSite; 

                if (originalSource != null && textBox != null
                    && textBox.IsAncestorOf(originalSource))
                { 
                    if (comboBox.IsDropDownOpen && !comboBox.StaysOpenOnEdit)
                    { 
                        // When combobox is not editable, clicks anywhere outside 
                        // the combobox will close it.  When the combobox is editable
                        // then clicking the text box should close the combobox as well. 
                        comboBox.Close();
                    }
                    else if (!comboBox.IsContextMenuOpen && !comboBox.IsKeyboardFocusWithin)
                    { 
                        // If textBox is clicked, claim focus
                        comboBox.Focus(); 
                        e.Handled = true;   // Handle so that textbox won't try to update cursor position 
                    }
                } 
            }
        }

 
        /// 
        ///     An event reporting the left mouse button was released. 
        ///  
        /// 
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) 
        {
            // Ignore the first mouse button up if we haven't gone over the popup yet
            // And ignore all mouse ups over the items host.
            if (HasMouseEnteredItemsHost && !IsMouseOverItemsHost) 
            {
                if (IsDropDownOpen) 
                { 
                    Close();
                    e.Handled = true; 
                    Debug.Assert(!CheckAccess() || Mouse.Captured != this, "On the dispatcher thread, ComboBox should not have capture after closing the dropdown");
                }
            }
 
            base.OnMouseLeftButtonUp(e);
        } 
 
        private static void OnMouseMove(object sender, MouseEventArgs e)
        { 
            ComboBox comboBox = (ComboBox)sender;

            // The mouse moved, see if we're over the items host yet
            if (comboBox.IsDropDownOpen) 
            {
                bool isMouseOverItemsHost = comboBox.ItemsHost != null ? comboBox.ItemsHost.IsMouseOver : false; 
 
                // When mouse enters items host, start tracking mouse movements
                if (isMouseOverItemsHost && !comboBox.HasMouseEnteredItemsHost) 
                {
                    comboBox.SetInitialMousePosition();
                }
 
                comboBox.IsMouseOverItemsHost = isMouseOverItemsHost;
                comboBox.HasMouseEnteredItemsHost |= isMouseOverItemsHost; 
            } 

            // If we get a mouse move and we have capture, then the mouse was 
            // outside the ComboBox.  We should autoscroll.
            if (Mouse.LeftButton == MouseButtonState.Pressed && comboBox.HasMouseEnteredItemsHost)
            {
                if (Mouse.Captured == comboBox) 
                {
                    if (Mouse.LeftButton == MouseButtonState.Pressed) 
                    { 
                        comboBox.DoAutoScroll(comboBox.HighlightedItem);
                    } 
                    else
                    {
                        // We missed the mouse up, release capture
                        comboBox.ReleaseMouseCapture(); 
                        comboBox.ResetLastMousePosition();
                    } 
 

                    e.Handled = true; 
                }
            }
        }
 
        /// 
        /// Called to toggle the DropDown using the keyboard. 
        ///  
        private void KeyboardToggleDropDown(bool commitSelection)
        { 
            KeyboardToggleDropDown(!IsDropDownOpen, commitSelection);
        }

        ///  
        /// Called to close the DropDown using the keyboard.
        ///  
        private void KeyboardCloseDropDown(bool commitSelection) 
        {
            KeyboardToggleDropDown(false /* openDropDown */, commitSelection); 
        }

        private void KeyboardToggleDropDown(bool openDropDown, bool commitSelection)
        { 
            // Close the dropdown and commit the selection if requested.
            // Make sure to set the selection after the dropdown has closed 
            // so we don't trigger any unnecessary navigation as a result 
            // of changing the selection.
            object itemToSelect = null; 
            if (commitSelection)
            {
                itemToSelect = HighlightedItem;
            } 

            IsDropDownOpen = openDropDown; 
 
            if (openDropDown == false && commitSelection && (itemToSelect != null))
            { 
                SelectionChange.SelectJustThisItem(itemToSelect, true /* assumeInItemsCollection */);
            }
        }
 
        private void CommitSelection()
        { 
            object itemToSelect = HighlightedItem; 
            if (itemToSelect != null)
            { 
                SelectionChange.SelectJustThisItem(itemToSelect, true /* assumeInItemsCollection */);
            }
        }
 
        private void OnAutoScrollTimeout(object sender, EventArgs e)
        { 
            if (Mouse.LeftButton == MouseButtonState.Pressed 
                && HasMouseEnteredItemsHost)
            { 
                DoAutoScroll(HighlightedItem);
            }
        }
 
        private void Close()
        { 
            if (IsDropDownOpen) 
            {
                ClearValue(IsDropDownOpenProperty); 

                // Force Close in case of triggers
                if (IsDropDownOpen)
                    IsDropDownOpen = false; 
            }
        } 
 
        #endregion
 
        #endregion

        #region Accessibility
 
        /// 
        /// Creates AutomationPeer () 
        ///  
        protected override AutomationPeer OnCreateAutomationPeer()
        { 
            return new ComboBoxAutomationPeer(this);
        }

        #endregion 

        #region Private Properties 
 
        internal TextBox EditableTextBoxSite
        { 
            get
            {
                return _editableTextBoxSite;
            } 
            set
            { 
                _editableTextBoxSite = value; 
            }
        } 

        private bool HasCapture
        {
            get 
            {
                return Mouse.Captured == this; 
            } 
        }
 
        /// 
        /// Returns true if the ItemsHost is visually connected to the RootVisual of its PresentationSource.
        /// 
        ///  
        /// 
        ///     Critical: This code accesses HwndSource from the call PresentationSource.CriticalFromVisual 
        ///     TreatAsSafe: It does not expose the critical data 
        /// 
        private bool IsItemsHostVisible 
        {
            [SecurityCritical,SecurityTreatAsSafe]
            get
            { 
                Panel itemsHost = ItemsHost;
                if (itemsHost != null) 
                { 
                    HwndSource source = PresentationSource.CriticalFromVisual(itemsHost) as HwndSource;
 
                    if (source != null && !source.IsDisposed && source.RootVisual != null)
                    {
                        return source.RootVisual.IsAncestorOf(itemsHost);
                    } 
                }
 
                return false; 
            }
        } 

        private ComboBoxItem HighlightedElement
        {
            get 
            {
                return _highlightedElement != null ? _highlightedElement.Target as ComboBoxItem : null; 
            } 
            set
            { 
                ComboBoxItem current = (_highlightedElement != null) ? (_highlightedElement.Target as ComboBoxItem) : null;

                if (current != null)
                { 
                    current.SetIsHighlighted(false);
                } 
 
                if (value != null)
                { 
                    _highlightedElement = new WeakReference(value);
                    value.SetIsHighlighted(true);
                }
                else 
                {
                    _highlightedElement = null; 
                } 

                CoerceValue(IsSelectionBoxHighlightedProperty); 
            }
        }

        private object HighlightedItem 
        {
            get 
            { 
                ComboBoxItem highlightedElement = HighlightedElement;
                if (highlightedElement != null) 
                {
                    object item = ItemContainerGenerator.ItemFromContainer(highlightedElement);
                    Debug.Assert(item != DependencyProperty.UnsetValue, "No item in container HighlightedElement");
 
                    return item;
                } 
 
                return null;
            } 
        }

        private bool IsMouseOverItemsHost
        { 
            get { return _cacheValid[(int)CacheBits.IsMouseOverItemsHost]; }
            set { _cacheValid[(int)CacheBits.IsMouseOverItemsHost] = value; } 
        } 

        private bool HasMouseEnteredItemsHost 
        {
            get { return _cacheValid[(int)CacheBits.HasMouseEnteredItemsHost]; }
            set { _cacheValid[(int)CacheBits.HasMouseEnteredItemsHost] = value; }
        } 

        private bool IsContextMenuOpen 
        { 
            get { return _cacheValid[(int)CacheBits.IsContextMenuOpen]; }
            set { _cacheValid[(int)CacheBits.IsContextMenuOpen] = value; } 
        }

        // Used to indicate that the Text Properties are changing
        // Don't reenter callbacks 
        private bool UpdatingText
        { 
            get { return _cacheValid[(int)CacheBits.UpdatingText]; } 
            set { _cacheValid[(int)CacheBits.UpdatingText] = value; }
        } 

        // Selected item is being updated; Don't reenter callbacks
        private bool UpdatingSelectedItem
        { 
            get { return _cacheValid[(int)CacheBits.UpdatingSelectedItem]; }
            set { _cacheValid[(int)CacheBits.UpdatingSelectedItem] = value; } 
        } 

        #endregion 

        #region Private Members

        private const string EditableTextBoxTemplateName = "PART_EditableTextBox"; 
        private const string PopupTemplateName = "PART_Popup";
 
        private TextBox _editableTextBoxSite; 
        private Popup _dropDownPopup;
        private int _textBoxSelectionStart; // the location of selection before call to TextUpdated. 
        private BitVector32 _cacheValid = new BitVector32(0);   // Condense boolean bits
        private WeakReference _highlightedElement; // a WeakReference to the ComboBoxItem which is "highlighted"
        private DispatcherTimer _autoScrollTimer;
        private UIElement _clonedElement; 

        private enum CacheBits 
        { 
            IsMouseOverItemsHost        = 0x01,
            HasMouseEnteredItemsHost    = 0x02, 
            IsContextMenuOpen           = 0x04,
            UpdatingText                = 0x08,
            UpdatingSelectedItem        = 0x10,
        } 

        #endregion Private Members 
 
        #region DTypeThemeStyleKey
 
        // Returns the DependencyObjectType for the registered ThemeStyleKey's default
        // value. Controls will override this method to return approriate types.
        internal override DependencyObjectType DTypeThemeStyleKey
        { 
            get { return _dType; }
        } 
 
        private static DependencyObjectType _dType;
 
        #endregion DTypeThemeStyleKey
    }
}
 


// 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.ComponentModel; 

using System.Collections; 
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Threading; 

using System.Windows; 
using System.Windows.Automation.Peers; 
using System.Security;
using System.Security.Permissions; 
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Documents;
using System.Windows.Interop; 
using System.Windows.Controls.Primitives;
using System.Windows.Markup; 
using System.Windows.Shapes; 

using MS.Internal.KnownBoxes; 

namespace System.Windows.Controls
{
    ///  
    /// ComboBox control
    ///  
    [Localizability(LocalizationCategory.ComboBox)] 
    [TemplatePart(Name = "PART_EditableTextBox", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] 
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(ComboBoxItem))]
    public class ComboBox : Selector
    {
        #region Constructors 

        static ComboBox() 
        { 
            KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));
            KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.None)); 
            KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.None));

            // Disable tooltips on combo box when it is open
            ToolTipService.IsEnabledProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceToolTipIsEnabled))); 

            DefaultStyleKeyProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(typeof(ComboBox))); 
            _dType = DependencyObjectType.FromSystemTypeInternal(typeof(ComboBox)); 

            IsTextSearchEnabledProperty.OverrideMetadata(typeof(ComboBox), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); 

            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture));
            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.MouseDownEvent, new MouseButtonEventHandler(OnMouseButtonDown), true); // call us even if the transparent button in the style gets the click.
            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.MouseMoveEvent, new MouseEventHandler(OnMouseMove)); 
            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.PreviewMouseDownEvent, new MouseButtonEventHandler(OnPreviewMouseButtonDown));
            EventManager.RegisterClassHandler(typeof(ComboBox), Mouse.MouseWheelEvent, new MouseWheelEventHandler(OnMouseWheel), true); // call us even if textbox in the style gets the click. 
            EventManager.RegisterClassHandler(typeof(ComboBox), UIElement.GotFocusEvent, new RoutedEventHandler(OnGotFocus)); // call us even if textbox in the style get focus 

            // Listen for ContextMenu openings/closings 
            EventManager.RegisterClassHandler(typeof(ComboBox), ContextMenuService.ContextMenuOpeningEvent, new ContextMenuEventHandler(OnContextMenuOpen), true);
            EventManager.RegisterClassHandler(typeof(ComboBox), ContextMenuService.ContextMenuClosingEvent, new ContextMenuEventHandler(OnContextMenuClose), true);
        }
 
        /// 
        /// Default DependencyObject constructor 
        ///  
        public ComboBox() : base()
        { 
            Initialize();
        }

        #endregion 

        #region Properties 
 
        /// 
        ///     DependencyProperty for MaxDropDownHeight 
        /// 
        //
        public static readonly DependencyProperty MaxDropDownHeightProperty
            = DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(ComboBox), 
                                          new FrameworkPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3));
 
        ///  
        ///     The maximum height of the popup
        ///  
        [Bindable(true), Category("Layout")]
        [TypeConverter(typeof(LengthConverter))]
        public double MaxDropDownHeight
        { 
            get
            { 
                return (double)GetValue(MaxDropDownHeightProperty); 
            }
            set 
            {
                SetValue(MaxDropDownHeightProperty, value);
            }
        } 

        ///  
        /// DependencyProperty for IsDropDownOpen 
        /// 
        public static readonly DependencyProperty IsDropDownOpenProperty = 
                DependencyProperty.Register(
                        "IsDropDownOpen",
                        typeof(bool),
                        typeof(ComboBox), 
                        new FrameworkPropertyMetadata(
                                BooleanBoxes.FalseBox, 
                                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
                                new PropertyChangedCallback(OnIsDropDownOpenChanged),
                                new CoerceValueCallback(CoerceIsDropDownOpen))); 

        /// 
        /// Whether or not the "popup" for this control is currently open
        ///  
        [Bindable(true), Browsable(false), Category("Appearance")]
        public bool IsDropDownOpen 
        { 
            get { return (bool) GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, BooleanBoxes.Box(value)); } 
        }

        private static object CoerceIsDropDownOpen(DependencyObject d, object value)
        { 
            if ((bool) value)
            { 
                ComboBox cb = (ComboBox) d; 
                if (!cb.IsLoaded)
                { 
                    cb.RegisterToOpenOnLoad();
                    return BooleanBoxes.FalseBox;
                }
            } 

            return value; 
        } 

        private static object CoerceToolTipIsEnabled(DependencyObject d, object value) 
        {
            ComboBox cb = (ComboBox) d;
            return cb.IsDropDownOpen ? BooleanBoxes.FalseBox : value;
        } 

        private void RegisterToOpenOnLoad() 
        { 
            Loaded += new RoutedEventHandler(OpenOnLoad);
        } 

        private void OpenOnLoad(object sender, RoutedEventArgs e)
        {
            // Open combobox after it has rendered (Loaded is fired before 1st render) 
            Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param)
            { 
                CoerceValue(IsDropDownOpenProperty); 

                return null; 
            }), null);
        }

        ///  
        ///
        ///  
        ///  
        protected virtual void OnDropDownOpened(EventArgs e)
        { 
            RaiseClrEvent(DropDownOpenedKey, e);
        }

        ///  
        ///
        ///  
        ///  
        protected virtual void OnDropDownClosed(EventArgs e)
        { 
            RaiseClrEvent(DropDownClosedKey, e);
        }

        private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ComboBox comboBox = (ComboBox)d; 
 
            comboBox.HasMouseEnteredItemsHost = false;
 
            bool newValue = (bool) e.NewValue;
            bool oldValue = !newValue;

            // Fire accessibility event 
            ComboBoxAutomationPeer peer = UIElementAutomationPeer.FromElement(comboBox) as ComboBoxAutomationPeer;
            if(peer != null) 
            { 
                peer.RaiseExpandCollapseAutomationEvent(oldValue, newValue);
            } 

            if (newValue)
            {
                // When the drop down opens, take capture 
                Mouse.Capture(comboBox, CaptureMode.SubTree);
 
                // Select text if editable 
                if (comboBox.IsEditable && comboBox.EditableTextBoxSite != null)
                    comboBox.EditableTextBoxSite.SelectAll(); 


                // Popup.IsOpen is databound to IsDropDownOpen.  We can't know
                // if IsDropDownOpen will be invalidated before Popup.IsOpen. 
                // If we are invalidated first and we try to focus the item, we
                // might succeed (b/c there's a logical link from the item to 
                // a PresentationSource).  When the popup finally opens, Focus 
                // will be sent to null because Core doesn't know what else to do.
                // So, we must focus the element only after we are sure the popup 
                // has opened.  We will queue an operation (at Send priority) to
                // do this work -- this is the soonest we can make this happen.
                comboBox.Dispatcher.BeginInvoke(
                    DispatcherPriority.Send, 
                    (DispatcherOperationCallback) delegate(object arg)
                    { 
                        ComboBox cb = (ComboBox)arg; 
                        if (cb.IsItemsHostVisible)
                        { 
                            cb.NavigateToItem(cb.InternalSelectedItem, ItemNavigateArgs.Empty, true /* alwaysAtTopOfViewport */);
                        }
                        return null;
                    }, 
                    comboBox);
 
                comboBox.OnDropDownOpened(EventArgs.Empty); 
            }
            else 
            {
                // If focus is within the subtree, make sure we have the focus so that focus isn't in the disposed hwnd
                if (comboBox.IsKeyboardFocusWithin)
                { 
                    if (comboBox.IsEditable)
                    { 
                        if (comboBox.EditableTextBoxSite != null && !comboBox.EditableTextBoxSite.IsKeyboardFocusWithin) 
                        {
                            comboBox.Focus(); 
                        }
                    }
                    else
                    { 
                        // It's not editable, make sure the combobox has focus
                        comboBox.Focus(); 
                    } 
                }
 
                // Make sure to clear the highlight when the dropdown closes
                comboBox.HighlightedElement = null;

                if (comboBox.HasCapture) 
                {
                    Mouse.Capture(null); 
                } 

                // No Popup in the style so fire closed now 
                if (comboBox._dropDownPopup == null)
                {
                    comboBox.OnDropDownClosed(EventArgs.Empty);
                } 
            }
 
            comboBox.CoerceValue(IsSelectionBoxHighlightedProperty); 
            comboBox.CoerceValue(ToolTipService.IsEnabledProperty);
        } 

        private void OnPopupClosed(object source, EventArgs e)
        {
            OnDropDownClosed(EventArgs.Empty); 
        }
 
        ///  
        /// DependencyProperty for IsEditable
        ///  
        public static readonly DependencyProperty IsEditableProperty =
                DependencyProperty.Register(
                        "IsEditable",
                        typeof(bool), 
                        typeof(ComboBox),
                        new FrameworkPropertyMetadata( 
                                BooleanBoxes.FalseBox, 
                                new PropertyChangedCallback(OnIsEditableChanged)));
 

        /// 
        ///     True if this ComboBox is editable.
        ///  
        /// 
        public bool IsEditable 
        { 
            get { return (bool) GetValue(IsEditableProperty); }
            set { SetValue(IsEditableProperty, BooleanBoxes.Box(value)); } 
        }

        private static void OnIsEditableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ComboBox cb = d as ComboBox;
 
            cb.Update(); 
        }
 
        /// 
        ///     DependencyProperty for Text
        /// 
        public static readonly DependencyProperty TextProperty = 
                DependencyProperty.Register(
                        "Text", 
                        typeof(string), 
                        typeof(ComboBox),
                        new FrameworkPropertyMetadata( 
                                String.Empty,
                                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
                                new PropertyChangedCallback(OnTextChanged)));
 
        /// 
        ///     The text of the currently selected item.  When there is no SelectedItem and IsEditable is true 
        ///     this is the text entered in the text box.  When IsEditable is false, this value represent the string version of the selected item. 
        /// 
        ///  
        public string Text
        {
            get { return (string) GetValue(TextProperty); }
            set { SetValue(TextProperty, value); } 
        }
 
        ///  
        ///     DependencyProperty for the IsReadOnlyProperty
        ///  
        public static readonly DependencyProperty IsReadOnlyProperty =
                TextBox.IsReadOnlyProperty.AddOwner(typeof(ComboBox));

        ///  
        ///     When the ComboBox is Editable, if the TextBox within it is read only.
        ///  
        public bool IsReadOnly 
        {
            get { return (bool) GetValue(IsReadOnlyProperty); } 
            set { SetValue(IsReadOnlyProperty, BooleanBoxes.Box(value)); }
        }

        private static readonly DependencyPropertyKey SelectionBoxItemPropertyKey = 
            DependencyProperty.RegisterReadOnly("SelectionBoxItem", typeof(object), typeof(ComboBox),
                                                new FrameworkPropertyMetadata(String.Empty)); 
 
        // This property is used as a Style Helper.
        // When the SelectedItem is a UIElement a VisualBrush is created and set to the Fill property 
        // of a Rectangle. Then we set SelectionBoxItem to that rectangle.
        // For data items, SelectionBoxItem is set to a string.
        /// 
        /// The DependencyProperty for the SelectionBoxItemProperty 
        /// 
        public static readonly DependencyProperty SelectionBoxItemProperty = SelectionBoxItemPropertyKey.DependencyProperty; 
 
        /// 
        /// Used to display the selected item 
        /// 
        public object SelectionBoxItem
        {
            get { return GetValue(SelectionBoxItemProperty); } 
            private set { SetValue(SelectionBoxItemPropertyKey, value); }
        } 
 
        private static readonly DependencyPropertyKey SelectionBoxItemTemplatePropertyKey =
            DependencyProperty.RegisterReadOnly("SelectionBoxItemTemplate", typeof(DataTemplate), typeof(ComboBox), 
                                                new FrameworkPropertyMetadata((DataTemplate)null));

        /// 
        /// The DependencyProperty for the SelectionBoxItemProperty 
        /// 
        public static readonly DependencyProperty SelectionBoxItemTemplateProperty = SelectionBoxItemTemplatePropertyKey.DependencyProperty; 
 
        /// 
        /// Used to set the item DataTemplate 
        /// 
        public DataTemplate SelectionBoxItemTemplate
        {
            get { return (DataTemplate) GetValue(SelectionBoxItemTemplateProperty); } 
            private set { SetValue(SelectionBoxItemTemplatePropertyKey, value); }
        } 
 
        private static readonly DependencyPropertyKey SelectionBoxItemStringFormatPropertyKey =
            DependencyProperty.RegisterReadOnly("SelectionBoxItemStringFormat", typeof(String), typeof(ComboBox), 
                                                new FrameworkPropertyMetadata((String)null));

        /// 
        /// The DependencyProperty for the SelectionBoxItemProperty 
        /// 
        public static readonly DependencyProperty SelectionBoxItemStringFormatProperty = SelectionBoxItemStringFormatPropertyKey.DependencyProperty; 
 
        /// 
        /// Used to set the item DataStringFormat 
        /// 
        public String SelectionBoxItemStringFormat
        {
            get { return (String) GetValue(SelectionBoxItemStringFormatProperty); } 
            private set { SetValue(SelectionBoxItemStringFormatPropertyKey, value); }
        } 
 
        /// 
        ///     DependencyProperty for StaysOpenOnEdit 
        /// 
        public static readonly DependencyProperty StaysOpenOnEditProperty
            = DependencyProperty.Register("StaysOpenOnEdit", typeof(bool), typeof(ComboBox),
                                          new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); 

        ///  
        ///     Determines whether the ComboBox will remain open when clicking on 
        ///     the text box when the drop down is open
        ///  
        /// 
        public bool StaysOpenOnEdit
        {
            get 
            {
                return (bool)GetValue(StaysOpenOnEditProperty); 
            } 
            set
            { 
                SetValue(StaysOpenOnEditProperty, BooleanBoxes.Box(value));
            }
        }
 

        private static readonly DependencyPropertyKey IsSelectionBoxHighlightedPropertyKey = 
                    DependencyProperty.RegisterReadOnly("IsSelectionBoxHighlighted", typeof(bool), typeof(ComboBox), 
                                        new FrameworkPropertyMetadata(BooleanBoxes.FalseBox,
                                                                      null, 
                                                                      new CoerceValueCallback(CoerceIsSelectionBoxHighlighted)));

        /// 
        /// The DependencyProperty for the IsSelectionBoxHighlighted Property 
        /// 
        private static readonly DependencyProperty IsSelectionBoxHighlightedProperty = IsSelectionBoxHighlightedPropertyKey.DependencyProperty; 
 
        /// 
        /// Indicates the SelectionBox area should be highlighted 
        /// 
        public bool IsSelectionBoxHighlighted
        {
            get { return (bool)GetValue(IsSelectionBoxHighlightedProperty); } 
        }
 
        private static object CoerceIsSelectionBoxHighlighted(object o, object value) 
        {
            ComboBox comboBox = (ComboBox)o; 
            return (!comboBox.IsDropDownOpen && comboBox.IsKeyboardFocusWithin) ||
                   (comboBox.HighlightedElement != null && comboBox.HighlightedElement.Content == comboBox._clonedElement);
        }
 
        #endregion
 
        #region Events 

        ///  
        ///     DropDown Open event
        /// 
        public event EventHandler DropDownOpened
        { 
            add { EventHandlersStoreAdd(DropDownOpenedKey, value); }
            remove { EventHandlersStoreRemove(DropDownOpenedKey, value); } 
        } 
        private static readonly EventPrivateKey DropDownOpenedKey = new EventPrivateKey();
 
        /// 
        ///     DropDown Close event
        /// 
        public event EventHandler DropDownClosed 
        {
            add { EventHandlersStoreAdd(DropDownClosedKey, value); } 
            remove { EventHandlersStoreRemove(DropDownClosedKey, value); } 
        }
        private static readonly EventPrivateKey DropDownClosedKey = new EventPrivateKey(); 

        #endregion

        #region Selection Changed 

        // Combo Box has several methods of input for selecting items 
        //   * Selector.OnSelectionChanged 
        //   * ComboBox.Text Changed
        //   * Editable Text Box changed 
        // When any one of these inputs change, the other two must be updated to reflect
        // the third.
        //
        // When Text changes, TextUpdated() tries searching (if TextSearch is enabled) for an 
        //   item that exactly matches the new value of Text.  If it finds one, it sets
        //   selected index to that item.  This will cause OnSelectionChanged to update 
        //   the SelectionBoxItem.  Finally TextUpdated() updates the TextBox. 
        //
        // When the TextBox text changes, TextUpdated() tries searching (if TextSearch is enabled) 
        //   for an item that partially matches the new value of Text.  If it finds one, it updates
        //   The textbox and selects the remaining portion of text.  Then it sets the selected index
        //   which will cause OnSelectionChanged to update the SelectionBoxItem.  Finally
        //   TextUpdated() updates ComboBox.Text property. 
        //
        // When Selection changes, SelectedItemUpdated() will update the ComboBox.Text property 
        //   and then update the SelectionBoxItem or EditableTextBox.Text depending on edit mode 
        //
 
        /// 
        /// A virtual function that is called when the selection is changed. Default behavior
        /// is to raise a SelectionChangedEvent
        ///  
        /// The inputs for this event. Can be raised (default behavior) or processed
        ///   in some other way. 
        protected override void OnSelectionChanged(SelectionChangedEventArgs e) 
        {
            base.OnSelectionChanged(e); 

            SelectedItemUpdated();

            if (IsDropDownOpen) 
            {
                object selectedItem = InternalSelectedItem; 
                if (selectedItem != null) 
                {
                    NavigateToItem(selectedItem, ItemNavigateArgs.Empty); 
                }
            }

            if (    AutomationPeer.ListenerExists(AutomationEvents.SelectionPatternOnInvalidated) 
                ||  AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected)
                ||  AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) 
                ||  AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)   ) 
            {
                ComboBoxAutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this) as ComboBoxAutomationPeer; 
                if (peer != null)
                    peer.RaiseSelectionEvents(e);
            }
        } 

        // When the selected item (or its content) changes, update 
        // The SelectedItem property and the Text properties 
        // ComboBoxItem also calls this method when its content changes
        internal void SelectedItemUpdated() 
        {
            try
            {
                UpdatingSelectedItem = true; 

                // If the selection changed as a result of Text or the TextBox 
                // changing, don't update the Text property - TextUpdated() will 
                if (!UpdatingText)
                { 
                    // Don't change the text in the TextBox unless there is an item selected.
                    string text = TextSearch.GetPrimaryTextFromItem(this, InternalSelectedItem);

                    if (Text != text) 
                    {
                        Text = text; 
                    } 
                }
 
                // Update SelectionItem/TextBox
                Update();
            }
            finally 
            {
                UpdatingSelectedItem = false; 
            } 
        }
 
        // When the Text Property changes, search for an item exactly
        // matching the new text and set the selected index to that item
        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ComboBox cb = (ComboBox)d;
 
            cb.TextUpdated((string)e.NewValue, false); 
        }
 
        // When the user types in the TextBox, search for an item that partially
        // matches the new text and set the selected index to that item
        private void OnEditableTextBoxTextChanged(object sender, TextChangedEventArgs e)
        { 
            Debug.Assert(_editableTextBoxSite == sender);
 
            if (!IsEditable) 
            {
                // Don't do any work if we're not editable. 
                return;
            }

            TextUpdated(EditableTextBoxSite.Text, true); 
        }
 
        // When selection changes, save the location of the selection start 
        private void OnEditableTextBoxSelectionChanged(object sender, RoutedEventArgs e)
        { 
            _textBoxSelectionStart = EditableTextBoxSite.SelectionStart;
        }

        // If TextSearch is enabled search for an item matching the new text 
        // (partial search if user is typing, exact search if setting Text)
        private void TextUpdated(string newText, bool textBoxUpdated) 
        { 
            // Only process this event if it is coming from someone outside setting Text directly
            if (!UpdatingText && !UpdatingSelectedItem) 
            {
                try
                {
                    // Set the updating flags so we don't reenter this function 
                    UpdatingText = true;
 
                    // Try searching for an item matching the new text 
                    if (IsTextSearchEnabled)
                    { 
                        int matchedIndex = TextSearch.FindMatchingPrefix(this, newText);

                        if (matchedIndex >= 0)
                        { 
                            // Allow partial matches when updating textbox
                            if (textBoxUpdated) 
                            { 
                                int selectionStart = EditableTextBoxSite.SelectionStart;
                                // Perform type search when the selection is at the end 
                                // of the textbox and the selection start increased
                                if (selectionStart == newText.Length &&
                                    selectionStart > _textBoxSelectionStart)
                                { 
                                    // Replace the currently typed text with the text
                                    // from the matched item 
                                    string matchedText = TextSearch.GetPrimaryTextFromItem(this, Items[matchedIndex]); 

                                    // Replace the TextBox's text with the matched text and 
                                    // select the text beyond what the user typed
                                    EditableTextBoxSite.Text = matchedText;
                                    EditableTextBoxSite.SelectionStart = newText.Length;
                                    EditableTextBoxSite.SelectionLength = matchedText.Length - newText.Length; 

                                    // ComboBox's text property should be updated with the matched text 
                                    newText = matchedText; 
                                }
                            } 
                            else //Text Property Set
                            {
                                // Require exact matches when setting TextProperty
                                string matchedText = TextSearch.GetPrimaryTextFromItem(this, Items[matchedIndex]); 
                                if (!String.Equals(newText, matchedText, StringComparison.CurrentCulture))
                                { 
                                    // Strings not identical, no match 
                                    matchedIndex = -1;
                                } 
                            }
                        }

                        // Update SelectedIndex if it changed 
                        if (matchedIndex != SelectedIndex)
                        { 
                            // OnSelectionChanged will update the SelectedItem 
                            SelectedIndex = matchedIndex;
                        } 
                    }

                    // Update TextProperty when TextBox changes and TextBox when TextProperty changes
                    if (textBoxUpdated) 
                    {
                        Text = newText; 
                    } 
                    else if (EditableTextBoxSite != null)
                    { 
                        EditableTextBoxSite.Text = newText;
                    }
                }
                finally 
                {
                    // Clear the updating flag 
                    UpdatingText = false; 
                }
            } 
        }

        // Updates:
        //    SelectionBox if not editable 
        //    EditableTextBox.Text if editable
        private void Update() 
        { 
            if (IsEditable)
            { 
                UpdateEditableTextBox();
            }
            else
            { 
                UpdateSelectionBoxItem();
            } 
        } 

        // Update the editable TextBox to match combobox text 
        private void UpdateEditableTextBox()
        {
            if (!UpdatingText)
            { 
                try
                { 
                    UpdatingText = true; 

                    string text = Text; 

                    // Copy ComboBox.Text to the editable TextBox
                    if (EditableTextBoxSite != null && EditableTextBoxSite.Text != text)
                    { 
                        EditableTextBoxSite.Text = text;
                        EditableTextBoxSite.SelectAll(); 
                    } 
                }
                finally 
                {
                    UpdatingText = false;
                }
            } 
        }
 
        ///  
        /// This function updates the selected item in the "selection box".
        /// This is called when selection changes or when the combobox 
        /// switches from editable to non-editable or vice versa.
        /// This will also get called in ApplyTemplate in case selection
        /// is set prior to the control being measured.
        ///  
        private void UpdateSelectionBoxItem()
        { 
            // propagate the new selected item to the SelectionBoxItem property; 
            // this displays it in the selection box
            object item = InternalSelectedItem; 
            DataTemplate itemTemplate = ItemTemplate;
            string stringFormat = ItemStringFormat;

            // if Items contains an explicit ContentControl, use its content instead 
            // (this handles the case of ComboBoxItem)
            ContentControl contentControl = item as ContentControl; 
 
            if (contentControl != null)
            { 
                item = contentControl.Content;
                itemTemplate = contentControl.ContentTemplate;
                stringFormat = contentControl.ContentStringFormat;
            } 

            if (_clonedElement != null) 
            { 
                _clonedElement.LayoutUpdated -= CloneLayoutUpdated;
                _clonedElement = null; 
            }

            if (itemTemplate == null && ItemTemplateSelector == null && stringFormat == null)
            { 
                // if the item is a logical element it cannot be displayed directly in
                // the selection box because it already belongs to the tree (in the dropdown box). 
                // Instead, try to extract some useful text from the visual. 
                DependencyObject logicalElement = item as DependencyObject;
 
                if (logicalElement != null)
                {
                    // If the item is a UIElement, create a copy using a visual brush
                    _clonedElement = logicalElement as UIElement; 

                    if (_clonedElement != null) 
                    { 
                        // Create visual copy of selected element
                        VisualBrush visualBrush = new VisualBrush(_clonedElement); 
                        visualBrush.Stretch = Stretch.None;

                        //Set position and dimension of content
                        visualBrush.ViewboxUnits = BrushMappingMode.Absolute; 
                        visualBrush.Viewbox = new Rect(_clonedElement.RenderSize);
 
                        //Set position and dimension of tile 
                        visualBrush.ViewportUnits = BrushMappingMode.Absolute;
                        visualBrush.Viewport = new Rect(_clonedElement.RenderSize); 

                        // If the FlowDirection on cloned element doesn't match the combobox's apply a mirror
                        // If the FlowDirection on cloned element doesn't match its parent's apply a mirror
                        // If both are true, they cancel out so no mirror should be applied 
                        FlowDirection elementFD = (FlowDirection)_clonedElement.GetValue(FlowDirectionProperty);
                        DependencyObject parent = VisualTreeHelper.GetParent(_clonedElement); 
                        FlowDirection parentFD = parent == null ? FlowDirection : (FlowDirection)parent.GetValue(FlowDirectionProperty); 
                        if ((elementFD != this.FlowDirection) != (elementFD != parentFD))
                        { 
                            visualBrush.Transform = new MatrixTransform(new Matrix(-1.0, 0.0, 0.0, 1.0, _clonedElement.RenderSize.Width, 0.0));
                        }

                        // Apply visual brush to a rectangle 
                        Rectangle rect = new Rectangle();
                        rect.Fill = visualBrush; 
                        rect.Width = _clonedElement.RenderSize.Width; 
                        rect.Height = _clonedElement.RenderSize.Height;
 
                        _clonedElement.LayoutUpdated += CloneLayoutUpdated;

                        item = rect;
                        itemTemplate = null; 
                    }
                    else 
                    { 
                        item = ExtractString(logicalElement);
                        itemTemplate = ContentPresenter.StringContentTemplate; 
                    }
                }
            }
 
            // display a null item by an empty string
            if (item == null) 
            { 
                item = String.Empty;
                itemTemplate = ContentPresenter.StringContentTemplate; 
            }

            SelectionBoxItem = item;
            SelectionBoxItemTemplate = itemTemplate; 
            SelectionBoxItemStringFormat = stringFormat;
        } 
 
        // Update our clone's size to match the actual object's size
        private void CloneLayoutUpdated(object sender, EventArgs e) 
        {
            Rectangle rect = (Rectangle)SelectionBoxItem;
            rect.Width = _clonedElement.RenderSize.Width;
            rect.Height = _clonedElement.RenderSize.Height; 

            VisualBrush visualBrush = (VisualBrush)rect.Fill; 
            visualBrush.Viewbox = new Rect(_clonedElement.RenderSize); 
            visualBrush.Viewport = new Rect(_clonedElement.RenderSize);
        } 

        #endregion

        #region Protected Methods 

        ///  
        ///     If control has a scrollviewer in its style and has a custom keyboard scrolling behavior when HandlesScrolling should return true. 
        /// Then ScrollViewer will not handle keyboard input and leave it up to the control.
        ///  
        protected internal override bool HandlesScrolling
        {
            get { return true; }
        } 

        ///  
        /// Prepare the element to display the item.  This may involve 
        /// applying styles, setting bindings, etc.
        ///  
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
 
            if (item is Separator)
                Separator.PrepareContainer(element as Control); 
        } 

        ///  
        ///     Called when this element or any below gets focus.
        /// 
        private static void OnGotFocus(object sender, RoutedEventArgs e)
        { 
            // When ComboBox gets logical focus, select the text inside us.
            ComboBox comboBox = (ComboBox)sender; 
 
            // If we're an editable combobox, forward focus to the TextBox element
            if (!e.Handled) 
            {
                if (comboBox.IsEditable && comboBox.EditableTextBoxSite != null)
                {
                    if (e.OriginalSource == comboBox) 
                    {
                        comboBox.EditableTextBoxSite.Focus(); 
                        e.Handled = true; 
                    }
                    else if (e.OriginalSource == comboBox.EditableTextBoxSite) 
                    {
                        comboBox.EditableTextBoxSite.SelectAll();
                    }
                } 
            }
        } 
 
        /// 
        ///     Called when an item is being focused 
        /// 
        internal override void FocusItem(object item, ItemNavigateArgs itemNavigateArgs)
        {
            // The base implementation sets focus, and we don't want to do that 
            // if we're editable.
            if (!IsEditable) 
            { 
                base.FocusItem(item, itemNavigateArgs);
            } 

            ComboBoxItem highlightedElement = ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
            HighlightedElement = highlightedElement;
 
            // When IsEditable is 'true', we'll always want to commit the selection. e.g. when user press KeyUp/Down.
            // However, when IsEditable is 'false' and Dropdown is open, we could get here when user navigate to 
            // the item using ITS. In this case, we don't want to commit the selection. 
            if ((IsEditable || (!IsDropDownOpen)) && itemNavigateArgs.DeviceUsed is KeyboardDevice)
            { 
                if (highlightedElement != null)
                {
                    SelectedIndex = ItemContainerGenerator.IndexFromContainer(highlightedElement);
                } 
                else
                { 
                    SelectedIndex = Items.IndexOf(item); 
                }
            } 
        }


        ///  
        ///     An event reporting that the IsKeyboardFocusWithin property changed.
        ///  
        protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e) 
        {
            base.OnIsKeyboardFocusWithinChanged(e); 

            // This is for the case when focus goes elsewhere and the popup is still open; make sure it is closed.
            if (IsDropDownOpen && !IsKeyboardFocusWithin)
            { 
                // IsKeyboardFocusWithin still flickers under certain conditions.  The case
                // we care about is focus going from the ComboBox to a ComboBoxItem. 
                // Here we can just check if something has focus and if it's a child 
                // of ours or is a context menu that opened below us.
                DependencyObject currentFocus = Keyboard.FocusedElement as DependencyObject; 
                if (currentFocus == null || (!IsContextMenuOpen && ItemsControlFromItemContainer(currentFocus) != this))
                {
                    Close();
                } 
            }
 
            CoerceValue(IsSelectionBoxHighlightedProperty); 
        }
 
        /// 
        ///     An event reporting a mouse wheel rotation.
        /// 
        private static void OnMouseWheel(object sender, MouseWheelEventArgs e) 
        {
            ComboBox comboBox = (ComboBox)sender; 
 
            // If we get a mouse wheel event we should scroll when the
            // drop down is closed and eat the mouse wheel when it's open. 
            // (If the drop down is open and has a scrollviewer, the scrollviewer
            // will handle it before we get here).
            // We should only do this when focus is within the combobox,
            // otherwise we could severely confuse the user. 
            if (comboBox.IsKeyboardFocusWithin)
            { 
                if (!comboBox.IsDropDownOpen) 
                {
                    // Negative delta means "down", which means we should move next in that case. 
                    if (e.Delta < 0)
                    {
                        comboBox.SelectNext();
                    } 
                    else
                    { 
                        comboBox.SelectPrev(); 
                    }
                } 

                e.Handled = true;
            }
            else 
            {
                // If focus isn't within the combobox (say, we're not focusable) 
                // but we get a mouse wheel event, we should do nothing unless 
                // the drop down is open, in which case we should eat it.
                if (comboBox.IsDropDownOpen) 
                {
                    e.Handled = true;
                }
            } 
        }
 
        private static void OnContextMenuOpen(object sender, ContextMenuEventArgs e) 
        {
            ((ComboBox)sender).IsContextMenuOpen = true; 
        }

        private static void OnContextMenuClose(object sender, ContextMenuEventArgs e)
        { 
            ((ComboBox)sender).IsContextMenuOpen = false;
        } 
 
        /// 
        /// Called when IsMouseCaptured changes on this element. 
        /// 
        /// 
        protected override void OnIsMouseCapturedChanged(DependencyPropertyChangedEventArgs e)
        { 
            // When we take capture, we should start a timer to call
            // us back and do auto scrolling behavior. 
            if (IsMouseCaptured) 
            {
                Debug.Assert(_autoScrollTimer == null, "IsMouseCaptured went from true to true"); 
                if (_autoScrollTimer == null)
                {
                    _autoScrollTimer = new DispatcherTimer(DispatcherPriority.SystemIdle);
                    _autoScrollTimer.Interval = AutoScrollTimeout; 
                    _autoScrollTimer.Tick += new EventHandler(OnAutoScrollTimeout);
                    _autoScrollTimer.Start(); 
                } 
            }
            else 
            {
                if (_autoScrollTimer != null)
                {
                    _autoScrollTimer.Stop(); 
                    _autoScrollTimer = null;
                } 
            } 

            base.OnIsMouseCapturedChanged(e); 
        }

        #endregion
 
        #region Internal Methods
 
        // Helper function called by ComboBoxItem when it receives a MouseDown 
        internal void NotifyComboBoxItemMouseDown(ComboBoxItem comboBoxItem)
        { 
        }

        // Helper function called by ComboBoxItem when it receives a MouseUp
        internal void NotifyComboBoxItemMouseUp(ComboBoxItem comboBoxItem) 
        {
            object item = ItemContainerGenerator.ItemFromContainer(comboBoxItem); 
            if (item != null) 
            {
                SelectionChange.SelectJustThisItem(item, true /* assumeInItemsCollection */); 
            }

            Close();
        } 

        // Called when Item is entered via mouse or keyboard focus 
        internal void NotifyComboBoxItemEnter(ComboBoxItem item) 
        {
            // When a ComboBoxItem is entered, it should be highlighted (and focused). 
            // Note: We may reach this before a nested combo box can grab capture
            //       if one of its items releases capture.  In this case, ignore the
            //       enter event
            if (IsDropDownOpen && Mouse.Captured == this && DidMouseMove()) 
            {
                HighlightedElement = item; 
                if (!IsEditable && !item.IsKeyboardFocusWithin) 
                {
                    item.Focus(); 
                }
            }
        }
 
        /// 
        /// Return true if the item is (or is eligible to be) its own ItemUI 
        ///  
        protected override bool IsItemItsOwnContainerOverride(object item)
        { 
            return (item is ComboBoxItem);
        }

        ///  Create or identify the element used to display the given item.  
        protected override DependencyObject GetContainerForItemOverride()
        { 
            return new ComboBoxItem(); 
        }
 
        #endregion

        #region Private Methods
 
        private void Initialize()
        { 
            CanSelectMultiple = false; 
        }
 
        /// 
        ///     An event reporting a key was pressed
        /// 
        protected override void OnPreviewKeyDown(KeyEventArgs e) 
        {
            // Only process preview key events if they going to our editable text box 
            if (IsEditable && e.OriginalSource == EditableTextBoxSite) 
            {
                KeyDownHandler(e); 
            }
        }

        ///  
        ///     An event reporting a key was pressed
        ///  
        protected override void OnKeyDown(KeyEventArgs e) 
        {
            KeyDownHandler(e); 
        }

        private void KeyDownHandler(KeyEventArgs e)
        { 
            bool handled = false;
            Key key = e.Key; 
 
            // We want to handle Alt key. Get the real key if it is Key.System.
            if (key == Key.System) 
            {
                key = e.SystemKey;
            }
 
            // In Right to Left mode we switch Right and Left keys
            bool isRTL = (FlowDirection == FlowDirection.RightToLeft); 
 
            switch (key)
            { 
                case Key.Up:
                    handled = true;
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
                    { 
                        KeyboardToggleDropDown(true /* commitSelection */);
                    } 
                    else 
                    {
                        // When the drop down isn't open then focus is on the ComboBox 
                        // and we can't use KeyboardNavigation.
                        if (IsItemsHostVisible)
                        {
                            NavigateByLine(HighlightedItem, FocusNavigationDirection.Up, new ItemNavigateArgs(e.Device, Keyboard.Modifiers)); 
                        }
                        else 
                        { 
                            SelectPrev();
                        } 
                    }

                    break;
 
                case Key.Down:
                    handled = true; 
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) 
                    {
                        KeyboardToggleDropDown(true /* commitSelection */); 
                    }
                    else
                    {
                        if (IsItemsHostVisible) 
                        {
                            NavigateByLine(HighlightedItem, FocusNavigationDirection.Down, new ItemNavigateArgs(e.Device, Keyboard.Modifiers)); 
                        } 
                        else
                        { 
                            SelectNext();
                        }
                    }
 
                    break;
 
                case Key.F4: 
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == 0)
                    { 
                        KeyboardToggleDropDown(true /* commitSelection */);
                        handled = true;
                    }
                    break; 

                case Key.Escape: 
                    if (IsDropDownOpen) 
                    {
                        KeyboardCloseDropDown(false /* commitSelection */); 
                        handled = true;
                    }
                    break;
 
                case Key.Enter:
                    if (IsDropDownOpen) 
                    { 
                        KeyboardCloseDropDown(true /* commitSelection */);
                        handled = true; 
                    }
                    break;

                case Key.Home: 
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt && !IsEditable)
                    { 
                        if (IsItemsHostVisible) 
                        {
                            NavigateToStart(new ItemNavigateArgs(e.Device, Keyboard.Modifiers)); 
                        }
                        else
                        {
                            SelectFirst(); 
                        }
                        handled = true; 
                    } 
                    break;
 
                case Key.End:
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt && !IsEditable)
                    {
                        if (IsItemsHostVisible) 
                        {
                            NavigateToEnd(new ItemNavigateArgs(e.Device, Keyboard.Modifiers)); 
                        } 
                        else
                        { 
                            SelectLast();
                        }
                        handled = true;
                    } 
                    break;
 
                case Key.Right: 
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt && !IsEditable)
                    { 
                        if (IsItemsHostVisible)
                        {
                            NavigateByLine(HighlightedItem, FocusNavigationDirection.Right, new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
                        } 
                        else
                        { 
                            if (!isRTL) 
                            {
                                SelectNext(); 
                            }
                            else
                            {
                                // If it's RTL then Right should go backwards 
                                SelectPrev();
                            } 
                        } 
                        handled = true;
                    } 
                    break;

                case Key.Left:
                    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt && !IsEditable) 
                    {
                        if (IsItemsHostVisible) 
                        { 
                            NavigateByLine(HighlightedItem, FocusNavigationDirection.Left, new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
                        } 
                        else
                        {
                            if (!isRTL)
                            { 
                                SelectPrev();
                            } 
                            else 
                            {
                                // If it's RTL then Left should go the other direction 
                                SelectNext();
                            }
                        }
                        handled = true; 
                    }
                    break; 
 
                case Key.PageUp:
                    if (IsItemsHostVisible) 
                    {
                        NavigateByPage(HighlightedItem, FocusNavigationDirection.Up, new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
                        handled = true;
                    } 
                    break;
 
                case Key.PageDown: 
                    if (IsItemsHostVisible)
                    { 
                        NavigateByPage(HighlightedItem, FocusNavigationDirection.Down, new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
                        handled = true;
                    }
                    break; 

                case Key.Oem5: 
                    if ((Keyboard.Modifiers & ModifierKeys.Control) == (ModifierKeys.Control)) 
                    {
                        // Scroll into view the selected item -- we want to highlight the item 
                        // that we scroll in, so we should navigate to it.
                        NavigateToItem(InternalSelectedItem, ItemNavigateArgs.Empty);
                        handled = true;
                    } 
                    break;
 
                default: 
                    handled = false;
                    break; 
            }
            if (handled)
            {
                e.Handled = true; 
            }
        } 
 
        private void SelectPrev()
        { 
            if (!Items.IsEmpty)
            {
                int selectedIndex = InternalSelectedIndex;
 
                // Search backwards from SelectedIndex - 1 but don't start before the beginning.
                // If SelectedIndex is less than 0, there is nothing to select before this item. 
                if (selectedIndex > 0) 
                {
                    SelectItemHelper(selectedIndex - 1, -1, -1); 
                }
            }
        }
 
        private void SelectNext()
        { 
            int count = Items.Count; 
            if (count > 0)
            { 
                int selectedIndex = InternalSelectedIndex;

                // Search forwards from SelectedIndex + 1 but don't start past the end.
                // If SelectedIndex is before the last item then there is potentially 
                // something afterwards that we could select.
                if (selectedIndex < count - 1) 
                { 
                    SelectItemHelper(selectedIndex + 1, +1, count);
                } 
            }
        }

        private void SelectFirst() 
        {
            SelectItemHelper(0, +1, Items.Count); 
        } 

        private void SelectLast() 
        {
            SelectItemHelper(Items.Count - 1, -1, -1);
        }
 
        // Walk in the specified direction until we get to a selectable
        // item or to the stopIndex. 
        // NOTE: stopIndex is not inclusive (it should be one past the end of the range) 
        private void SelectItemHelper(int startIndex, int increment, int stopIndex)
        { 
            Debug.Assert((increment > 0 && startIndex <= stopIndex) || (increment < 0 && startIndex >= stopIndex), "Infinite loop detected");

            for (int i = startIndex; i != stopIndex; i += increment)
            { 
                // If the item is selectable and the wrapper is selectable, select it.
                // Need to check both because the user could set any combination of 
                // IsSelectable and IsEnabled on the item and wrapper. 
                object item = Items[i];
                if (IsSelectableHelper(item) && IsSelectableHelper(ItemContainerGenerator.ContainerFromIndex(i))) 
                {
                    SelectionChange.SelectJustThisItem(item, true /* assumeInItemsCollection */);
                    break;
                } 
            }
        } 
 
        private bool IsSelectableHelper(object o)
        { 
            DependencyObject d = o as DependencyObject;
            // If o is not a DependencyObject, it is just a plain
            // object and must be selectable and enabled.
            if (d == null) 
            {
                return true; 
            } 
            // It's selectable if IsSelectable is true and IsEnabled is true.
            return (bool)d.GetValue(FrameworkElement.IsEnabledProperty); 
        }

        private static string ExtractString(DependencyObject d)
        { 
            TextBlock text;
            Visual visual; 
            TextElement textElement; 
            string strValue = String.Empty;
 
            if ((text = d as TextBlock) != null)
            {
                strValue = text.Text;
            } 
            else if ((visual = d as Visual) != null)
            { 
                int count = VisualTreeHelper.GetChildrenCount(visual); 
                for(int i = 0; i < count; i++)
                { 
                    strValue += ExtractString((DependencyObject)(VisualTreeHelper.GetChild(visual, i)));
                }
            }
            else if ((textElement = d as TextElement) != null) 
            {
                strValue += TextRangeBase.GetTextInternal(textElement.ContentStart, textElement.ContentEnd); 
            } 

            return strValue; 
        }


        ///  
        /// Called when the Template's tree has been generated
        ///  
        public override void OnApplyTemplate() 
        {
            base.OnApplyTemplate(); 

            if (_dropDownPopup != null)
            {
                _dropDownPopup.Closed -= OnPopupClosed; 
            }
 
            EditableTextBoxSite = GetTemplateChild(EditableTextBoxTemplateName) as TextBox; 
            _dropDownPopup = GetTemplateChild(PopupTemplateName) as Popup;
 
            // EditableTextBoxSite should have been set by now if it's in the visual tree
            if (EditableTextBoxSite != null)
            {
                EditableTextBoxSite.TextChanged += new TextChangedEventHandler(OnEditableTextBoxTextChanged); 
                EditableTextBoxSite.SelectionChanged += new RoutedEventHandler(OnEditableTextBoxSelectionChanged);
            } 
 
            if (_dropDownPopup != null)
            { 
                _dropDownPopup.Closed += OnPopupClosed;
            }

            Update(); 
        }
 
        internal override void OnTemplateChangedInternal(FrameworkTemplate oldTemplate, FrameworkTemplate newTemplate) 
        {
            base.OnTemplateChangedInternal(oldTemplate, newTemplate); 

            // This is called when a template is applied but before the new template has been inflated.

            // If we had a style before, detach from event handlers 
            if (EditableTextBoxSite != null)
            { 
                EditableTextBoxSite.TextChanged -= new TextChangedEventHandler(OnEditableTextBoxTextChanged); 
                EditableTextBoxSite.SelectionChanged -= new RoutedEventHandler(OnEditableTextBoxSelectionChanged);
            } 
        }

        #region Capture
 
        private static void OnLostMouseCapture(object sender, MouseEventArgs e)
        { 
            ComboBox comboBox = (ComboBox)sender; 

            // ISSUE (jevansa) -- task 22022: 
            //        We need a general mechanism to do this, or at the very least we should
            //        share it amongst the controls which need it (Popup, MenuBase, ComboBox).
            if (Mouse.Captured != comboBox)
            { 
                if (e.OriginalSource == comboBox)
                { 
                    // If capture is null or it's not below the combobox, close. 
                    // More workaround for task 22022 -- check if it's a descendant (following Logical links too)
                    if (Mouse.Captured == null || !MenuBase.IsDescendant(comboBox, Mouse.Captured as DependencyObject)) 
                    {
                        comboBox.Close();
                    }
                } 
                else
                { 
                    if (MenuBase.IsDescendant(comboBox, e.OriginalSource as DependencyObject)) 
                    {
                        // Take capture if one of our children gave up capture (by closing their drop down) 
                        if (comboBox.IsDropDownOpen && Mouse.Captured == null && MS.Win32.SafeNativeMethods.GetCapture() == IntPtr.Zero)
                        {
                            Mouse.Capture(comboBox, CaptureMode.SubTree);
                            e.Handled = true; 
                        }
                    } 
                    else 
                    {
                        comboBox.Close(); 
                    }
                }
            }
        } 

        private static void OnMouseButtonDown(object sender, MouseButtonEventArgs e) 
        { 
            ComboBox comboBox = (ComboBox)sender;
 
            // If we (or one of our children) are clicked, claim the focus (don't steal focus if our context menu is clicked)
            if (!comboBox.IsContextMenuOpen && !comboBox.IsKeyboardFocusWithin)
            {
                comboBox.Focus(); 
            }
 
            e.Handled = true;   // Always handle so that parents won't take focus away 

            // Note: This half should be moved into OnMouseDownOutsideCapturedElement 
            // When we have capture, all clicks off the popup will have the combobox as
            // the OriginalSource.  So when the original source is the combobox, that
            // means the click was off the popup and we should dismiss.
            if (Mouse.Captured == comboBox && e.OriginalSource == comboBox) 
            {
                comboBox.Close(); 
                Debug.Assert(!comboBox.CheckAccess() || Mouse.Captured != comboBox, "On the dispatcher thread, ComboBox should not have capture after closing the dropdown"); 
            }
        } 

        private static void OnPreviewMouseButtonDown(object sender, MouseButtonEventArgs e)
        {
            ComboBox comboBox = (ComboBox)sender; 

            if (comboBox.IsEditable) 
            { 
                Visual originalSource = e.OriginalSource as Visual;
                Visual textBox = comboBox.EditableTextBoxSite; 

                if (originalSource != null && textBox != null
                    && textBox.IsAncestorOf(originalSource))
                { 
                    if (comboBox.IsDropDownOpen && !comboBox.StaysOpenOnEdit)
                    { 
                        // When combobox is not editable, clicks anywhere outside 
                        // the combobox will close it.  When the combobox is editable
                        // then clicking the text box should close the combobox as well. 
                        comboBox.Close();
                    }
                    else if (!comboBox.IsContextMenuOpen && !comboBox.IsKeyboardFocusWithin)
                    { 
                        // If textBox is clicked, claim focus
                        comboBox.Focus(); 
                        e.Handled = true;   // Handle so that textbox won't try to update cursor position 
                    }
                } 
            }
        }

 
        /// 
        ///     An event reporting the left mouse button was released. 
        ///  
        /// 
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) 
        {
            // Ignore the first mouse button up if we haven't gone over the popup yet
            // And ignore all mouse ups over the items host.
            if (HasMouseEnteredItemsHost && !IsMouseOverItemsHost) 
            {
                if (IsDropDownOpen) 
                { 
                    Close();
                    e.Handled = true; 
                    Debug.Assert(!CheckAccess() || Mouse.Captured != this, "On the dispatcher thread, ComboBox should not have capture after closing the dropdown");
                }
            }
 
            base.OnMouseLeftButtonUp(e);
        } 
 
        private static void OnMouseMove(object sender, MouseEventArgs e)
        { 
            ComboBox comboBox = (ComboBox)sender;

            // The mouse moved, see if we're over the items host yet
            if (comboBox.IsDropDownOpen) 
            {
                bool isMouseOverItemsHost = comboBox.ItemsHost != null ? comboBox.ItemsHost.IsMouseOver : false; 
 
                // When mouse enters items host, start tracking mouse movements
                if (isMouseOverItemsHost && !comboBox.HasMouseEnteredItemsHost) 
                {
                    comboBox.SetInitialMousePosition();
                }
 
                comboBox.IsMouseOverItemsHost = isMouseOverItemsHost;
                comboBox.HasMouseEnteredItemsHost |= isMouseOverItemsHost; 
            } 

            // If we get a mouse move and we have capture, then the mouse was 
            // outside the ComboBox.  We should autoscroll.
            if (Mouse.LeftButton == MouseButtonState.Pressed && comboBox.HasMouseEnteredItemsHost)
            {
                if (Mouse.Captured == comboBox) 
                {
                    if (Mouse.LeftButton == MouseButtonState.Pressed) 
                    { 
                        comboBox.DoAutoScroll(comboBox.HighlightedItem);
                    } 
                    else
                    {
                        // We missed the mouse up, release capture
                        comboBox.ReleaseMouseCapture(); 
                        comboBox.ResetLastMousePosition();
                    } 
 

                    e.Handled = true; 
                }
            }
        }
 
        /// 
        /// Called to toggle the DropDown using the keyboard. 
        ///  
        private void KeyboardToggleDropDown(bool commitSelection)
        { 
            KeyboardToggleDropDown(!IsDropDownOpen, commitSelection);
        }

        ///  
        /// Called to close the DropDown using the keyboard.
        ///  
        private void KeyboardCloseDropDown(bool commitSelection) 
        {
            KeyboardToggleDropDown(false /* openDropDown */, commitSelection); 
        }

        private void KeyboardToggleDropDown(bool openDropDown, bool commitSelection)
        { 
            // Close the dropdown and commit the selection if requested.
            // Make sure to set the selection after the dropdown has closed 
            // so we don't trigger any unnecessary navigation as a result 
            // of changing the selection.
            object itemToSelect = null; 
            if (commitSelection)
            {
                itemToSelect = HighlightedItem;
            } 

            IsDropDownOpen = openDropDown; 
 
            if (openDropDown == false && commitSelection && (itemToSelect != null))
            { 
                SelectionChange.SelectJustThisItem(itemToSelect, true /* assumeInItemsCollection */);
            }
        }
 
        private void CommitSelection()
        { 
            object itemToSelect = HighlightedItem; 
            if (itemToSelect != null)
            { 
                SelectionChange.SelectJustThisItem(itemToSelect, true /* assumeInItemsCollection */);
            }
        }
 
        private void OnAutoScrollTimeout(object sender, EventArgs e)
        { 
            if (Mouse.LeftButton == MouseButtonState.Pressed 
                && HasMouseEnteredItemsHost)
            { 
                DoAutoScroll(HighlightedItem);
            }
        }
 
        private void Close()
        { 
            if (IsDropDownOpen) 
            {
                ClearValue(IsDropDownOpenProperty); 

                // Force Close in case of triggers
                if (IsDropDownOpen)
                    IsDropDownOpen = false; 
            }
        } 
 
        #endregion
 
        #endregion

        #region Accessibility
 
        /// 
        /// Creates AutomationPeer () 
        ///  
        protected override AutomationPeer OnCreateAutomationPeer()
        { 
            return new ComboBoxAutomationPeer(this);
        }

        #endregion 

        #region Private Properties 
 
        internal TextBox EditableTextBoxSite
        { 
            get
            {
                return _editableTextBoxSite;
            } 
            set
            { 
                _editableTextBoxSite = value; 
            }
        } 

        private bool HasCapture
        {
            get 
            {
                return Mouse.Captured == this; 
            } 
        }
 
        /// 
        /// Returns true if the ItemsHost is visually connected to the RootVisual of its PresentationSource.
        /// 
        ///  
        /// 
        ///     Critical: This code accesses HwndSource from the call PresentationSource.CriticalFromVisual 
        ///     TreatAsSafe: It does not expose the critical data 
        /// 
        private bool IsItemsHostVisible 
        {
            [SecurityCritical,SecurityTreatAsSafe]
            get
            { 
                Panel itemsHost = ItemsHost;
                if (itemsHost != null) 
                { 
                    HwndSource source = PresentationSource.CriticalFromVisual(itemsHost) as HwndSource;
 
                    if (source != null && !source.IsDisposed && source.RootVisual != null)
                    {
                        return source.RootVisual.IsAncestorOf(itemsHost);
                    } 
                }
 
                return false; 
            }
        } 

        private ComboBoxItem HighlightedElement
        {
            get 
            {
                return _highlightedElement != null ? _highlightedElement.Target as ComboBoxItem : null; 
            } 
            set
            { 
                ComboBoxItem current = (_highlightedElement != null) ? (_highlightedElement.Target as ComboBoxItem) : null;

                if (current != null)
                { 
                    current.SetIsHighlighted(false);
                } 
 
                if (value != null)
                { 
                    _highlightedElement = new WeakReference(value);
                    value.SetIsHighlighted(true);
                }
                else 
                {
                    _highlightedElement = null; 
                } 

                CoerceValue(IsSelectionBoxHighlightedProperty); 
            }
        }

        private object HighlightedItem 
        {
            get 
            { 
                ComboBoxItem highlightedElement = HighlightedElement;
                if (highlightedElement != null) 
                {
                    object item = ItemContainerGenerator.ItemFromContainer(highlightedElement);
                    Debug.Assert(item != DependencyProperty.UnsetValue, "No item in container HighlightedElement");
 
                    return item;
                } 
 
                return null;
            } 
        }

        private bool IsMouseOverItemsHost
        { 
            get { return _cacheValid[(int)CacheBits.IsMouseOverItemsHost]; }
            set { _cacheValid[(int)CacheBits.IsMouseOverItemsHost] = value; } 
        } 

        private bool HasMouseEnteredItemsHost 
        {
            get { return _cacheValid[(int)CacheBits.HasMouseEnteredItemsHost]; }
            set { _cacheValid[(int)CacheBits.HasMouseEnteredItemsHost] = value; }
        } 

        private bool IsContextMenuOpen 
        { 
            get { return _cacheValid[(int)CacheBits.IsContextMenuOpen]; }
            set { _cacheValid[(int)CacheBits.IsContextMenuOpen] = value; } 
        }

        // Used to indicate that the Text Properties are changing
        // Don't reenter callbacks 
        private bool UpdatingText
        { 
            get { return _cacheValid[(int)CacheBits.UpdatingText]; } 
            set { _cacheValid[(int)CacheBits.UpdatingText] = value; }
        } 

        // Selected item is being updated; Don't reenter callbacks
        private bool UpdatingSelectedItem
        { 
            get { return _cacheValid[(int)CacheBits.UpdatingSelectedItem]; }
            set { _cacheValid[(int)CacheBits.UpdatingSelectedItem] = value; } 
        } 

        #endregion 

        #region Private Members

        private const string EditableTextBoxTemplateName = "PART_EditableTextBox"; 
        private const string PopupTemplateName = "PART_Popup";
 
        private TextBox _editableTextBoxSite; 
        private Popup _dropDownPopup;
        private int _textBoxSelectionStart; // the location of selection before call to TextUpdated. 
        private BitVector32 _cacheValid = new BitVector32(0);   // Condense boolean bits
        private WeakReference _highlightedElement; // a WeakReference to the ComboBoxItem which is "highlighted"
        private DispatcherTimer _autoScrollTimer;
        private UIElement _clonedElement; 

        private enum CacheBits 
        { 
            IsMouseOverItemsHost        = 0x01,
            HasMouseEnteredItemsHost    = 0x02, 
            IsContextMenuOpen           = 0x04,
            UpdatingText                = 0x08,
            UpdatingSelectedItem        = 0x10,
        } 

        #endregion Private Members 
 
        #region DTypeThemeStyleKey
 
        // Returns the DependencyObjectType for the registered ThemeStyleKey's default
        // value. Controls will override this method to return approriate types.
        internal override DependencyObjectType DTypeThemeStyleKey
        { 
            get { return _dType; }
        } 
 
        private static DependencyObjectType _dType;
 
        #endregion DTypeThemeStyleKey
    }
}
 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK