Jump to content
Xtreme .Net Talk

snarfblam

Leaders
  • Posts

    2156
  • Joined

  • Last visited

  • Days Won

    2

Everything posted by snarfblam

  1. Glad to help. What have you got so far?
  2. Why not move the code into a separate method that you can call both from the MouseClick event and where ever else you would like to run the code from.
  3. AllPaintingInWmPaint is a good plan if you really are doing all painting in the Paint events/methods. It gives WinForms a better idea of how you are painting (i.e. that you are adhering strictly to the standard invalidation/paint cycle) so it can handle its own side of the painting a little better. However, the biggest issue this seems to address are flickering and double-buffering. From MSDN,
  4. I don't know if there is a simple way to do this. I suppose a MenuStrip is rendered with a ToolStripRenderer. You could write your own renderer. You might even be able to inherit the standard renderer and override one or two methods to make it happen. If you really want your .NET menu look the same as a VB6 menu, the best thing to do would be to skip the MenuStrip and go with a standard MainMenu control. This isn't shown in the toolbox by default, but it can be added easily (right-click the toolbox and select "Choose Items..."). This will give you a standard windows menu.
  5. Sorry, I can't really make a lot of sense of your PctAnimate_Paint code. I'm not going to pick on your coding style, but I can't really do much with that. I know plenty about GDI+ painting, but not so much about OpenGL or what's going on inside your app, so if the code's not clear it might as well be Chinese.
  6. Would GetWindowRect do what you need?
  7. I'm having a little bit of trouble understanding exactly what your code is doing. How you deal with the paint-related events depends on how you are drawing. Are you rendering a static image every time you need to redraw the control? Or are you continuously rendering (i.e. on a loop or timer). It sounds like the former. Unless you are running a rendering loop, you should be redrawing your pctanimate control every time its paint event is raised. You haven't posted the code for the handler, but it sounds like the problem may be there. It looks like you are resizing controls in your frmBackground's Paint event. Generally, the only thing you should be doing in the Paint event handler is drawing to the form. Resizing controls every time the form is painted sounds like a bad idea to me.
  8. Hi EccentricDyslex. Just two things I want to point out. One is that it helps a lot in terms of readability if you use
  9. Looks alright. Are you having any issues with it?
  10. You don't describe what the XML data is/represents or what the textboxes are supposed to represent/contain, or how you would like to separate the XML into textboxes. Please elaborate on the question. (This doesn't mean simply fill in the blanks I drew for you, but rather restate the question with any information that might be necessary to answer the question.)
  11. public void shuffle(int seed) { Random rnd = new Random(seed); This code creates a new random object every time the function is called. The instantiation occurs in the function, therefore you create an instance each time the function is invoked. If it is called in succession too quickly you will get the same seed on those successive calls. That was the original problem. public static class Program { // ... the usual ... Random randomGenerator = new Random(); public Random RandomGenerator { get { return randomGenerator; }} } This creates a single random object. It is a static declaration, so you get one instance shared everywhere. The point is that you seed it only once, ever. If you only seed it once, you don't have to worry about seeding it too frequently. HiPerfTimer ht = new HiPerfTimer(); ht.Start(); Random rnd = new Random(Convert.ToInt32(Right(ht.Duration.ToString(),7))); This is like putting a turbo in your car to compensate for the fact that the hand brake is engaged. Just dis-engage the hand brake. And what happens when your program is run on a machine that doesn't have a high resolution timer?
  12. Why not just use a single random object? public static class Program { // ... the usual ... Random randomGenerator = new Random(); public Random RandomGenerator { get { return randomGenerator; }} } // Elsewhere... // (two overloads... one to use a shared random generator // and one that allows a seed to be specified for debugging) public void shuffle() { shuffle(-1); // Magic number } public void shuffle(int seed) { Random rnd; if(seed == -1) // Magic number rnd = Program.RandomGenerator; else rnd = new Random(seed); int sort1; int sort2; Card tmpcard = new Card(); for (int ctr = 0; ctr < 100; ctr++) { sort1 = (int)((rnd.NextDouble() * 52) + 1); sort2 = (int)((rnd.NextDouble() * 52) + 1); tmpcard = this.Cards[sort1]; this.Cards[sort1] = this.Cards[sort2]; this.Cards[sort2] = tmpcard; } this.next = 1; //reset pointer to first card } With one Random object exposed through the Program class, it's easy for the entire program to use a single random number generator, as is generally considered good practice.
  13. The normal approach would be to use a single random number generator throughout an entire application. Other than for debugging purposes there are few situations where it makes sense to create more than one Random object (or call Randomize more than once).
  14. First and foremost, you can not DirectCast from a name to anything. Code like the following: Dim ctrl As Timer = Directcast("Timer1", Timer) is bound to fail. What you are really doing there is casting the string "Timer1" to a timer, which is, of course, invalid. The string is not a timer, it is a string. The other problem is that a timer isn't a control. It is a component. In VB6 we didn't make this distinction. In DotNet the two are different things, and components aren't stored in the control collection, they are stored in the component collection. Also, unlike a control, a component does not have a .Name property. A component is oblivious to it's design-time name, and can't be referenced by its name. The best you can do is enumerate over components.Components and identify which ones are timers. As an alternative you could add all the timers to a collection (or a dictionary if you need to name the timers) and use that to identify all the timers. For Each c As Component In components.Components
  15. Re: Call Paint Event PD split your post from the thread because the thread was seven years old. You generally want to start a new thread with a link to the old thread if the thread is more than a month old or if your post is not answering or supplementing an answer to the original question. As far as why invalidate works, it's not quite like your typical cross-thread call (.Invoke). It does cause code the be called on the UI thread, but only because it works through the message pump. That's just a happy accident I guess. The reason Invalidate doesn't always cause a redraw is because Windows tries to optimize the invalidation/paint cycle. If you invalidate several rectangles in quick succession, Windows can combine them into a single paint event. (Notice, if your version of windows doesn't use desktop compositing, that when you drag window X over windows Y, window X moves for several frames, leaving path of blankness or artifacts in its wake, before window Y is redrawn.) This also means that Windows tends to "wait and see" when you invalidate. There can be a slight delay. When debugging (paused), the message pump is frozen and the Paint event might never be raised.
  16. There should be no reason that you can't do this the same way in a class library that you do in a WinForms app. The class library should create the form and a background worker. The logic should be executed in the background worker and the events can be handled to update the progress form. The component that kicks it all off should probably be invoked on the UI thread. The only difference will be that the code will be spread across multiple classes, but the approach is still exactly the same.
  17. The foreach loop will loop over all items in a collection and assume that they are all the type you specified. You can't use the type as a filter. When the loop comes to something that isn't a Movie it throws an error because it was only expecting Movies. If you want to filter by type, you need to do this yourself. The foreach loop should specify a valid type for all items in the collection. In this case that would be RentalItem. Then, inside the loop, you can check the type of each object using the is operator (or the as operator). foreach (RentalItem r in rentalList) { if(r is Movie) { [color="Green"] // ...[/color] } }
  18. How are you doing this? Are you calculating the numbers as they are shown? Or are you calculating them all before hand? And have you already gotten the bomb placement sorted out?
  19. Of course. Ask any questions you have.
  20. In this code: className.OnProcessUpdates += new ClassNameControl.ProcessUpdatesDelegate(this.DoSomething); If OnProcessUpdates is an event, this should be the correct VB equivalent: AddHandler className.OnProcessUpdates, AddressOf Me.DoSomething However, it's hard to tell what exactly is going on in the C# code. C# syntax treats events very similarly to raw delegates. VB's event syntax, on the other hand, does not extend to normal delegates. If we are dealing with a simple delegate rather than an event, which appears to be the case, the VB equivalent would probably be one of the following: className.OnProcessUpdates = AddressOf Me.DoSomething className.OnProcessUpdates = New ClassNameControl.ProcessUpdatesDelegate(AddressOf Me.DoSomething) className.OnProcessUpdates = [Delegate].Combine(className.OnProcessUpdates, New ClassNameControl.ProcessUpdatesDelegate(AddressOf Me.DoSomething)) The first two are equivalent to each other. The third would be equivalent to C#'s += syntax. What you have, the second option, is not equivalent to the C# code because your code will replace one delegate with another if called multiple times. The original C# code will accumulate the delegates into a MulticastDelegate, which will call all functions that were added instead of only the most recent. Depending on whether the delegate assignment code is called more than once, your code may or may not work as expected. As to the NullReferenceException, you are most likely trying to invoke the delegate before it is assigned. Check to make sure the delegate is not Nothing before trying to invoke it.
  21. Let's take a couple of steps back. We don't need to declare anything as an event or an EventHandler. I guess I should have been clearer about the "handles" part. You have a control with an event you want to handle (in our case, "mineField(x, y).Click). You have an event handler that you want to use to handle that event (in our case, "mineField_Click"). You need to connect the event to the event handler. VB gives us two ways to do that: "Handles" and "AddHandler." The "Handles" clause is intended to be used for a "pre-defined" control, for example, something you put on the form in the designer. AddHandler is a dynamic way of doing the same thing. You generally use AddHandler for something you create at run-time. You only use one or the other. For example, these two code listings do the same thing: Class MyForm Inherits Form [color="Blue"]WithEvents[/color] b As Button [color="Green"]' Normally, the designer automatically writes this bit for you[/color] Public Sub New() [color="Green"]' Normally the designer creates the button for you[/color] b = New Button Controls.Add(b) End Sub Sub Button_Click(sender As Object, e As EventArgs) [color="Blue"]Handles b.Click[/color] MessageBox.Show("Wee-ow!") End Sub End class Class MyForm Inherits Form Dim b As Button [color="Green"]' Normally, the designer automatically writes this bit for you[/color] Public Sub New() [color="Green"]' Normally the designer creates the button for you[/color] b = New Button Controls.Add(b) [color="Blue"]AddHandler b.Click, AddressOf Button_Click[/color] End Sub Sub Button_Click(sender As Object, e As EventArgs) MessageBox.Show("Wee-ow!") End Sub End class The parts in blue are the code that connects the event to the handler. Both programs above will have the same exact result. When you use AddHandler, you use it instead of Handles. We don't need the Handles part. When you create controls at run-time like we are, it's often easier to use AddHandler. If you are storing the controls in an array, like we are, YOU MUST use AddHandler. There is no way to use Handles with an array of controls. Hopefully, that should solve problem #3. Now, as for storing both the X and the Y values in the .Tag property, well, I was trying to get you to figure this one out yourself. I can't keep giving you almost complete code. It's against this forum's policy and for a good reason. It's not the right way to learn. Programming is as much about learning how to figure things out as it is about knowing how to do things. My recommendation was to create a Point and store that in the tag. You see, you can only put one object in the .Tag, but certain kinds of objects can hold multiple pieces of data. A Point object holds an X and a Y, which is exactly what we need. If we wanted a Point with an X of 25 and a Y of 10, we would create it like this: Dim p As Point = New Point(25, 10) [color="Green"]' or[/color] Dim p As New Point(25, 10) Easy, no? Now you have p.X and p.Y, both stored in p, which is a Point. Here is an example of how you could use a Point: Dim p As New Point(100, 100) Me.Location = p [color="Green"]' Set a form's location to X:100, Y:100[/color] That is not what your code should do. That's an example of how we can put an X and a Y into a single object and treat it as a whole. You can also get the X and the Y back out again. Dim p As New Point(75, 33) [color="Green"]' Later that day[/color] Dim someX as Integer = p.X Dim someY an Integer = p.Y MessageBox.Show("X = " & CStr(someX)) MessageBox.Show("Y = " & CStr(someY)) So... We have an X and a Y we want to store in a Tag. But a Tag can only hold one object. But we do have a way of putting an X and a Y into one object, so what do we do? Hopefully, that solves #1. It looks like you may have already solved #2. Just to be sure, I'm going to highlight what you got right: Private Sub mineGrid_Click(ByVal sender As Object, ByVal e As [color="Blue"]System.EventArgs[/color])
  22. Are you putting this code into Visual Studio and running it? Are you checking to make sure it's doing what it's supposed to? The reason I ask is because of this: mineField(x, y).Tag = CStr(x) mineField(x, y).Tag = CStr(y) [color="Green"]' Also note that the tag does [i]not[/i] need to be a string[/color] You can only set the tag to one thing at a time. If you need to store more than a single value (X and Y would be two values) you need to use a data type that holds more than a single value. If you run the program, when you click the button you should notice that only the Y value is coming up. The X will be missing.
  23. First things first. I don't know if it's just the way you pasted it or if that is actually how you have the code set up, but you want all the constants and, most importantly, the array, declared outside of any sub. If they're declared within a sub, they can only be accessed within that sub, but you'll need to access them from other subs too. If you declare them outside a sub but inside the form's code, you can access them anywhere within the form's code.
  24. Re: VB.NET --> Screen Resolution Change Example Complete! The documentation from MSDN explains the parameters and, more importantly, the return value. The -2 is DISP_CHANGE_BADMODE (from http://www.mrexcel.com/forum/showthread.php?t=10616 via google). MSDN states that this return value indicates: .
  25. You've taken a big step in the right direction, but your approach is a little unorthodox: For y As Integer = 81 To 225 Step 16 For x As Integer = 12 To 156 Step 16 You're looping over the locations (pixel coordinates) you want the buttons at. While it works, I recommend against it. You would be better served by looping over the cell coordinates: For y As Integer = 0 To 9 [color="Green"]' 10 Wide[/color] For x As Integer = 0 To 9 [color="Green"]' 10 Tall[/color] Then you can calculate the location of each button based on the X and Y. This might strike you as a little round-a-bout. Why not have the for-loop calculate the location for you? Because it makes the code a little harder to read, and much harder to modify. And on that note, I strongly recommend you use constants instead of putting the values directly in your logic. That would bring us here: Const GameWidth As Integer = 10 Const GameHeight As Integer = 10 Const CellWidth As Integer = 16 Const CellHeight As Integer = 16 Const GridLeft As Integer = 12 Const GridTop As Integer = 81 [color="Green"]' Later on...[/color] For y As Integer = 0 To GameWidth - 1 For x As Integer = 0 To GameHeight - 1 buttons(x, y) = new Button Dim ButtonX As Integer = GridLeft + X * CellWidth Dim ButtonY As Integer = GridTop + Y * CellHeight buttons(x,y).Location = New Point(ButtonX, ButtonY) Now, that seems like a lot of extra work to get the same job done, but notice two things. First, we don't even need any comments there. You, any other programmer, or you a year in the future can look at that code and know what the loop does. It loops over the game field, makes buttons, and calculates the positions. It's even obvious how it calculates the positions. Because we use descriptive names rather than "magic numbers", we have what we call self-documenting code. You should always comment your code when you need to, but the less often you need to, the better. The other thing to notice is that we can now easily tweak our program by changing the constants. Want to make the game 20x20? Change GameWidth and GameHeight each to 20. Want to make the cells wider? Change CellWidth to 20. The bigger problem with your code is that you seem to have overlooked a huge point in my first post: the 2-D array. If you don't understand them that well, I'm glad to answer any questions, but it really isn't too different from a normal array. Instead of identifying each value by one index, you identify each value by two indecies. It makes the array look more like a table (or grid), instead of a list. ' Why 200? Dim Buttons(200) As Button ' Becomes Dim Buttons(GameWidth - 1, GameHeight - 1) As Button Again, more code, but it explains itself much better. The reason I so strongly recommend the 2-D array is because it creates a strong connection between the visual and logical aspects of the program. If the user clicks on button(3, 3), you need to show the number of surrounding bombs for that spot. You need to look at each surrounding cell and tally the bombs. (The approach you're taking for assigning the numbers probably won't work. Each spot either has a bomb or it doesn't. You need to calculate the numbers later. We can get into all that later.) For button(3, 3), these are the surrounding cells we need to check: button(2, 2): up, left button(2, 3): up button(2, 4): up, right button(3, 2): left button(3, 4): right button(4, 2): down, left button(4, 3): down button(4, 4): down, right We can generalize this very easily: button(x - 1, y - 1): up, left button(x, y - 1): up button(x + 1, y - 1): up, right button(x - 1, y): left button(x + 1, y): right button(x - 1, y + 1): down, left button(x , y + 1): down button(x + 1, y + 1): down, right Now we can apply our surrounding-bomb-counting logic to any (x, y). How would you do that with your array? If the user clicks button(55), how does the program know which surrounding boxes to check? Button(54) would (usually) be to the left, and button(56) to the right, but you have no easy way to figure out which buttons are above and below. The user interface is a grid. Why not make our data a grid? Now, if you can digest the loops, the constants, and the 2-D array we can move onto distributing and counting the bombs and what happens when the user clicks a button.
×
×
  • Create New...