Exemple #1
0
        private unsafe bool WmNotify(ref Message m) {
            NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam;

            // column header custom draw message handling
            if (nmhdr->code == NativeMethods.NM_CUSTOMDRAW && OwnerDraw)
            {
                try
                {
                    NativeMethods.NMCUSTOMDRAW* nmcd = (NativeMethods.NMCUSTOMDRAW*)m.LParam;
                    // Find out which stage we're drawing
                    switch (nmcd->dwDrawStage)
                    {
                        case NativeMethods.CDDS_PREPAINT:
                            {
                                m.Result = (IntPtr)(NativeMethods.CDRF_NOTIFYITEMDRAW);
                                return true; // we are done - don't do default handling

                            }
                        case NativeMethods.CDDS_ITEMPREPAINT:
                            {
                                Graphics g = Graphics.FromHdcInternal(nmcd->hdc);
                                Rectangle r = Rectangle.FromLTRB(nmcd->rc.left, nmcd->rc.top, nmcd->rc.right, nmcd->rc.bottom);
                                DrawListViewColumnHeaderEventArgs e = null;

                                try
                                {
                                    Color foreColor = ColorTranslator.FromWin32(SafeNativeMethods.GetTextColor(new HandleRef(this, nmcd->hdc)));
                                    Color backColor = ColorTranslator.FromWin32(SafeNativeMethods.GetBkColor(new HandleRef(this, nmcd->hdc)));
                                    Font font = GetListHeaderFont();
                                    e = new DrawListViewColumnHeaderEventArgs(g, r, (int)(nmcd->dwItemSpec),
                                                                        columnHeaders[(int)nmcd->dwItemSpec],
                                                                        (ListViewItemStates)(nmcd->uItemState),
                                                                        foreColor, backColor, font);

                                    OnDrawColumnHeader(e);
                                }
                                finally
                                {
                                    g.Dispose();
                                }

                                if (e.DrawDefault)
                                {
                                    m.Result = (IntPtr)(NativeMethods.CDRF_DODEFAULT);
                                    return false;
                                }
                                else
                                {
                                
                                    m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT);
                                    return true; // we are done - don't do default handling
                                }
                            }

                        default:
                            return false; //default handling
                    }
                }
                catch (Exception e)
                {
                    Debug.Fail("Exception occurred attempting to setup header custom draw. Disabling custom draw for the column header", e.ToString());
                    m.Result = (IntPtr)NativeMethods.CDRF_DODEFAULT;
                }
            }


            if (nmhdr->code == NativeMethods.NM_RELEASEDCAPTURE && listViewState[LISTVIEWSTATE_columnClicked]) {
                listViewState[LISTVIEWSTATE_columnClicked] = false;
                OnColumnClick(new ColumnClickEventArgs(columnIndex));
            }

            if (nmhdr->code == NativeMethods.HDN_BEGINTRACKA ||
                nmhdr->code == NativeMethods.HDN_BEGINTRACKW) {
               this.listViewState[LISTVIEWSTATE_headerControlTracking] = true;

               // Reset our tracking information for the new BEGINTRACK cycle.
               this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging] = false;
               this.newWidthForColumnWidthChangingCancelled = -1;
               this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging] = false;

               NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER));
               if (this.columnHeaders != null && this.columnHeaders.Length > nmheader.iItem) {
                   this.columnHeaderClicked = this.columnHeaders[nmheader.iItem];
                   this.columnHeaderClickedWidth = this.columnHeaderClicked.Width;
               } else {
                   this.columnHeaderClickedWidth = -1;
                   this.columnHeaderClicked = null;
               }
            }

            if (nmhdr->code == NativeMethods.HDN_ITEMCHANGINGA || 
                nmhdr->code == NativeMethods.HDN_ITEMCHANGINGW) {
                NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER));

                if (columnHeaders != null && nmheader.iItem < columnHeaders.Length &&
                    (this.listViewState[LISTVIEWSTATE_headerControlTracking] || this.listViewState[LISTVIEWSTATE_headerDividerDblClick])) {
                    // SECREVIEW:
                    // UnsafeNativeMethods.PtrToStructure asserts ReflectionPermission.
                    // We are fine asserting this permission because the HDITEM2 type is our type and we have control over it
                    // and over what it does in its constructor.
                    NativeMethods.HDITEM2 hdItem = (NativeMethods.HDITEM2) UnsafeNativeMethods.PtrToStructure((IntPtr) nmheader.pItem, typeof(NativeMethods.HDITEM2));
                    int newColumnWidth = ((hdItem.mask & NativeMethods.HDI_WIDTH) != 0) ? hdItem.cxy : -1;
                    ColumnWidthChangingEventArgs colWidthChanging = new ColumnWidthChangingEventArgs(nmheader.iItem, newColumnWidth);
                    OnColumnWidthChanging(colWidthChanging);
                    m.Result = (IntPtr) (colWidthChanging.Cancel ? 1 : 0);
                    if (colWidthChanging.Cancel) {
                        hdItem.cxy = colWidthChanging.NewWidth;

                        // We are called inside HDN_DIVIDERDBLCLICK.
                        // Turn off the compensation that our processing of HDN_DIVIDERDBLCLICK would otherwise add.
                        if (this.listViewState[LISTVIEWSTATE_headerDividerDblClick]) {
                            this.listViewState[LISTVIEWSTATE_columnResizeCancelled] = true;
                        }

                        this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging] = true;
                        this.newWidthForColumnWidthChangingCancelled = colWidthChanging.NewWidth;

                        // skip default processing
                        return true;
                    } else {
                        return false;
                    }
                }
            }

            if ((nmhdr->code == NativeMethods.HDN_ITEMCHANGEDA ||
                nmhdr->code == NativeMethods.HDN_ITEMCHANGEDW) &&
                !this.listViewState[LISTVIEWSTATE_headerControlTracking]) {
                NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER)m.GetLParam(typeof(NativeMethods.NMHEADER));
                if (columnHeaders != null && nmheader.iItem < columnHeaders.Length) {
                    int w = columnHeaders[nmheader.iItem].Width;

                    if (this.columnHeaderClicked == null ||
                        (this.columnHeaderClicked == this.columnHeaders[nmheader.iItem] &&
                         this.columnHeaderClickedWidth != -1 &&
                         this.columnHeaderClickedWidth != w)) {

                        //
                        // If the user double clicked on the column header and we still need to compensate for the column resize
                        // then don't fire ColumnWidthChanged because at this point the column header does not have the final width.
                        //
                        if (this.listViewState[LISTVIEWSTATE_headerDividerDblClick]) {
                            if (this.CompensateColumnHeaderResize(m, this.listViewState[LISTVIEWSTATE_columnResizeCancelled]) == 0) {
                                OnColumnWidthChanged(new ColumnWidthChangedEventArgs(nmheader.iItem));
                            }
                        } else {
                            OnColumnWidthChanged(new ColumnWidthChangedEventArgs(nmheader.iItem));
                        }

                    }
                }

                this.columnHeaderClicked = null;
                this.columnHeaderClickedWidth = -1;

                ISite site = Site;

                // [....], this seems like a really wierd place to annouce this change...
                if (site != null) {
                    IComponentChangeService cs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
                    if (cs != null) {
                        try {
                            cs.OnComponentChanging(this, null);
                        }
                        catch (CheckoutException coEx) {
                            if (coEx == CheckoutException.Canceled) {
                                return false;
                            }
                            throw coEx;
                        }
                    }
                }
            }

            if (nmhdr->code == NativeMethods.HDN_ENDTRACKA || 
                nmhdr->code == NativeMethods.HDN_ENDTRACKW) {
                Debug.Assert(this.listViewState[LISTVIEWSTATE_headerControlTracking], "HDN_ENDTRACK and HDN_BEGINTRACK are out of [....]...");
                this.listViewState[LISTVIEWSTATE_headerControlTracking] = false;
                if (this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging]) {
                    m.Result = (IntPtr) 1;
                    if (this.newWidthForColumnWidthChangingCancelled != -1) {
                        NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER)m.GetLParam(typeof(NativeMethods.NMHEADER));
                        if (this.columnHeaders != null && this.columnHeaders.Length > nmheader.iItem) {
                            this.columnHeaders[nmheader.iItem].Width = this.newWidthForColumnWidthChangingCancelled;
                        }
                    }

                    this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging] = false;
                    this.newWidthForColumnWidthChangingCancelled = -1;

                    // skip default processing
                    return true;
                } else {
                    return false;
                }
            }

            if (nmhdr->code == NativeMethods.HDN_ENDDRAG) {
                NativeMethods.NMHEADER header = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER));
                if (header.pItem != IntPtr.Zero) {
                    // SECREVIEW:
                    // UnsafeNativeMethods.PtrToStructure asserts ReflectionPermission.
                    // We are fine asserting this permission because the HDITEM type is our type and we have control over it
                    // and over what it does in its constructor.
                    NativeMethods.HDITEM2 hdItem = (NativeMethods.HDITEM2) UnsafeNativeMethods.PtrToStructure((IntPtr) header.pItem, typeof(NativeMethods.HDITEM2));
                    if ((hdItem.mask & NativeMethods.HDI_ORDER) == NativeMethods.HDI_ORDER) {

                        int from = this.Columns[header.iItem].DisplayIndex;
                        int to = hdItem.iOrder;
                        // check this
                        if (from == to) {
                            return false;
                        }

                        // sometimes ComCtl gives us bogus values for HDIItem.iOrder.
                        // vsw 541880.
                        if (to < 0) {
                            return false;
                        }
                        ColumnReorderedEventArgs chrevent = new ColumnReorderedEventArgs(from,
                                                                                         to,
                                                                                         this.Columns[header.iItem]);
                        OnColumnReordered(chrevent);
                        if (chrevent.Cancel) {
                            m.Result = new IntPtr(1);
                            return true;
                        } else {
                            // set the display indices. This is not an expensive operation because
                            // we only set an integer in the column header class
                            int lowDI = Math.Min(from, to);
                            int hiDI = Math.Max(from, to);
                            bool hdrMovedForward = to > from;
                            ColumnHeader movedHdr = null;
                            int[] indices = new int[this.Columns.Count];
                            for (int i = 0; i < this.Columns.Count; i ++) {

                                ColumnHeader hdr = this.Columns[i];
                                if (hdr.DisplayIndex == from) {
                                    movedHdr = hdr;
                                } else if (hdr.DisplayIndex >= lowDI && hdr.DisplayIndex <= hiDI) {
                                    hdr.DisplayIndexInternal -= hdrMovedForward ? 1 : -1;
                                }
                                indices[i] = hdr.DisplayIndexInternal;
                            }

                            movedHdr.DisplayIndexInternal = to;
                            indices[movedHdr.Index] = movedHdr.DisplayIndexInternal;
                            SetDisplayIndices( indices );
#if DEBUG
                            CheckDisplayIndices();
#endif
                        }
                    }
                }
            }

            if (nmhdr->code == NativeMethods.HDN_DIVIDERDBLCLICKA ||
                nmhdr->code == NativeMethods.HDN_DIVIDERDBLCLICKW) {
                // We need to keep track that the user double clicked the column header divider
                // so we know that the column header width is changing.
                this.listViewState[LISTVIEWSTATE_headerDividerDblClick] = true;

                // Reset ColumnResizeCancelled.
                // It will be set if the user cancels the ColumnWidthChanging event.
                this.listViewState[LISTVIEWSTATE_columnResizeCancelled] = false;

                bool columnResizeCancelled = false;

                // ComCtl32 does not add enough padding when resizing the first column via mouse double click.
                // See vsw 336709 for a complete explanation including listing of the comctl32 code.
                // Our wrapper will add 2 pixels. (1 pixel is not enough, 3 pixels is too much)

                // Send the message to ComCtl32 so that it resizes the column.
                try
                {
                    DefWndProc(ref m);
                }
                finally
                {
                    this.listViewState[LISTVIEWSTATE_headerDividerDblClick] = false;
                    columnResizeCancelled = this.listViewState[LISTVIEWSTATE_columnResizeCancelled];
                    this.listViewState[LISTVIEWSTATE_columnResizeCancelled] = false;
                }


                this.columnHeaderClicked = null;
                this.columnHeaderClickedWidth = -1;

                if (columnResizeCancelled) {
                    // If the column resize was cancelled then apply the NewWidth supplied by the user.
                    if (this.newWidthForColumnWidthChangingCancelled != -1) {
                        NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER));
                        if (this.columnHeaders != null && this.columnHeaders.Length > nmheader.iItem) {
                            this.columnHeaders[nmheader.iItem].Width = this.newWidthForColumnWidthChangingCancelled;
                        }
                    }

                    // Tell ComCtl that the HDN_DIVIDERDBLCLICK was cancelled.
                    m.Result = (IntPtr) 1;
                } else {
                    // Compensate for the column resize.
                    int compensateForColumnResize = this.CompensateColumnHeaderResize(m, columnResizeCancelled);
                    if (compensateForColumnResize != 0) {
                        #if DEBUG
                        NativeMethods.NMHEADER header = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER));
                        Debug.Assert(header.iItem == 0, "we only need to compensate for the first column resize");
                        Debug.Assert(this.columnHeaders.Length > 0, "there should be a column that we need to compensate for");
                        #endif

                        ColumnHeader col = this.columnHeaders[0];
                        col.Width += compensateForColumnResize;
                    }
                }

                // We called DefWndProc so we don't need default handling.
                return true;
            }

            return false; // still need default handling
        }