WindowsListBox.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / AccessibleTech / longhorn / Automation / Win32Providers / MS / Internal / AutomationProxies / WindowsListBox.cs / 1 / WindowsListBox.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: HWND-based ListBox Proxy 
//
// History: 
//          Kris Krueger            Created
//          [....].  (Entirely revised to split the ListBox and ListBoxItems in different class
//          alexsn                  selection
// 
//---------------------------------------------------------------------------
 
using System; 
using System.Collections;
using System.Text; 
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows.Automation;
using System.Windows.Automation.Provider; 
using System.Windows;
using Accessibility; 
using MS.Win32; 

namespace MS.Internal.AutomationProxies 
{
    // This class represents ListBox and ListBox with check buttons.
    class WindowsListBox: ProxyHwnd, ISelectionProvider
    { 
        // -----------------------------------------------------
        // 
        // Constructors 
        //
        // ----------------------------------------------------- 

        #region Constructors

        internal WindowsListBox (IntPtr hwnd, ProxyFragment parent, int item, bool parentedByCombo) 
           : base(hwnd, parent, item)
        { 
            // Set the strings to return properly the properties. 
            _parentedByCombo = parentedByCombo;
            _fIsKeyboardFocusable = true; 
            _cControlType = ControlType.List;
            _fIsContent = !_parentedByCombo;

            if (parentedByCombo) 
            {
                _sAutomationId = "ListBox"; // This string is a non-localizable string 
            } 

            // support for events 
            _createOnEvent = new WinEventTracker.ProxyRaiseEvents (RaiseEvents);
        }

        #endregion 

        #region Proxy Create 
 
        // Static Create method called by UIAutomation to create this proxy.
        // returns null if unsuccessful 
        internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild, int idObject)
        {
            return Create(hwnd, idChild);
        } 

        internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild) 
        { 
            bool parentedByCombo = false;
            ProxyFragment parent = null; 
            int item = 0;

            try
            { 
                int style = Misc.GetWindowStyle(hwnd);
                // If can not get windows style the hwnd is bad so do not create a proxy for it. 
                if (style == 0) 
                {
                    return null; 
                }

                if (Misc.IsBitSet(style, NativeMethods.LBS_COMBOBOX))
                { 
                    // List portion of combo box
                    NativeMethods.COMBOBOXINFO cbInfo = new NativeMethods.COMBOBOXINFO(NativeMethods.comboboxInfoSize); 
 
                    if (WindowsComboBox.GetComboInfo(hwnd, ref cbInfo) && (cbInfo.hwndCombo != IntPtr.Zero))
                    { 
                        parent = (ProxyFragment)WindowsComboBox.Create(cbInfo.hwndCombo, 0);
                        parentedByCombo = true;
                        item = (int)WindowsComboBox.ComboChildren.List;
                    } 
                }
            } 
            catch (ElementNotAvailableException) 
            {
                return null; 
            }

            WindowsListBox listbox = new WindowsListBox (hwnd, parent, item, parentedByCombo);
 
            if (idChild == 0)
            { 
                return listbox; 
            }
            else 
            {
                return listbox.CreateListboxItem(idChild - 1);
            }
        } 

        // Static Create method called by the event tracker system 
        // WinEvents are one throwns because items exist. so it makes sense to create the item and 
        // check for details afterward.
        internal static void RaiseEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild) 
        {
            // NOTE: List may be a portion of the Combobox
            // Use WindowsListBox.Create in order to set-up a correct parenthood chain
 
            switch (idObject)
            { 
                case NativeMethods.OBJID_WINDOW: 
                    RaiseEventsOnWindow(hwnd, eventId, idProp, idObject, idChild);
                    break; 

                case NativeMethods.OBJID_CLIENT:
                    RaiseEventsOnClient(hwnd, eventId, idProp, idObject, idChild);
                    break; 

                case NativeMethods.OBJID_VSCROLL : 
                case NativeMethods.OBJID_HSCROLL : 
                    break;
 
                default :
                    ProxySimple el = (ProxyHwnd)WindowsListBox.Create(hwnd, 0);
                    if (el != null)
                    { 
                        el.DispatchEvents(eventId, idProp, idObject, idChild);
                    } 
                    break; 
            }
        } 

        #endregion

        //------------------------------------------------------ 
        //
        //  Patterns Implementation 
        // 
        //-----------------------------------------------------
 
        #region ProxySimple Interface

        // Returns a pattern interface if supported.
        internal override object GetPatternProvider (AutomationPattern iid) 
        {
            // selection is always supported 
            if (iid == SelectionPattern.Pattern) 
            {
                return this; 
            }
            // the scroll pattern is only supported when the list is scrollable.
            else if (iid == ScrollPattern.Pattern && WindowScroll.HasScrollableStyle(_hwnd))
            { 
                // delegate work to the NonClientArea implementation of IScrollProvider
                IScrollProvider scroll = NonClientArea.Create(_hwnd, 0) as IScrollProvider; 
 
                if (scroll != null)
                { 
                    return scroll;
                }
            }
 

            return null; 
        } 

        // Process all the Element Properties 
        internal override object GetElementProperty(AutomationProperty idProp)
        {
            if (idProp == AutomationElement.IsControlElementProperty)
            { 
                return IsParentedByCombo() || SafeNativeMethods.IsWindowVisible(_hwnd);
            } 
            else if (idProp == AutomationElement.IsOffscreenProperty) 
            {
                if (IsParentedByCombo()) 
                {
                    // Since the bounding rectangle of a collapsed listbox protion of a combo-box is still
                    // on the virtal desktop needs to check to make sure the listbox protion is
                    // expanded, i.e. visible. 
                    if (!SafeNativeMethods.IsWindowVisible(_hwnd))
                    { 
                        return true; 
                    }
                } 
            }

            return base.GetElementProperty(idProp);
        } 

        #endregion 
 
        #region ProxyFragment Interface
 
        // ------------------------------------------------------
        //
        // RawElementProvider interface implementation
        // 
        // ------------------------------------------------------
 
        // Returns the next sibling element in the raw hierarchy. 
        // Peripheral controls have always negative values.
        internal override ProxySimple GetNextSibling (ProxySimple child) 
        {
            int item = child._item;
            int count = Length;
 
            // Next for an item that does not exist in the list
            if (item >= count) 
            { 
                throw new ElementNotAvailableException ();
            } 

            if (item >= 0 && (item + 1) < count)
            {
                return CreateListboxItem (item + 1); 
            }
            else 
            { 
                return base.GetNextSibling (child);
            } 
        }

        // Returns the previous sibling element in the raw hierarchy.
        // Peripheral controls have always negative values. 
        internal override ProxySimple GetPreviousSibling (ProxySimple child)
        { 
            // start with the scrollbars 
            ProxySimple ret = base.GetPreviousSibling (child);
 
            if (ret != null)
            {
                return ret;
            } 

            // then try out the items 
            int item = child._item; 
            int count = Length;
 
            // Next for an item that does not exist in the list
            if (item >= count)
            {
                throw new ElementNotAvailableException (); 
            }
 
            if (item > 0 && (item) < count) 
            {
                return CreateListboxItem (item - 1); 
            }
            else
            {
                return item != 0 && count > 0 ? CreateListboxItem (count - 1) : null; 
            }
        } 
 
        // Returns the first child element in the raw hierarchy.
        internal override ProxySimple GetFirstChild () 
        {
            if (Length > 0)
            {
                return CreateListboxItem (0); 
            }
 
            // no content go for the scrollbars 
            return base.GetFirstChild ();
        } 

        // Returns the last child element in the raw hierarchy.
        internal override ProxySimple GetLastChild ()
        { 
            // start with the scrollbars
            ProxySimple ret = base.GetFirstChild (); 
 
            if (ret != null)
            { 
                return ret;
            }

            int count = Length; 

            return count > 0 ? CreateListboxItem (count - 1) : null; 
        } 

        // Returns a Proxy element corresponding to the specified screen coordinates. 
        internal override ProxySimple ElementProviderFromPoint (int x, int y)
        {
            NativeMethods.Win32Rect listboxrect = new NativeMethods.Win32Rect ();
 
            Misc.GetClientRectInScreenCoordinates(_hwnd, ref listboxrect);
            if (Misc.PtInRect(ref listboxrect, x, y)) 
            { 
                int ret = Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_ITEMFROMPOINT, IntPtr.Zero, NativeMethods.Util.MAKELPARAM(x - listboxrect.left, y - listboxrect.top));
                if (NativeMethods.Util.HIWORD(ret) == 0) 
                {
                    int index = NativeMethods.Util.LOWORD(ret);
                    return CreateListboxItem(index);
                } 
            }
 
            return base.ElementProviderFromPoint (x, y); 
        }
 
        // Returns an item corresponding to the focused element (if there is one),
        // or null otherwise.
        internal override ProxySimple GetFocus ()
        { 
            int index = Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_GETCARETINDEX, IntPtr.Zero, IntPtr.Zero);
 
            if (index != NativeMethods.LB_ERR) 
            {
                return CreateListboxItem(index); 
            }

            return this;
        } 

        #endregion 
 
        #region Selection Pattern
 
        // -----------------------------------------------------
        //
        // ISelectionProvider interface implementation
        // 
        // ------------------------------------------------------
 
        // Returns an enumerator over the current selection. 
        IRawElementProviderSimple[] ISelectionProvider.GetSelection()
        { 
            int count = Length;
            int countSelection = IsMultipleSelection() ? GetSelectionCount() : 1;

            if (count <= 0 || countSelection <= 0) 
            {
                return null; 
            } 

            IRawElementProviderSimple[] selection = new IRawElementProviderSimple[countSelection]; 

            int index = 0;
            for (int itemPos = 0; itemPos < count; itemPos++)
            { 
                if (ListboxItem.IsSelected(_hwnd, itemPos))
                { 
                    selection[index] = CreateListboxItem(itemPos); 
                    index++;
                } 
            }

            if (index == 0)
            { 
                return null;
            } 
 
            return selection;
        } 

        // Returns whether the control supports multiple selection.
        bool ISelectionProvider.CanSelectMultiple
        { 
            get
            { 
                return IsMultipleSelection (); 
            }
        } 

        // Returns whether the control requires a minimum of one
        // selected element at all times.
        bool ISelectionProvider.IsSelectionRequired 
        {
            // If ListBox supports multipleselection - this property always returns false 
            // since user can unselect everything using the Ctrl + Click 
            // This property is dynamic in the case of single-selected listbox.
            // This should be documented, user should not cached this value (single-selection lb) 
            get
            {
                if (IsMultipleSelection ())
                { 
                    return false;
                } 
 
                return Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero) >= 0;
            } 
        }

        #endregion
 
        // -----------------------------------------------------
        // 
        // Protected Methods 
        //
        // ----------------------------------------------------- 

        #region Protected Methods

        // Picks a WinEvent to track for a UIA property 
        protected override int[] PropertyToWinEvent(AutomationProperty idProp)
        { 
            // Upon creation, a single selection Listbox can have no selection to start with 
            // however once an item has been selection, the selection cannot be removed.
            // The notification handler is set based on the type of Listbox (single selection) 
            // and if nothing is selected.
            if (idProp == SelectionPattern.IsSelectionRequiredProperty && !IsMultipleSelection() && !HasSelection())
            {
                return new int[] { NativeMethods.EventObjectSelection }; 
            }
 
            return base.PropertyToWinEvent(idProp); 
        }
 
        #endregion

        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //------------------------------------------------------ 

        #region Private Method 

        // Creates a list item RawElementBase Item
        private ProxySimple CreateListboxItem (int index)
        { 
            return new ListboxItem (_hwnd, this, index);
        } 
 
        // Return the number of items (non peripheral) in the listbox.
        private int Length 
        {
            get
            {
                return Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero); 
            }
        } 
 
        private bool IsParentedByCombo ()
        { 
            return _parentedByCombo;
        }

        private static void RaiseEventsOnClient(IntPtr hwnd, int eventId, object idProp, int idObject, int idChild) 
        {
            ProxySimple el = null; 
 
            WindowsListBox wlb = (WindowsListBox)WindowsListBox.Create(hwnd, 0);
 
            // Upon creation, a single selection Listbox can have no selection to start with
            // however once an item has been selection, the selection cannot be removed.
            // This WinEvent can only be received once, on the first selection.
            // Once the notification is received the notification handler is removed to not get it a 
            // second time.
            if ((eventId == NativeMethods.EventObjectSelection || eventId == NativeMethods.EventObjectSelectionAdd) && (idProp as AutomationProperty) == SelectionPattern.IsSelectionRequiredProperty) 
            { 
                // This array must be kept in sync with the array in PropertyToWinEvent
                WinEventTracker.EvtIdProperty[] aEvtIdProperties = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectSelection, SelectionPattern.IsSelectionRequiredProperty) }; 

                WinEventTracker.RemoveToNotificationList(hwnd, aEvtIdProperties, null, aEvtIdProperties.Length);
                el = wlb;
            } 
            else if (eventId == NativeMethods.EventObjectSelection || eventId == NativeMethods.EventObjectSelectionRemove || eventId == NativeMethods.EventObjectSelectionAdd)
            { 
                bool isMultipleSelection = wlb.IsMultipleSelection(); 

                // User should send SelectionAdd for a Multiselect listbox but it sends instead 
                // Selection. The code below fixes the bug in User
                if (eventId == NativeMethods.EventObjectSelection && isMultipleSelection && wlb.HasOtherSelections(idChild - 1))
                {
                    eventId = NativeMethods.EventObjectSelectionAdd; 
                }
 
                // The spec says a ElementSelectionEvent should be fired when action causes only one 
                // selection.
                if ((eventId == NativeMethods.EventObjectSelectionRemove || eventId == NativeMethods.EventObjectSelectionAdd) && 
                    isMultipleSelection && wlb.GetSelectionCount() == 1)
                {
                    // The net result of the user action is that there is only one item selected in the
                    // listbox, so change the event to an EventObjectSelected. 
                    idProp = SelectionItemPattern.ElementSelectedEvent;
                    eventId = NativeMethods.EventObjectSelection; 
 
                    // Now need to find what item is selected.
                    int selection = wlb.GetOtherSelection(idChild - 1); 
                    if (selection != NativeMethods.LB_ERR)
                    {
                        idChild = selection;
                    } 
                }
 
                el = wlb.CreateListboxItem(idChild - 1); 
            }
            else 
            {
                el = wlb;
            }
 
            // Special case for logical element change for listbox item
            if ((idProp as AutomationEvent) == AutomationElement.StructureChangedEvent && 
                (eventId == NativeMethods.EventObjectDestroy || eventId == NativeMethods.EventObjectCreate)) 
            {
                // Since children are referenced by position in the tree, addition and removal 
                // of items leads to different results when asking properties for the same element
                // On removal, item + 1 is now item!
                // Use Children Invalidated to let the client knows that all the cached
                AutomationInteropProvider.RaiseStructureChangedEvent( wlb, new StructureChangedEventArgs( StructureChangeType.ChildrenInvalidated, wlb.MakeRuntimeId() ) ); 
                return;
            } 
 
            if (el != null)
            { 
                el.DispatchEvents(eventId, idProp, idObject, idChild);
            }
        }
 
        private static void RaiseEventsOnWindow(IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        { 
            AutomationProperty automationProperty = idProp as AutomationProperty; 
            ProxySimple el = null;
 
            if ((eventId == NativeMethods.EventObjectShow || eventId == NativeMethods.EventObjectHide) &&
                (automationProperty != null && automationProperty == ExpandCollapsePattern.ExpandCollapseStateProperty))
            {
                if (Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LBS_COMBOBOX)) 
                {
                    // List portion of combo: We'll hit it in the case when user hovers over it 
                    NativeMethods.COMBOBOXINFO cbInfo = new NativeMethods.COMBOBOXINFO(NativeMethods.comboboxInfoSize); 

                    if (WindowsComboBox.GetComboInfo(hwnd, ref cbInfo) && (cbInfo.hwndCombo != IntPtr.Zero)) 
                    {
                        WindowsComboBox cb = (WindowsComboBox)WindowsComboBox.Create(cbInfo.hwndCombo, 0);

                        if (!cb.IsSimpleCombo()) 
                        {
                            el = cb; 
                        } 
                    }
                } 
            }

            if (el != null)
            { 
                el.DispatchEvents(eventId, idProp, idObject, idChild);
            } 
        } 

        #region Selection Pattern Helpers 

        private int GetOtherSelection(int skipItem)
        {
            for (int i = 0, count = Length; i < count; i++) 
            {
                if (i != skipItem && ListboxItem.IsSelected(_hwnd, i)) 
                { 
                    // Win32 listbox items are 0 based, UIAutomation listbox items are 1 based.
                    return i + 1; 
                }
            }

            return NativeMethods.LB_ERR; 
        }
 
        private int GetSelectionCount() 
        {
            int result = Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_GETSELCOUNT, IntPtr.Zero, IntPtr.Zero); 
            return result != NativeMethods.LB_ERR ? result : 0;
        }

        // Detect if there any selections in 
        // This is used by the WindowsListBoxItem class
        // returns true if any items are selected 
        private bool HasSelection () 
        {
            int i, count; 

            for (i = 0, count = Length; i < count && !ListboxItem.IsSelected (_hwnd, i); i++)
                ;
 
            return (i < count);
        } 
 
        // Detect if listbox has any element except skipItem selected
        // This is used by the WindowsListBoxItem class 
        // returns true if any items
        private bool HasOtherSelections (int skipItem)
        {
            for (int i = 0, count = Length; i < count; i++) 
            {
                if (i != skipItem && ListboxItem.IsSelected (_hwnd, i)) 
                { 
                    return true;
                } 
            }

            return false;
        } 

        // Clears all elements in the multiple-selection  listbox 
        // This is used by the WindowsListBoxItem class 
        // returns true if operation succeeded
        private bool ClearAll () 
        {
            // clear all possible only in the multi-select case
            System.Diagnostics.Debug.Assert (IsMultipleSelection (), "Calling ClearAll on single-selected listbox");
 
            return Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_SETSEL, IntPtr.Zero, new IntPtr(-1)) != NativeMethods.LB_ERR;
        } 
 
        // Check the LBS_MULTIPLESEL or/and LBS_EXTENDEDSEL for multiple selection
        // This is used by WindowListBoxItem class 
        // returns true if multiple selection is supported
        private bool IsMultipleSelection ()
        {
            return (0 != (WindowStyle & (NativeMethods.LBS_MULTIPLESEL | NativeMethods.LBS_EXTENDEDSEL))); 
        }
 
        private bool IsWinFormCheckedListBox() 
        {
            if (WindowsFormsHelper.IsWindowsFormsControl(_hwnd)) 
            {
                return ((WindowStyle & NativeMethods.LBS_OWNERDRAWFIXED) == NativeMethods.LBS_OWNERDRAWFIXED) &&
                    ((WindowStyle & NativeMethods.LBS_WANTKEYBOARDINPUT) == NativeMethods.LBS_WANTKEYBOARDINPUT);
            } 
            return false;
        } 
 

        #endregion 

        #endregion

        //----------------------------------------------------- 
        //
        //  Private Fields 
        // 
        //------------------------------------------------------
 
        #region Private Fields

        private bool _parentedByCombo;
 
        #endregion
 
        // ------------------------------------------------------ 
        //
        //  ListboxItem Private Class 
        //
        //-----------------------------------------------------

        #region ListBoxItem 

        // Summary description for WindowsListboxItem. 
        class ListboxItem : ProxySimple, ISelectionItemProvider, IScrollItemProvider, IToggleProvider 
        {
            // ------------------------------------------------------ 
            //
            // Constructors
            //
            // ----------------------------------------------------- 

            #region Constructors 
 
            // Constructor.
            internal ListboxItem (IntPtr hwnd, ProxyFragment parent, int item) 
            : base (hwnd, parent, item)
            {
                // Set the strings to return properly the properties.
                _cControlType = ControlType.ListItem; 
                _fHasPersistentID = false;
                _fIsKeyboardFocusable = true; 
                _listBox = (WindowsListBox) parent; 
            }
 
            #endregion

            //-----------------------------------------------------
            // 
            //  Patterns Implementation
            // 
            //----------------------------------------------------- 

            #region ProxySimple Interface 

            // Returns a pattern interface if supported.
            internal override object GetPatternProvider (AutomationPattern iid)
            { 
                if (iid == SelectionItemPattern.Pattern)
                { 
                    return this; 
                }
                else if (iid == ScrollItemPattern.Pattern && WindowScroll.IsScrollable(_hwnd)) 
                {
                    return this;
                }
                else if (_listBox.IsWinFormCheckedListBox() && iid == TogglePattern.Pattern) 
                {
                    return this; 
                } 

                return null; 
            }

            // Gets the bounding rectangle for this element
            internal override Rect BoundingRectangle 
            {
                get 
                { 
                    // Don't need to normalize, LB_GETITEMRECT returns absolute coordinates.
                    return BoundingRect().ToRect(false); 
                }
            }

            //Gets the localized name 
            // Returns the text of ListBox item. Check value is not included.
            internal override string LocalizedName 
            { 
                get
                { 
                    // NOTE: List can be a part of the combobox. Many times
                    // sending LB_ message will work, however many apps have ownerdraw combos,
                    // in this case only CB_ type message will work
                    if (((WindowsListBox)_parent).IsParentedByCombo()) 
                    {
                        WindowsComboBox cb = (WindowsComboBox)_parent._parent; 
                        return cb.GetListItemText(_item); 
                    }
 

                    int iTextLen = Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_GETTEXTLEN, new IntPtr(_item), IntPtr.Zero);

                    if (iTextLen != 0) 
                    {
                        if (Misc.GetClassName(_hwnd).Equals("Internet Explorer_TridentLstBox")) 
                        { 
                            // The Trident listbox is a superclassed standard listbox.
                            // Trident listboxes are owner draw that does not have the hasstring style set. 
                            // All the control contains is the owner draw data and not text.  Private
                            // messages were added to retrieve the owner draw data as text.  The new messages
                            // are used just like the normally LB_GETTEXT and CB_GETTEXT messages.
                            return XSendMessage.GetItemText(_hwnd, NativeMethods.WM_USER + NativeMethods.LB_GETTEXT, _item, iTextLen); 
                        }
                        else 
                        { 
                            string text = Misc.GetUnsafeText(_hwnd, NativeMethods.LB_GETTEXT, new IntPtr(_item), iTextLen);
                            // The application engineer has most likely hidden associated information in the 
                            // listbox item's text with a tab, '\t'.  If this is the case remove that hidden
                            // information.
                            int iPos = text.IndexOf('\t');
                            if (iPos > 0) 
                            {
                                text = text.Substring(0, iPos); 
                            } 

                            return text; 
                        }
                    }

                    return ""; 
                }
            } 
 
            #endregion
 
            #region SelectionItem Pattern

            // Selects this element
            void ISelectionItemProvider.Select () 
            {
                // Check that control can be interacted with. 
                // This state could change anytime 
                if (!SafeNativeMethods.IsWindowEnabled(_hwnd))
                { 
                    throw new ElementNotEnabledException();
                }

                // get needed info about lbItem 
                bool multipleSelected = _listBox.IsMultipleSelection ();
                bool itemSelected = IsSelected (_hwnd, _item); 
                bool parentedByCombo = _listBox.IsParentedByCombo(); 

                if (multipleSelected) 
                {
                    if (_listBox.HasOtherSelections(_item))
                    {   // some other elements are selected
                        // unselect all. It is just simplier to unselect all 
                        // and re-select us, than unselect each element one by one
                        _listBox.ClearAll (); 
                    } 
                    else if (itemSelected)
                    { 
                        // multi-selected lbItem with
                        // only 1 selected element - us
                        return;
                    } 
                }
                else if (itemSelected) 
                { 
                    // if it is a combo, then always perform the selection otherwise the listbox won't disappear
                    if (!parentedByCombo) 
                    {
                        // single-selection and we selected already
                        return;
                    } 
                }
 
                if (parentedByCombo) 
                {
                    // if this is a combo and the listbox is not displayed the selection will not stick, so 
                    // display the listbox before doing the select.
                    if (((IExpandCollapseProvider)_listBox._parent).ExpandCollapseState == ExpandCollapseState.Collapsed)
                    {
                        ((IExpandCollapseProvider)_listBox._parent).Expand(); 
                    }
                } 
 
                // do the selection
                if (!Select(multipleSelected)) 
                {
                    throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed));
                }
            } 

            // Adds this element to the selection 
            void ISelectionItemProvider.AddToSelection () 
            {
                // Check that control can be interacted with. 
                // This state could change anytime
                if (!SafeNativeMethods.IsWindowEnabled(_hwnd))
                {
                    throw new ElementNotEnabledException(); 
                }
 
                // check if item already selected 
                if (ListboxItem.IsSelected (_hwnd, _item) && !_listBox.IsParentedByCombo())
                { 
                    // if it is a combo, then always perform the selection otherwise the listbox won't disappear
                    return;
                }
 
                bool multipleSelection = _listBox.IsMultipleSelection();
 
                // object does not support multi-selection 
                if (!multipleSelection)
                { 
                    IRawElementProviderSimple container = ((ISelectionItemProvider)this).SelectionContainer;
                    bool selectionRequired = container != null ? ((ISelectionProvider)container).IsSelectionRequired : true;

                    // For single selection containers that IsSelectionRequired == false and nothing is selected 
                    // an AddToSelection is valid.
                    if (selectionRequired || _listBox.HasSelection()) 
                    { 
                        throw new InvalidOperationException(SR.Get(SRID.DoesNotSupportMultipleSelection));
                    } 
                }

                if (_listBox.IsParentedByCombo())
                { 
                    // if this is a combo and the listbox is not displayed the selection will not stick, so
                    // display the listbox before doing the select. 
                    if (((IExpandCollapseProvider)_listBox._parent).ExpandCollapseState == ExpandCollapseState.Collapsed) 
                    {
                        ((IExpandCollapseProvider)_listBox._parent).Expand(); 
                    }
                }

                // At this point we know: Item either supports multiple selection or nothing 
                // is selected in the list
                // Try to select an item 
                if (!Select(multipleSelection)) 
                {
                    throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); 
                }
            }

            // Removes this element from the selection 
            void ISelectionItemProvider.RemoveFromSelection ()
            { 
                // Check that control can be interacted with. 
                // This state could change anytime
                if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) 
                {
                    throw new ElementNotEnabledException();
                }
 
                if (!IsSelected(_hwnd, _item))
                { 
                    // simple case, item is not selected 
                    return;
                } 

                // object does not support multi-selection
                if (!_listBox.IsMultipleSelection())
                { 
                    // single-selected lb - user cannot remove the selection using keyboard and mouse
                    // At this point we know that item is selected, lb is single-selected hence 
                    // RemoveFromSelection is not possible 
                    throw new InvalidOperationException(SR.Get(SRID.SelectionRequired));
                } 

                if (!UnSelect(_hwnd, _item))
                {
                    throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); 
                }
            } 
 
            // True if this element is part of the the selection
            bool ISelectionItemProvider.IsSelected 
            {
                get
                {
                    return ListboxItem.IsSelected (_hwnd, _item); 
                }
            } 
 
            // Returns the container for this element
            IRawElementProviderSimple ISelectionItemProvider.SelectionContainer 
            {
                get
                {
                    System.Diagnostics.Debug.Assert (_parent is WindowsListBox, "Invalid Parent for a Listbox Item"); 
                    return _parent;
                } 
            } 

            #endregion SelectionItem Pattern 

            #region ScrollItem Pattern

            void IScrollItemProvider.ScrollIntoView () 
            {
                if (_listBox._parentedByCombo && !SafeNativeMethods.IsWindowVisible(_hwnd)) 
                { 
                    throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed));
                } 

                if (!WindowScroll.IsScrollable(_hwnd))
                {
                    throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); 
                }
 
                // It is assumed that in a listbox an item will always be smaller than the scrolling area 
                Misc.ProxySendMessage(_hwnd, NativeMethods.LB_SETTOPINDEX, new IntPtr(_item), IntPtr.Zero);
            } 

            #endregion ScrollItem Pattern

            #region IToggleProvider 

            void IToggleProvider.Toggle() 
            { 
                // Check that button can be clicked
                // This state could change anytime 
                if (!SafeNativeMethods.IsWindowEnabled(_hwnd))
                {
                    throw new ElementNotEnabledException();
                } 
                if (!SafeNativeMethods.IsWindowVisible(_hwnd))
                { 
                    throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); 
                }
 
                Toggle();
            }

            ToggleState IToggleProvider.ToggleState 
            {
                get 
                { 
                    return ToggleState;
                } 
            }

            #endregion IToggleProvider
 
            //------------------------------------------------------
            // 
            //  Internal Methods 
            //
            //----------------------------------------------------- 

            #region Internal Methods

            #region Selection Pattern Helpers 

            // Detects if an item is selected 
            internal static bool IsSelected (IntPtr hwnd, int item) 
            {
                return Misc.ProxySendMessageInt(hwnd, NativeMethods.LB_GETSEL, new IntPtr(item), IntPtr.Zero) > 0; 
            }

            internal static bool Select (IntPtr hwnd, int item, bool fMultipleSelection)
            { 
                int SendMessageResult = 0;
 
                if (fMultipleSelection) 
                {
                    SendMessageResult = Misc.ProxySendMessageInt(hwnd, NativeMethods.LB_SETSEL, new IntPtr(1), new IntPtr(item)); 
                }
                else
                {
                    SendMessageResult = Misc.ProxySendMessageInt(hwnd, NativeMethods.LB_SETCURSEL, new IntPtr(item), IntPtr.Zero); 
                }
 
                return NativeMethods.LB_ERR != SendMessageResult; 
            }
 
            #endregion

            #region Focus Helper
 
            // Returns an item corresponding to the focused element (if there is one), or null otherwise.
            internal override bool SetFocus () 
            { 
                if (_listBox.IsMultipleSelection ())
                { 
                    return Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_SETCARETINDEX, new IntPtr(_item), new IntPtr(0)) != NativeMethods.LB_ERR;
                }
                else
                { 
                    return Select (_hwnd, _item, false);
                } 
            } 

            #endregion 

            #endregion

            //------------------------------------------------------ 
            //
            //  Protected Methods 
            // 
            //------------------------------------------------------
 
            #region Protected Methods

            // This routine is only called on elements belonging to an hwnd that has the focus.
            protected override bool IsFocused () 
            {
                if (_listBox.IsMultipleSelection ()) 
                { 
                    return Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_GETCARETINDEX, IntPtr.Zero, IntPtr.Zero) == _item;
                } 
                else
                {
                    return Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_GETSEL, new IntPtr(_item), IntPtr.Zero) >= 0;
                } 
            }
 
            #endregion 

            //----------------------------------------------------- 
            //
            //  Private Methods
            //
            //------------------------------------------------------ 

            #region Private Methods 
 
            private NativeMethods.Win32Rect BoundingRect()
            { 
                NativeMethods.Win32Rect itemRect = new NativeMethods.Win32Rect();
                Misc.ProxySendMessage(_hwnd, NativeMethods.LB_GETITEMRECT, new IntPtr(_item), ref itemRect);
                return Misc.MapWindowPoints(_hwnd, IntPtr.Zero, ref itemRect, 2) ? itemRect : NativeMethods.Win32Rect.Empty;
            } 

            // Process all the Toggle Properties 
            private ToggleState ToggleState 
            {
                get 
                {
                    ToggleState icsState = ToggleState.Indeterminate;

                    // Special handling for forms 
                    if (_windowsForms == WindowsFormsHelper.FormControlState.Undeterminate)
                    { 
                        _windowsForms = WindowsFormsHelper.GetControlState(_hwnd); 
                    }
 
                    if (_windowsForms == WindowsFormsHelper.FormControlState.True)
                    {
                        int childrenReturned;
                        object[] accChildren = Accessible.GetAccessibleChildren(this.AccessibleObject, out childrenReturned); 
                        IAccessible accChild = (IAccessible)accChildren[_item];
 
                        if (((int)accChild.get_accState(NativeMethods.CHILD_SELF) & NativeMethods.STATE_SYSTEM_CHECKED) == NativeMethods.STATE_SYSTEM_CHECKED) 
                        {
                            icsState = ToggleState.On; 
                        }
                        else if (((int)accChild.get_accState(NativeMethods.CHILD_SELF) & NativeMethods.STATE_SYSTEM_MIXED) == NativeMethods.STATE_SYSTEM_MIXED)
                        {
                            icsState = ToggleState.Indeterminate; 
                        }
                        else 
                        { 
                            icsState = ToggleState.Off;
                        } 
                    }
                    return icsState;
                }
            } 

            #region Selection Pattern Helpers 
 
            private void Toggle()
            { 
                // Convoluted way fake a mouse action
                NativeMethods.Win32Point pt = new NativeMethods.Win32Point();
                if (GetClickablePoint(out pt, false))
                { 
                    // Mouse method is used here because following methods fail:
                    //     -BM_CLICK message doesn't work with all buttons (e.g. Start) 
                    //     -WM_MOUSEACTIVATE + WM_LBUTTONDOWN + WM_LBUTTONUP messages don't work with all buttons 
                    //     -WM_KEYDOWN + WM_KEYUP messages for space bar
                    //     -SendKeyboardInput for space bar 
                    // See prior versions of this file for alternative code.
                    //
                    Misc.MouseClick(pt.x, pt.y);
                } 
            }
 
            private bool Select(bool fMultipleSelection) 
            {
                int sendMessageResult = 0; 
                bool success = true;

                if (!((WindowsListBox)_parent).IsParentedByCombo())
                { 
                    if (fMultipleSelection)
                    { 
                        sendMessageResult = 
                            Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_SETSEL, new IntPtr(1), new IntPtr(_item));
                    } 
                    else
                    {
                        sendMessageResult =
                            Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_SETCURSEL, new IntPtr(_item), IntPtr.Zero); 
                    }
 
                    success = (NativeMethods.LB_ERR != sendMessageResult); 
                    if (success)
                    { 
                        // Whether multiple selection or not, send LBN_SELCHANGE.
                        // This is normally sent when a user presses an arrow key, but
                        // NOT when LB_SETCURSEL is sent programmatically.  We need to
                        // mimic the action of a user, including all side effects. 
                        int listBoxStyle = Misc.GetWindowStyle(_hwnd);
                        if (Misc.IsBitSet(listBoxStyle, NativeMethods.LBS_NOTIFY)) 
                        { 
                            // Get the child ID of the listbox in its parent hwnd.
                            int idListBox = Misc.GetWindowId(_hwnd); 
                            IntPtr wParam =
                                new IntPtr(NativeMethods.Util.MAKELONG(
                                                idListBox, NativeMethods.LBN_SELCHANGE));
                            IntPtr hwndListBoxParent = Misc.GetParent(_hwnd); 
                            // The return value indicates whether the WM_COMMAND was processed,
                            // which is irrelevant, so ignore the return value here. 
                            Misc.ProxySendMessageInt( 
                                hwndListBoxParent, NativeMethods.WM_COMMAND, wParam, _hwnd);
                        } 
                    }
                }
                else
                { 
                    ProxyFragment combo = (WindowsComboBox)_parent._parent;
                    sendMessageResult = 
                        Misc.ProxySendMessageInt(_hwnd, NativeMethods.LB_SETCURSEL, new IntPtr(_item), IntPtr.Zero); 
                    success = (NativeMethods.LB_ERR != sendMessageResult);
                    if (success) 
                    {
                        int id = Misc.GetWindowId(_hwnd);
                        IntPtr wParam = new IntPtr(NativeMethods.Util.MAKELONG(id, NativeMethods.LBN_SELCHANGE));
                        // The return value indicates whether the WM_COMMAND was processed, 
                        // which is irrelevant, so ignore the return value here.
                        Misc.ProxySendMessageInt(combo._hwnd, NativeMethods.WM_COMMAND, wParam, _hwnd); 
                    } 
                }
 
                return success;
            }

            // This method should be called only on multi-selected listbox 
            private bool UnSelect (IntPtr hwnd, int item)
            { 
                // alexsn @ 

 

                System.Diagnostics.Debug.Assert (_listBox.IsMultipleSelection (), "Calling UnSelect on single-selected listbox");

                return Misc.ProxySendMessageInt(hwnd, NativeMethods.LB_SETSEL, IntPtr.Zero, new IntPtr(item)) != NativeMethods.LB_ERR; 
            }
 
            #endregion 

            #endregion 

            //-----------------------------------------------------
            //
            //  Private Fields 
            //
            //----------------------------------------------------- 
 
            #region Private Fields
 
            private WindowsListBox _listBox;

            #endregion
 
        }
 
        #endregion 

    } 
}

// 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