DataGridView rightmost column user increase size not working

后端 未结 2 1095
抹茶落季
抹茶落季 2021-01-21 09:11

I have a strange issue with a DataGridView on a WinForm. I can resize all columns via mouse, but for the rightmost I can only shrink and not increase the size.

相关标签:
2条回答
  • 2021-01-21 09:56

    I had this issue as well and found it truly annoying that the .NET dgv doesn't handle this. I tried the other suggested solution, but it didn't always fire even when I was clicking on the resize area of the last col, and it also fires even when you're not clicking on the header.

    I came up with this quick-and-dirty method that resizes only if you click on the last column's header. It uses the CellMouseMove, so you can check if they're clicking on the header (RowIndex = -1), and only bothers to fire if they're clicking the left mouse button on the last col (use the DisplayIndex when checking if it's the right-most col, since they may have reordered their columns).

    It assumes a five px "buffer" to the left and right of the right edge of the column header, which is when the "resize" cursor appears when you hover over the right edge of the col header. I found this to be the case on my machine, but maybe the buffer is slightly different on other display setups, so you may need to adjust that. I also increase the column width by 5 px at a time, which is smoother than doing it one at a time. You can also adjust that to your liking.

    Private Sub dgvView_CellMouseMove(sender As Object, e As DataGridViewCellMouseEventArgs) Handles dgvView.CellMouseMove
        If e.Button = MouseButtons.Left AndAlso e.RowIndex = -1 AndAlso dgvView.Columns(e.ColumnIndex).DisplayIndex = dgvView.ColumnCount - 1 Then
            Dim iBuffer As Integer = 5, iIncrease As Integer = 5
            Dim iCSWidth As Integer = dgvView.ClientSize.Width
            Dim iMouseX As Integer = dgvView.GetColumnDisplayRectangle(e.ColumnIndex, True).X + e.X
            If dgvView.Controls.OfType(Of VScrollBar)().First().Visible Then iCSWidth -= SystemInformation.VerticalScrollBarWidth
            If iMouseX >= iCSWidth - iBuffer AndAlso iMouseX <= iCSWidth + iBuffer Then
                dgvView.HorizontalScrollingOffset += iIncrease
                dgvView.Columns(e.ColumnIndex).Width += iIncrease
            End If
        End If
    End Sub
    

    The only downside to this method is that it still fires if you're moving the last column to the right (not resizing it). If you click the middle of the col and start moving it right, once you reach the edge, it will think you're resizing and will start resizing it. Frankly, I didn't care about this. If they're moving the right-most column to the right and they've reached the edge of the screen, it almost makes sense to start resizing it, but I doubt I'll ever even run into a user that tries this.

    I could have avoided this behavior, but it would have required adding more code to the CellMouseDown and CellMouseUp to check that they were within the "resize" range when they first clicked the mouse (and setting a bool var to store that), rechecking that they're still in the resize range every time they move the mouse, then turning off the bool when they mouseup. The problem is, there are ways around firing the CellMouseUp event (for example, hitting esc or another window getting the focus while you're resizing bypasses the cellmouseup event). Then your var is left True and simply moving your mouse over the right-most column header will cause it to start resizing.

    I should note that moving the mouse to the right without moving your mouse up or down at all does NOT fire the CellMouseMove or MouseMove events for the dgv. This is because the mouse isn't actually "moving" (since it has reached the edge of the dgv). So if your user is very slowly moving their mouse right (without going up or down at all) nothing will happen. Most people move up or down a little while moving the mouse left/right, so this isn't much of an issue. I didn't even bother looking into how to address this (if it's even possible, since my dgv stretches to the far right of the form, so event Form_MouseMove probably wouldn't fire).

    In other words, I'm sure there is an even more complicated method that would account for every little scenario, but I decided not to waste a bunch of time and place a bunch of extra code in various events just to make this perfect. The method above is simple, short, covers 99% of use cases, and will probably never throw a user for a loop.

    0 讨论(0)
  • 2021-01-21 10:06

    This is by design. When you Click/Hold your mouse button, the mouse is captured and cannot leave the client area, with the effect of blocking the resize. You have to give it a push.

    I tried not to P/Invoke to release the capture.
    See if this works for you (of course if any AutoSize mode is set, nothing will happen).

    private bool IsLastColumn = false;
    private bool IsMouseDown = false;
    private int MouseLocationX = 0;
    
    private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
    {
       var dgv = sender as DataGridView;
       int lastColumnIndex = dgv.Columns.Count - 1;
    
       //Check if the mouse pointer is contained in last column boundaries
       //In the middle of it because clicking the divider of the row before
       //the last may be seen as inside the last too.
       Point location = new Point(e.X - (dgv.Columns[lastColumnIndex].Width / 2), e.Y);
       if (dgv.GetColumnDisplayRectangle(lastColumnIndex, true).Contains(location))
       {
          //Store a positive checks and the current mouse position
          this.IsLastColumn = true;
          this.IsMouseDown = true;
          this.MouseLocationX = e.Location.X;
       }
    }
    
    private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
    {
       var dgv = sender as DataGridView;
    
       //If it's the last column and the left mouse button is pressed...
       if ((this.IsLastColumn) && (this.IsMouseDown) && (e.Button == MouseButtons.Left))
       {
          // Adding the width of the vertical scrollbar, if any.
          int cursorXPosition = e.X;
          if (dgv.Controls.OfType<VScrollBar>().Where(s => s.Visible).Count() > 0)
          {
              cursorXPosition += SystemInformation.VerticalScrollBarWidth;
          }
    
          //... calculate the offset of the movement.
          //You'll have to play with it a bit, though
          int colWidth = dgv.Columns[dgv.Columns.Count - 1].Width;
          int colWidthOffset = columnWidth + (e.X - this.MouseLocationX) > 0 ? (e.X - this.MouseLocationX) : 1;
          //If mouse pointer reaches the limit of the clientarea...
          if ((colWidthOffset > -1) && (cursorXPosition >= dgv.ClientSize.Width - 1))
          {
             //...resize the column and move the scrollbar offset
             dgv.HorizontalScrollingOffset = dgv.ClientSize.Width + colWidth + colWidthOffset;
             dgv.Columns[dgv.Columns.Count - 1].Width = colWidth + colWidthOffset;
          }
       }
    }
    
    private void dataGridView1_MouseUp(object sender, MouseEventArgs e)
    {
        this.IsMouseDown = false;
        this.IsLastColumn = false;
    }
    
    0 讨论(0)
提交回复
热议问题