【问题标题】:Attempt to draw a rectangle goes left when it should go right and up when it should go down尝试绘制一个矩形在它应该向右时向左,在它应该向下时向上
【发布时间】:2012-05-03 06:19:08
【问题描述】:

我对@9​​87654326@ 方法的使用导致绘制了我的拖动操作的偏移和反转阴影。方法的名称 (DrawReversibleFrame()) 似乎表明这可能是它应该工作的方式(绘制反向图像,而不是忠实地模仿你的动作)。但是,我想要相反的(而且似乎没有简单的DrawFrame() 方法)。

我尝试了两个在我发布初始问题后指出的示例:How do I draw a rectangle based on the movement of the mouse?

这两个尝试可以在这里找到:

filled rubber band in Winforms Application

..这里:

http://www.switchonthecode.com/tutorials/winforms-painting-on-top-of-child-controls

...但没有一个适合我。

首先,两个示例的事件中对 base.* 的调用在我运行它们时会导致 StackOverflows(不是双关语)(通常我什至不需要为 StackOverflows 做任何事情来炸毁我的应用程序) .

我能够从DrawReversibleFrame() 示例(上面的第二个链接)中获得几乎可以工作的代码。但是,当我用鼠标向右和向下(从左上角到右下角)拖动时,绘制的矩形从右下角开始向上到左上角(但我正在以相反的方式拖动 - 从左上到右下)——移动的大小/数量似乎完全正确。

此外,绘制操作的开始点也不完全在正确的位置——它开始于我的初始点的左侧和上方一点。

注意:我使用PointToScreen() 是因为拖动操作发生在TableLayoutPanel 上,该TableLayoutPanel 已放置在表单顶部。如果我不使用它,我根本看不到任何绘图。

我的代码是:

public partial class Form1 : Form
{
    private bool _IsSelecting = false;
    private Point _StartPoint = Point.Empty;
    private Rectangle _FrameRect = Rectangle.Empty; //1

    public Form1()
    {
        InitializeComponent();
        // DoubleBuffered = true; //makes no apparent difference either way
    }

    private void tableLayoutPanel1_MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left)
            return;

        _IsSelecting = true;
        _StartPoint = PointToScreen(e.Location);
        _FrameRect = new Rectangle(_StartPoint, Size.Empty);
    }

    private void tableLayoutPanel1_MouseMove(object sender, MouseEventArgs e)
    {
        if (!_IsSelecting)
            return;

        ControlPaint.DrawReversibleFrame(_FrameRect, Color.Black, FrameStyle.Dashed);

        Point pt = PointToScreen(e.Location);
        _FrameRect.Width = _StartPoint.X - pt.X;
        _FrameRect.Height = _StartPoint.Y - pt.Y;

        ControlPaint.DrawReversibleFrame(_FrameRect, Color.Black, FrameStyle.Dashed);
    }

    private void tableLayoutPanel1_MouseUp(object sender, MouseEventArgs e)
    {
        if (!_IsSelecting)
            return;

        ControlPaint.DrawReversibleFrame(_FrameRect,
            Color.Black, FrameStyle.Dashed);

        _IsSelecting = false;
        _FrameRect = Rectangle.Empty;
        _StartPoint = Point.Empty;
    }
}

更新

为了回应约翰的评论,我在这里颠倒了数学:

    _FrameRect.Width = pt.X - _StartPoint.X;
    _FrameRect.Height = pt.Y - _StartPoint.Y;

...但这只会取笑我,因为它使它几乎可以工作,但是将对 PointToScreen() 的调用更改为 PointToClient() 会使情况变得更糟(进一步向左和顶部,有时矩形太大)。

这是现在的代码(矩形是完美的大小,但从鼠标的左侧和上方开始):

private void tableLayoutPanel1_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button != MouseButtons.Left)
        return;

    _IsSelecting = true;
    _StartPoint = PointToScreen(e.Location);
    //_StartPoint = PointToClient(e.Location); //<- this makes it way worse - the rectangle is way too big, and starts even further up and to the left
    _FrameRect = new Rectangle(_StartPoint, Size.Empty);
}

private void tableLayoutPanel1_MouseMove(object sender, MouseEventArgs e)
{
    if (!_IsSelecting)
        return;

    ControlPaint.DrawReversibleFrame(_FrameRect, Color.Black, FrameStyle.Dashed);
    Point pt = PointToScreen(e.Location);
    //Point pt = PointToClient(e.Location); //worse
    _FrameRect.Width = pt.X - _StartPoint.X;
    _FrameRect.Height = pt.Y - _StartPoint.Y;
    ControlPaint.DrawReversibleFrame(_FrameRect, Color.Black, FrameStyle.Dashed);
}

private void tableLayoutPanel1_MouseUp(object sender, MouseEventArgs e)
{
    if (!_IsSelecting)
        return;

    ControlPaint.DrawReversibleFrame(_FrameRect,
        Color.Black, FrameStyle.Dashed);
    _IsSelecting = false;
    _FrameRect = Rectangle.Empty;
    _StartPoint = Point.Empty;
}

另一个更新

注意:我在动态创建的 TableLayoutPanel 上执行 mousedown,它位于表单上的面板上。

当我添加下面的代码时,值是 25,然后是 146。所以它似乎应该工作 - 它补偿了 TableLayoutPanel 将 Y 值转换为 146 时上面有东西的事实。但事实并非如此够了,还是……???

log.Debug(String.Format("Mouse Down Location.Y = {0}", e.Location.Y));
_StartPoint = PointToScreen(e.Location);
log.Debug(String.Format("Mouse Down PointToScreen Location.Y = {0}",    _StartPoint.Y));

还有一个更新

我终于能够在这里找到矩形下方的文本框以及我的问题的答案(我无法在 StackOverflow 上发布新问题,因为我已经超过了 30 天内 50 个问题的限制) :

http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/9039ab0c-a587-476c-a952-9f35c6ccba42/

...但现在我有另一个问题:

代码(下面)现在确实找到了它下面的控件,因为它循环通过面板上的 all 文本框,并且它确实发现了绘制的矩形下面的正确数量的控件(如果我拖过九个文本框,找到九个)。

但是,标记为找到的文本框不是正确的——它们不是我拖过的文本框,而是位于 TableLayoutPanel 中的几行下方!

绘制的 _FrameRect 是从 PointToScreen() 转换而来的,而 textBox.Bounds 是从 RectangleToScreen() 转换而来的,所以它们应该是同步的,但不知何故,它们不是……(如果我没有这些转换绝对位置到相对位置,根本没有发现任何文本框)。

private void HighlightAllTextBoxValsBetweenPoints() {
    foreach (Control ctrl in tableLayoutPanelGreatgooglyMoogly.Controls) {
        var tb = ctrl as TextBox;
        if (tb == null)
            continue;
        if (RectangleToScreen(tb.Bounds).IntersectsWith(_FrameRect)) {
            //MessageBox.Show(String.Format("{0}", tb.Name));
            tb.BackColor = PSEUDO_HIGHLIGHT_COLOR;
        }
    }
}

再次,找到正确数量的控件,它们被“突出显示”(backColor 属性已更改),但它们不是正确的。

最终更新

根据这里上传的一只猫(我猜是青蛙腿)的代码:

http://www.c-sharpcorner.com/Forums/Thread/170813/

...我能够使控件的伪突出显示正常工作。以下是相关代码:

// Declare a rectangle and a point:
Rectangle describedRect;
Point startingPoint;
private bool _IsSelecting = false; // <- already had this

// In the MouseDown event:
_IsSelecting = true;
startingPoint = e.Location;

// In the MouseMove event:
if (!_IsSelecting)
    return;
describedRect = Rectangle.FromLTRB(startingPoint.X, startingPoint.Y, e.X, e.Y); 

// In the MouseUp event:
if (!_IsSelecting) 
    return;
HighlightAllTextBoxValsBetweenPoints();
_IsSelecting = false;

private void HighlightAllTextBoxValsBetweenPoints() {
    foreach (Control ctrl in tableLayoutPanelGreatGooglyMoogly.Controls) {
        var tb = ctrl as TextBox;
        if (tb == null)
            continue;

        if (describedRect.IntersectsWith(tb.Bounds)) {
            tb.BackColor = PSEUDO_HIGHLIGHT_COLOR;
        }
    }
}

【问题讨论】:

  • Width =Height = 行中将- 更改为+ 一样简单吗?
  • 不,我试过了。从表面上看(不是双关语),这似乎是有道理的,但实际上它会使事情变得更糟。
  • 重新阅读您的其他问题 - 相同的答案:更改为 tableLayoutPanel1.PointToClient() ... 并交换您的数学 (_FrameRect.Width = pt.X - _StartPoint.x);
  • 我在上面添加了我最新的代码;反转数学几乎有效。

标签: c# winforms graphics drawing paint


【解决方案1】:

您需要使用您尝试使用的TableLayoutPanel 控件的PointToScreen 方法:

private void tableLayoutPanel1_MouseDown(object sender, MouseEventArgs e) {
  if (e.Button != MouseButtons.Left)
    return;

  _IsSelecting = true;
  _StartPoint = tableLayoutPanel1.PointToScreen(e.Location);
  _FrameRect = new Rectangle(_StartPoint, Size.Empty);
}

private void tableLayoutPanel1_MouseMove(object sender, MouseEventArgs e) {
  if (!_IsSelecting)
    return;

  ControlPaint.DrawReversibleFrame(_FrameRect, Color.Black, FrameStyle.Dashed);    
  Point pt = tableLayoutPanel1.PointToScreen(e.Location);
  _FrameRect.Width = pt.X - _StartPoint.X;
  _FrameRect.Height = pt.Y - _StartPoint.Y;
  ControlPaint.DrawReversibleFrame(_FrameRect, Color.Black, FrameStyle.Dashed);
}

【讨论】:

  • 我已经尝试过了,并且像许多其他示例一样,它几乎可以工作 - 但矩形绘制得太远(“Y”值太低) . “X”是完美的。
  • @ClayShannon 哎呀,我的错。更新了代码。只需指定应该转换 PointToScreen 变量的控件,在您的情况下,它将是 tableLayoutPanel1
  • 我可以看到 Location.Y “关闭”(太​​高)的差异是我面板上方部分的高度。 IOW,如果顶部面板高 3 英寸,并且我在中间面板下方一英寸处按下鼠标,则在顶部面板下方一英寸处绘制矩形。但是我需要添加顶部面板的高度以使矩形正确绘制?抵消似乎没有任何区别。这也不是:_StartPoint.Y = _StartPoint.Y + panelTop.Size.Height;
  • 完美,有效(将 tableLayoutPanel1 添加到对 PointToScreen 的调用中)!
  • 但是(请参阅我原始帖子中的上一次更新)
猜你喜欢
  • 1970-01-01
  • 2016-01-11
  • 1970-01-01
  • 2012-05-28
  • 1970-01-01
  • 1970-01-01
  • 2011-06-21
  • 2020-01-19
  • 1970-01-01
相关资源
最近更新 更多