简体   繁体   English

'参数无效' C# winforms 图形错误

[英]'parameter is not valid' C# winforms graphics error

I am trying to create a game in C#, using winforms, where shapes randomly appears and you have to click the shapes (one at a time) as quickly as possible.我正在尝试使用winforms在C#中创建一个游戏,其中形状随机出现,您必须尽快单击形状(一次一个)。

When the shape is created, a timer starts, this timer counts how long it took you to click the shape, then once clicked, the shape deletes.创建形状后,会启动一个计时器,该计时器会计算您单击该形状所花费的时间,然后单击该形状后,该形状将被删除。 I am having trouble with the shape deleting part.我在删除形状部分时遇到问题。 I have the timer, and I have it waiting for the timer to be stopped, but whenever I try to delete the shape I get the following error at the line: g.clear(Color.black) .我有计时器,我让它等待计时器停止,但是每当我尝试删除形状时,我都会在该行收到以下错误: g.clear(Color.black)

System.ArgumentException: 'Parameter is not valid.' System.ArgumentException:“参数无效。”

The full error trace is:完整的错误跟踪是:

  System.ArgumentException
  HResult=0x80070057
  Message=Parameter is not valid.
  Source=System.Drawing
  StackTrace:
  at System.Drawing.Graphics.Clear(Color color)
  at NumbersGame.ShapesRound.<ShapesWindow_Paint>d__9.MoveNext() in
  C:\Users\Matthew\Desktop\AHProject\NumbersGame\NumbersGame\ShapesRound.cs:line 92 

This is all of my code for the game:这是我所有的游戏代码:

namespace NumbersGame
{
    public partial class ShapesRound : UserControl
    {
        public ShapesRound()
        {
            InitializeComponent();
        }
        public bool GameStarted = false;
        public GraphicsPath path;
        public Rectangle currentShape = new Rectangle();
        Stopwatch timer = new Stopwatch();
        public bool timerIsRunning = false;

        public bool ShapeClicked(Point location)
        {
            bool clicked = false;
            if (currentShape.Contains(location))
            {
                clicked = true;
            }
            return clicked;
        }

        private void ShapesRound_Load(object sender, EventArgs e)
        {
            path = new GraphicsPath();
            nameBox.Text = "Matthew";
            // nameBox.Text = Welcome.name;
            scoreBox.Text = Welcome.totalScore.ToString();
        }

        private void ShapesRound_MouseDown(object sender, MouseEventArgs e)
        {
            if (ShapeClicked(e.Location))
            {
                //   MessageBox.Show("CLICKED");
                //end timer
                timer.Stop();
                var timeP = timer.ElapsedMilliseconds / 1000;
                //   MessageBox.Show(timeP.ToString() + " SECONDS");  
            }
        }
    
        private async void ShapesWindow_Paint(object sender, PaintEventArgs e) 
        {
            if (GameStarted == false) { return; } //if the game hasnt started (ie the start button has not been clicked), do nothing
            else
            { 
                using (Graphics g = e.Graphics)
                {
                    g.SmoothingMode = SmoothingMode.AntiAlias;
                    currentShape = new Rectangle(10, 100, 75, 75); //assign coordinates to the global CurrentShape variable
                    g.FillEllipse(new SolidBrush(Color.Black), currentShape);
                    
                    //INVALID PAARAM?//fill the currentshape on screen
                    //start a timer 
                    timer.Start(); //start a timer
                    while (timer.IsRunning) //while the timer is running (ie shape isnt clicked) wait 
                    {
                        await Task.Delay(500);
                    }
                    g.Clear(Color.Black);
                    //    currentShape = null; 

                    //  MessageBox.Show("DELETING");
                    var bckCol = ShapesWindow.BackColor;

                    // e.Graphics.FillEllipse(new SolidBrush(bckCol), currentShape);
                    //     e.Graphics.Clear(Color.Black); //INVALID PAARAM?
                    ShapesWindow.Refresh();
                }              
            }        
        }
       
        private void StartButton_Click(object sender, EventArgs e)
        {
            GameStarted = true;
            ShapesWindow.Paint += new PaintEventHandler(ShapesWindow_Paint);
            
            ShapesWindow.Refresh();    
        }  
    }
}

If I understand correctly, you are drawing a shape.如果我理解正确,您正在绘制一个形状。 Then when that shape is clicked, you want to delete that shape and draw a new one, and the process repeats.然后,当单击该形状时,您要删除该形状并绘制一个新形状,然后重复该过程。

If I'm correct, then reverse your logic.如果我是对的,那就颠倒你的逻辑。 Right now, you are drawing a shape, waiting, then trying to clear.现在,您正在绘制一个形状,等待,然后尝试清除。 But you can clear first, then draw the shape.但你可以先清除,然后绘制形状。 That way, when you detect a click, you just trigger a refresh.这样,当您检测到点击时,您只需触发刷新。

private void ShapesRound_MouseDown(object sender, MouseEventArgs e)
{
    if (ShapeClicked(e.Location))
    {
        //   MessageBox.Show("CLICKED");
        //end timer
        timer.Stop();
        var timeP = timer.ElapsedMilliseconds / 1000;
        //   MessageBox.Show(timeP.ToString() + " SECONDS");
        
        ShapesWindow.Refresh();
    }
}

private async void ShapesWindow_Paint(object sender, PaintEventArgs e) 
{
    if (GameStarted == false) { return; } //if the game hasnt started (ie the start button has not been clicked), do nothing
    else
    { 
        using (Graphics g = e.Graphics)
        {
            g.Clear(Color.Black);
            
            g.SmoothingMode = SmoothingMode.AntiAlias;
            currentShape = new Rectangle(10, 100, 75, 75); //assign coordinates to the global CurrentShape variable
            g.FillEllipse(new SolidBrush(Color.Black), currentShape);
            
            //start a timer 
            timer.Start(); //start a timer
        }              
    }        
}

If there's ever a time you don't want to draw a new shape, then declare a bool value that will tell it to not draw a new shape.如果您不想绘制新形状,则声明一个bool值,告诉它不要绘制新形状。 Set that whenever you don't want to draw a new value, and check that in your paint event after you clear, like:每当您不想绘制新值时设置它,并在清除后在您的绘制事件中检查它,例如:

...
g.Clear(Color.Black);
if (dontDrawNewShape) return;
...

This isn't really an answer to your original question but rather to your comment:这并不是对您最初问题的真正答案,而是对您的评论的回答:

Maybe I'm missing something here.也许我在这里遗漏了一些东西。 I'm trying to paint an object essentially when the last object has been clicked.我正在尝试绘制 object,基本上是在单击最后一个 object 时。 That involves deleting the first object, and painting a new one, upon the click of the old one.这涉及删除第一个 object,并在单击旧的时绘制一个新的。 How do you suggest I go about doing that?您如何建议我 go 这样做? In a non-"argh" inducing way.以非“argh”诱导方式。

As rene and Flydog57 pointed out, your approach is very nonstandard.正如 rene 和 Flydog57 指出的那样,您的方法非常不标准。 Rather than start from where you are, let me just expand on their comments and explain the standard way of structuring a Windows Forms app like the one you're trying to build.与其从您所在的位置开始,不如让我扩展他们的评论并解释构建 Windows Forms 应用程序的标准方法,就像您要构建的应用程序一样。

  1. Keep track of the "state" of your control (what objects are visible, etc.) in member variables on the control.在控件的成员变量中跟踪控件的“状态”(哪些对象可见等)。
  2. The Paint event is just for refreshing the control, or part of it, in one pass. Paint事件仅用于一次刷新控件或其中的一部分。 If something that would affect the appearance of your control happens, call Invalidate() from whatever event handler has resulted in the state change and trust Windows to notify your control via the Paint event when it's convenient.如果发生会影响控件外观的事情,请从导致 state 更改的任何事件处理程序调用Invalidate()并信任 Windows 在方便时通过 Paint 事件通知您的控件。 Note: Invalidate() doesn't cause a synchronous draw like Refresh() does (which is usually overzealous);注意: Invalidate()不会像Refresh()那样导致同步绘制(这通常是过分热心的); it instead queues up a redraw for when Windows is done handling higher-priority messages like mouse movements.当 Windows 完成处理更高优先级的消息(如鼠标移动)时,它会排队重绘。
  3. Remember that the Graphics object you're passed in the Paint handler is only good for one redraw.请记住,您在Paint处理程序中传递的Graphics object 仅适用于一次重绘。 It becomes invalid once you return from that handler or, as in your code, you start a new repaint (as you do with Refresh , making your Paint handler recursive, which is part of what was so argh-inducing about it).一旦您从该处理程序返回,或者像在您的代码中那样,您开始一个新的重绘(就像您对Refresh所做的那样,使您的Paint处理程序递归,这是它如此引人入胜的一部分),它就会变得无效。
  4. Keep in mind that what you draw in the Paint event handler isn't persistent.请记住,您在Paint事件处理程序中绘制的内容不是持久的。 That is, if something occludes the window, or you drag it offscreen, or anything else happens that causes the video memory allocated to your control to be invalidated, Windows will fire the Paint event for you to redraw whatever was there before.也就是说,如果有东西遮挡了 window,或者您将其拖到屏幕外,或者发生其他任何事情导致分配给您的控件的视频 memory 无效,那么 Windows 之前发生的任何事情都会触发Paint事件以触发您重绘事件。 This is another reason you want to keep track of state in a member variable--your control may be asked to redraw itself when its state has not changed.这是您希望在成员变量中跟踪 state 的另一个原因 - 当其 state 未更改时,可能会要求您的控件重新绘制自身。 You can't rely on video memory to accurately represent what has been drawn in the past.您不能依靠视频 memory 来准确表示过去绘制的内容。
  5. If something needs to be animated or happen after an elapsed period, create a System.Windows.Forms.Timer as a member variable, register a Tick handler, and Start() the timer.如果需要动画或在经过一段时间后发生某些事情,请创建一个System.Windows.Forms.Timer作为成员变量,注册一个Tick处理程序,然后Start()计时器。 The Tick handler should update the persistent state of the control as appropriate and call Invalidate() . Tick处理程序应适当更新控件的持久 state 并调用Invalidate() If the timer was intended for a one-off action, Stop() it.如果计时器用于一次性操作,则Stop()它。
  6. For something like a hit test on a shape you've drawn, handle the control's Click or MouseDown event and test against the member variable that you're using to persist state.对于您绘制的形状的命中测试,处理控件的ClickMouseDown事件并针对您用于持久化 state 的成员变量进行测试。 If your last drawn shape was a Rectangle , keep that Rectangle in a member variable and test the mouse location from the EventArgs against that Rectangle .如果您最后绘制的形状是Rectangle ,则将该Rectangle保存在成员变量中,并根据该Rectangle测试EventArgs中的鼠标位置。 If it's then time to draw a different shape, create a new persistent object that represents the new shape and call Invalidate() .如果是时候绘制不同的形状,请创建一个新的持久 object 来表示新形状并调用Invalidate()

Let me know if you have questions about this general outline and I'll update my answer accordingly.如果您对此一般大纲有任何疑问,请告诉我,我会相应地更新我的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM