Send feedback/effect to joystick from .net - c#

Thanks to this answer https://stackoverflow.com/a/13734766/637142 I am able to know when a button is pressed or when the steering wheel is rotated. Now my question is how do I send an effect to the device? For example when I am playing a game if I crash the wheel will vibrate. How could I make the steering wheel vibrate?
I belive what I need to do is to Start() an effect (http://sharpdx.org/documentation/api/t-sharpdx-directinput-effect). The SharpDX.DirectInput.Joystick class does not seem to have a method to return me all the effects. There is a method called GetEffects but that method returns a collection of EffectInfo objects. How does a game sends commands to the joystick?

The source code is copy-pasted from here.
To use this source you need a "Force Effect file" (C:\MyEffectFile.ffe), to "play" it on the joystick.
According to this book to create the "force effect" file you need to use the "Force Editor" that came with DirectX SDK.
(the same book, alternatively, state that you can create the effect from scratch in the code... down in the answer I've found another piece of code that create and use an effect without loading it from a file :-))
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Microsoft.DirectX;
using Microsoft.DirectX.DirectInput;
namespace JoystickProject
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private Device device = null;
private bool running = true;
private ArrayList effectList = new ArrayList();
private bool button0pressed = false;
private string joyState = "";
public bool InitializeInput()
{
// Create our joystick device
foreach(DeviceInstance di in Manager.GetDevices(DeviceClass.GameControl,
EnumDevicesFlags.AttachedOnly | EnumDevicesFlags.ForceFeeback))
{
// Pick the first attached joystick we see
device = new Device(di.InstanceGuid);
break;
}
if (device == null) // We couldn't find a joystick
return false;
device.SetDataFormat(DeviceDataFormat.Joystick);
device.SetCooperativeLevel(this, CooperativeLevelFlags.Exclusive | CooperativeLevelFlags.Background);
device.Properties.AxisModeAbsolute = true;
device.Properties.AutoCenter = false;
device.Acquire();
// Enumerate any axes
foreach(DeviceObjectInstance doi in device.Objects)
{
if ((doi.ObjectId & (int)DeviceObjectTypeFlags.Axis) != 0)
{
// We found an axis, set the range to a max of 10,000
device.Properties.SetRange(ParameterHow.ById,
doi.ObjectId, new InputRange(-5000, 5000));
}
}
// Load our feedback file
EffectList effects = null;
effects = device.GetEffects(#"C:\MyEffectFile.ffe",
FileEffectsFlags.ModifyIfNeeded);
foreach(FileEffect fe in effects)
{
EffectObject myEffect = new EffectObject(fe.EffectGuid, fe.EffectStruct,
device);
myEffect.Download();
effectList.Add(myEffect);
}
while(running)
{
UpdateInputState();
Application.DoEvents();
}
return true;
}
private void PlayEffects()
{
// See if our effects are playing.
foreach(EffectObject myEffect in effectList)
{
//if (button0pressed == true)
//{
//MessageBox.Show("Button Pressed.");
// myEffect.Start(1, EffectStartFlags.NoDownload);
//}
if (!myEffect.EffectStatus.Playing)
{
// If not, play them
myEffect.Start(1, EffectStartFlags.NoDownload);
}
}
//button0pressed = true;
}
protected override void OnClosed(EventArgs e)
{
running = false;
}
private void UpdateInputState()
{
PlayEffects();
// Check the joystick state
JoystickState state = device.CurrentJoystickState;
device.Poll();
joyState = "Using JoystickState: \r\n";
joyState += device.Properties.ProductName;
joyState += "\n";
joyState += device.ForceFeedbackState;
joyState += "\n";
joyState += state.ToString();
byte[] buttons = state.GetButtons();
for(int i = 0; i < buttons.Length; i++)
joyState += string.Format("Button {0} {1}\r\n", i, buttons[i] != 0 ? "Pressed" : "Not Pressed");
label1.Text = joyState;
//if(buttons[0] != 0)
//button0pressed = true;
}
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.BackColor = System.Drawing.SystemColors.ActiveCaptionText;
this.label1.Location = new System.Drawing.Point(8, 8);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(272, 488);
this.label1.TabIndex = 0;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.BackColor = System.Drawing.SystemColors.ControlText;
this.ClientSize = new System.Drawing.Size(288, 502);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.label1});
this.Name = "Form1";
this.Text = "Joystick Stuff";
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (Form1 frm = new Form1())
{
frm.Show();
if (!frm.InitializeInput())
MessageBox.Show("Couldn't find a joystick.");
}
}
}
}
I've just found here another piece of code that maybe useful.
This sample seem to create the effect from scratch, so you shouldn't need an "effect file".
DeviceList xDeviceList = Manager.GetDevices(DeviceClass.GameControl, EnumDevicesFlags.AttachedOnly);
DeviceInstance someDeviceInstance;
foreach (DeviceInstance deviceInstance in xDeviceList)
{
someDeviceInstance = deviceInstance;
break;
}
Device someDevice = new Device(someDeviceInstance.InstanceGuid);
someDevice.SetCooperativeLevel(this.Handle, CooperativeLevelFlags.Exclusive | CooperativeLevelFlags.Background);
int[] axis = new int[0];
foreach (DeviceObjectInstance doi in someDevice.Objects)
{
if((doi.Flags & (int)ObjectInstanceFlags.Actuator) != 0)
{
axis = new int[axis.Length + 1];
axis[axis.Length - 1] = doi.Offset;
}
}
someDevice.Acquire();
Effect effect = new Effect();
effect.SetDirection(new int[axis.Length]);
effect.SetAxes(new int[axis.Length]);
effect.ConditionStruct = new Condition[axis.Length];
effect.Flags = EffectFlags.Cartesian | EffectFlags.ObjectOffsets;
effect.Duration = int.MaxValue;
effect.SamplePeriod = 0;
effect.Gain = 10000;
effect.TriggerButton = (int)Microsoft.DirectX.DirectInput.Button.NoTrigger;
effect.TriggerRepeatInterval = 0;
effect.UsesEnvelope = false;
effect.EffectType = Microsoft.DirectX.DirectInput.EffectType.ConstantForce;
effect.StartDelay = 0;
effect.Constant = new Microsoft.DirectX.DirectInput.ConstantForce();
effect.Constant.Magnitude = -5000;
EffectObject effectObject = null;
foreach (EffectInformation ei in someDevice.GetEffects(EffectType.ConstantForce))
{
effectObject = new EffectObject(ei.EffectGuid, effect, someDevice);
}
effectObject.SetParameters(effect, EffectParameterFlags.Start );
And here is another link then may be useful Force feedback sample

Related

Prevent parent container from scrolling by the mouse wheel if a child (graphic) control is already scrolling

How to prevent the parent container from scrolling by the mouse wheel if the child (graphic) control is already focused and is scrolling?
I have a GMap.NET WinForms control embedded in a WinForms. Whenever that control is focused and scrolled via the mouse wheel, the parent form will also be scrolled. I just want the parent form to stay put while I'm scrolling the GMap.NET control via the mouse wheel. And when I want to scroll the parent form, I can always make GMap.NET control lose focus.
How to do what I want?
Here's my code:
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.gMapControl = new GMap.NET.WindowsForms.GMapControl();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.SuspendLayout();
//
// gMapControl
//
this.gMapControl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.gMapControl.Bearing = 0F;
this.gMapControl.CanDragMap = true;
this.gMapControl.EmptyTileColor = System.Drawing.Color.Navy;
this.gMapControl.GrayScaleMode = false;
this.gMapControl.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow;
this.gMapControl.LevelsKeepInMemmory = 5;
this.gMapControl.Location = new System.Drawing.Point(525, 1);
this.gMapControl.MarkersEnabled = true;
this.gMapControl.MaxZoom = 2;
this.gMapControl.MinZoom = 2;
this.gMapControl.MouseWheelZoomEnabled = true;
this.gMapControl.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter;
this.gMapControl.Name = "gMapControl";
this.gMapControl.NegativeMode = false;
this.gMapControl.PolygonsEnabled = true;
this.gMapControl.RetryLoadTile = 0;
this.gMapControl.RoutesEnabled = true;
this.gMapControl.ScaleMode = GMap.NET.WindowsForms.ScaleModes.Integer;
this.gMapControl.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225)))));
this.gMapControl.ShowTileGridLines = false;
this.gMapControl.Size = new System.Drawing.Size(198, 378);
this.gMapControl.TabIndex = 0;
this.gMapControl.Zoom = 0D;
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.BackColor = System.Drawing.Color.Black;
this.flowLayoutPanel1.Location = new System.Drawing.Point(1, 1);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(489, 885);
this.flowLayoutPanel1.TabIndex = 6;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoScroll = true;
this.ClientSize = new System.Drawing.Size(745, 533);
this.Controls.Add(this.flowLayoutPanel1);
this.Controls.Add(this.gMapControl);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
#endregion
private GMap.NET.WindowsForms.GMapControl gMapControl;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
gMapControl.MapProvider = GMapProviders.GoogleTerrainMap;
gMapControl.MinZoom = 5;
gMapControl.MaxZoom = 12;
gMapControl.Zoom = 7.5;
gMapControl.ShowCenter = true;
gMapControl.DragButton = MouseButtons.Middle;
}
}
This is not just a problem of the GMap.NET for sure, I've tested with other (proprietary) graphic control and it behaves all the same.
I'm not sure if there's a better way around this but here's a hacky workaround to stop the WM_MOUSEWHEEL message from reaching the control and still execute the zooming logic.
Add the following class to your project:
public class MyGMapControl : GMapControl, IMessageFilter
{
public MyGMapControl()
{
Application.AddMessageFilter(this);
}
protected override void Dispose(bool disposing)
{
if (disposing) Application.RemoveMessageFilter(this);
base.Dispose(disposing);
}
public bool PreFilterMessage(ref Message m)
{
const int WM_MOUSEWHEEL = 0x020A;
if (m.HWnd == this.Handle && m.Msg == WM_MOUSEWHEEL)
{
Point posOnScreen = new Point(m.LParam.ToInt32());
Point pos = PointToClient(posOnScreen);
int delta = m.WParam.ToInt32();
var args = new MouseEventArgs(MouseButtons.None, 0, pos.X, pos.Y, delta);
this.OnMouseWheel(args);
return true;
}
return false;
}
}
Then, use MyGMapControl instead of GMapControl.
Here, we're creating a MessageFilter to intercept the WM_MOUSEWHEEL message and return true in PreFilterMessage to stop the message from reaching the control. Now, the scrolling won't occur but neither will the zooming logic because it's implemented inside OnMouseWheel() which is triggered by the WM_MOUSEWHEEL message. So, we manually call the OnMouseWheel() method before returning true to ensure that the zooming occurs.
As Jimi suggested in the comments, you could override WndProc() of the control's parent container, check for WM_MOUSEWHEEL, and return if the mouse cursor is over the GMapControl:
protected override void WndProc(ref Message m)
{
const int WM_MOUSEWHEEL = 0x020A;
if (m.Msg == WM_MOUSEWHEEL)
{
if (gMapControl.Bounds.Contains(PointToClient(MousePosition))) return;
}
base.WndProc(ref m);
}

Access button from the form in other class in C#

I want to know if a button is pressed in C# from another class. I have my standard class Form and another class Device. Now I want to access the button from InitializeComponent in Form in my class Device. Does anyone know a good way to do this?
If I press on the btnInitialise I want to show a messagebox (with text "test") to start with. I want to use this button in the class Device. I don't rely know how I can reference the button btnInitialise that is automatically made in my form to the class Device.
public class Form1 : System.Windows.Forms.Form
{
#region "Windows Form Designer generated code"
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.btnInitialise = new System.Windows.Forms.Button();
this.cmbdevice = new System.Windows.Forms.ComboBox();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Location = new System.Drawing.Point(42, 41);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(645, 414);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.grpDevice);
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(637, 388);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "tabPage1";
this.tabPage1.UseVisualStyleBackColor = true;
//
// btnInitialise
//
this.btnInitialise.Location = new System.Drawing.Point(351, 16);
this.btnInitialise.Name = "btnInitialise";
this.btnInitialise.Size = new System.Drawing.Size(96, 25);
this.btnInitialise.TabIndex = 21;
this.btnInitialise.Text = "Initialize";
this.btnInitialise.Click += new System.EventHandler(this.btnInitialise_Click);
//
// Form1
//
this.ClientSize = new System.Drawing.Size(1005, 532);
this.Controls.Add(this.tabControl1);
this.Name = "Form1";
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.grpDevice.ResumeLayout(false);
this.grpDevice.PerformLayout();
this.ResumeLayout(false);
}
private TabControl tabControl1;
private TabPage tabPage1;
private Button btnInitialise;
#endregion "Windows Form Designer generated code"
#region "Global variables"
// OpenLayers fields
////Encapsulates a DT-open layers deviceand manages and distributes subsystems for the device
private Device device = null;
#endregion "Global variables"
//Automatically to initialize components of form
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//Set the culture to en-US for decimal point instead of decimal comma in results
CultureInfo english = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentCulture = english;
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (device != null)
{
device.Dispose();
}
}
base.Dispose(disposing);
}
/// <summary>
/// The main entry point for the application.
/// </summary>
///
//Run application, show error message if appl doesnt run
[STAThread]
private static void Main()
{
try
{
Application.Run(new Form1());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString(), "Error");
}
}
}
public class Device
{
//When clicking on the initialize button show messagebox
private void btnInitialise_Click(object sender, EventArgs e)
{
MessageBox.Show("test");
}
}

Foreach stops finding dynamically created controls after some amount of time. Resizing window makes it work again. Why? How can I fix it?

I'm a fairly new c# programmer currently in class and I've been doing this side project that (kind of) accurately represents bouncing balls. I also want it to be able to handle balls that are created on mouse click. I've done all of this and it works, for a few seconds. The original ball always works and always bounces/rolls. Dynamically created balls that are created on mouse click will hop and roll for a little but then they all freeze(not all at the same time) as the foreach statement I use stops recognizing that they are there. Weirdly enough when the window is resized using the bottom they start to work again. Am I missing something or is this a bug I can't fix? Here's the form code and the designer code. If you need anything else let me know.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace testBouncingBallMulti
{
public partial class Form1 : Form
{
const int INITY = 0;
const int INITX = 3;
const int GRAVITY = 1;
public Form1()
{
InitializeComponent();
this.ball0.Tag = new Point(INITX, INITY);
}
byte counter = 1;
private void createBall(object sender, MouseEventArgs e)
{
Label l = new Label();
l.BackColor = System.Drawing.Color.Black;
l.Location = new Point(MousePosition.X - this.Left, MousePosition.Y - this.Top);
this.Controls.Add(l);
l.Size = new System.Drawing.Size(15, 15);
l.Tag = new Point(INITX, INITY);
l.Name = "ball" + Convert.ToString(counter);
counter++;
}
private void physicsLoop1(object sender, EventArgs e)
{
foreach (var ball in this.Controls.OfType<Label>())
{
moveBall(ball);
bounce(ball);
Point? init = ball.Tag as Point?;
Point velocities = init.GetValueOrDefault(new Point(0, 0));
if (velocities.Y == 0 && ball.Location.Y >= ClientSize.Height - 15)
{
return;
}
velocities.Y = velocities.Y + GRAVITY;
ball.Tag = new Point(velocities.X, velocities.Y);
}
}
private void moveBall(Control ball)
{
Point? init = ball.Tag as Point?;
Point velocities = init.GetValueOrDefault(new Point(0,0));
ball.Location = new Point(ball.Location.X + velocities.X, ball.Location.Y + velocities.Y);
}
private void bounce(Control ball)
{
Point? init = ball.Tag as Point?;
Point velocities = init.GetValueOrDefault(new Point(0,0));
if (ball.Location.Y >= ClientSize.Height - 15)
{
if (velocities.Y == 0 && ball.Location.Y >= ClientSize.Height - 15)
{
}
else if (ball.Location.Y > ClientSize.Height - 15)
{
ball.Location = new Point(ball.Location.X, ClientSize.Height - 15);
velocities.Y = -(velocities.Y - 2);
}
}
if (ball.Location.X >= ClientSize.Width - 15)
{
velocities.X = -(velocities.X);
}
if (ball.Location.X <= 0)
{
ball.Location = new Point(0, ball.Location.Y);
velocities.X = -(velocities.X);
}
ball.Tag = new Point(velocities.X, velocities.Y);
}
private void physicsLoop2(object sender, EventArgs e)
{
}
private void physicsLoop3(object sender, EventArgs e)
{
}
}
}
and the designer code:
namespace testBouncingBallMulti
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.ball0 = new System.Windows.Forms.Label();
this.timer2 = new System.Windows.Forms.Timer(this.components);
this.timer3 = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Interval = 20;
this.timer1.Tick += new System.EventHandler(this.physicsLoop1);
//
// ball0
//
this.ball0.BackColor = System.Drawing.Color.Black;
this.ball0.Location = new System.Drawing.Point(12, 24);
this.ball0.Name = "ball0";
this.ball0.Size = new System.Drawing.Size(15, 15);
this.ball0.TabIndex = 0;
//
// timer2
//
this.timer2.Tick += new System.EventHandler(this.physicsLoop2);
//
// timer3
//
this.timer3.Tick += new System.EventHandler(this.physicsLoop3);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(504, 396);
this.Controls.Add(this.ball0);
this.Name = "Form1";
this.Text = "Form1";
this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.createBall);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Label ball0;
private System.Windows.Forms.Timer timer2;
private System.Windows.Forms.Timer timer3;
}
}
Replace return with continue in
if (velocities.Y == 0 && ball.Location.Y >= ClientSize.Height - 15)
{
return;
}
You need to give some breathing room in your label move procedure to repaint the form. Either use best practice and place your painting into a thread or at least process a windows message or two in your timer loop.

PictureBox remains drawn in old position when movng to newer position

I'm learning C# using the Head First C# book. In the first lab when building the Greyhound Racing game, I've encountered some behavior and I'm not understanding why my code is rendering the way it is. Upon the first click of the Race button, the horses race to the end of the track, yet they are rendered such that they each create a trail of previous images behind them until they reach the end of the track whereupon the preceding images finally vanish. Upon subsequent clicks of the Race button, the same thing occurs but it also fails to wipe the PictureBox for each dog from the finish line until the current race is complete.
Here's a short 19 second video that demonstrates what I mean: Example of trailing images
Why do the dogs 'trail' during the race, and why do they not vanish from the finish line upon being rendered at the start again until the completion of the next following race? I would think that when the dogs are repositioned in TakeStartingPosition() they would be moved, not redrawn. Same with the Run(), I would think each new position is a move and not a redraw yet it appears to be redrawing the image at each movement step and not wiping the old until the very end of the race. What am I doing incorrectly?
Greyhound.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RaceTrackSimulator
{
class Greyhound
{
public int StartingPosition;
public int RacetrackLength;
public PictureBox MyPictureBox = null;
public int Location = 0;
public Random Randomizer;
public bool Run()
{
// Move forward either 1, 2, 3 or 4 spaces at random
int moveSpaces = Randomizer.Next(1, 4);
// Update the position of my Picturebox on the form like this:
// MyPictureBox.Left = StartingPosition + Location;
MyPictureBox.Left = StartingPosition + Location;
// Return true if I won the race
if (Location >= RacetrackLength)
{
return true;
}
else
{
Location += moveSpaces;
return false;
}
}
public void TakeStartingPosition()
{
// Reset my location to 0 and my PictureBox to starting position
Location = 0;
MyPictureBox.Left = StartingPosition;
}
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RaceTrackSimulator
{
public partial class Form1 : Form
{
Greyhound[] Dogs;
public Form1()
{
InitializeComponent();
Random MyRandomizer = new Random();
// Initialize Dogs
Dogs = new Greyhound[4];
Dogs[0] = new Greyhound()
{
MyPictureBox = pictureBox2,
StartingPosition = racetrackPictureBox.Left,
RacetrackLength = racetrackPictureBox.Width - pictureBox2.Width,
Randomizer = MyRandomizer
};
Dogs[1] = new Greyhound()
{
MyPictureBox = pictureBox3,
StartingPosition = racetrackPictureBox.Left,
RacetrackLength = racetrackPictureBox.Width - pictureBox3.Width,
Randomizer = MyRandomizer
};
Dogs[2] = new Greyhound()
{
MyPictureBox = pictureBox4,
StartingPosition = racetrackPictureBox.Left,
RacetrackLength = racetrackPictureBox.Width - pictureBox4.Width,
Randomizer = MyRandomizer
};
Dogs[3] = new Greyhound()
{
MyPictureBox = pictureBox5,
StartingPosition = racetrackPictureBox.Left,
RacetrackLength = racetrackPictureBox.Width - pictureBox5.Width,
Randomizer = MyRandomizer
};
}
private void raceButton_Click(object sender, EventArgs e)
{
bool winner = false;
int winningDog = 0;
for (int eachDog = 0; eachDog < Dogs.Length; eachDog++)
{
Dogs[eachDog].TakeStartingPosition();
}
while (!winner)
{
for (int i = 0; i < 4; i++)
{
if (Dogs[i].Run())
{
winner = true;
winningDog = i+1;
}
System.Threading.Thread.Sleep(1);
}
}
MessageBox.Show("Winning Dog is #" + winningDog);
}
}
}
Form1.Designer.cs
namespace RaceTrackSimulator
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.pictureBox2 = new System.Windows.Forms.PictureBox();
this.racetrackPictureBox = new System.Windows.Forms.PictureBox();
this.pictureBox3 = new System.Windows.Forms.PictureBox();
this.pictureBox4 = new System.Windows.Forms.PictureBox();
this.pictureBox5 = new System.Windows.Forms.PictureBox();
this.raceButton = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.racetrackPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox5)).BeginInit();
this.SuspendLayout();
//
// pictureBox2
//
this.pictureBox2.Image = global::RaceTrackSimulator.Properties.Resources.dog;
this.pictureBox2.Location = new System.Drawing.Point(13, 22);
this.pictureBox2.Name = "pictureBox2";
this.pictureBox2.Size = new System.Drawing.Size(75, 20);
this.pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.pictureBox2.TabIndex = 2;
this.pictureBox2.TabStop = false;
//
// racetrackPictureBox
//
this.racetrackPictureBox.Image = global::RaceTrackSimulator.Properties.Resources.racetrack;
this.racetrackPictureBox.Location = new System.Drawing.Point(13, 12);
this.racetrackPictureBox.Name = "racetrackPictureBox";
this.racetrackPictureBox.Size = new System.Drawing.Size(600, 200);
this.racetrackPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.racetrackPictureBox.TabIndex = 0;
this.racetrackPictureBox.TabStop = false;
//
// pictureBox3
//
this.pictureBox3.Image = global::RaceTrackSimulator.Properties.Resources.dog;
this.pictureBox3.Location = new System.Drawing.Point(13, 74);
this.pictureBox3.Name = "pictureBox3";
this.pictureBox3.Size = new System.Drawing.Size(75, 20);
this.pictureBox3.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.pictureBox3.TabIndex = 3;
this.pictureBox3.TabStop = false;
//
// pictureBox4
//
this.pictureBox4.Image = global::RaceTrackSimulator.Properties.Resources.dog;
this.pictureBox4.Location = new System.Drawing.Point(13, 126);
this.pictureBox4.Name = "pictureBox4";
this.pictureBox4.Size = new System.Drawing.Size(75, 20);
this.pictureBox4.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.pictureBox4.TabIndex = 4;
this.pictureBox4.TabStop = false;
//
// pictureBox5
//
this.pictureBox5.Image = global::RaceTrackSimulator.Properties.Resources.dog;
this.pictureBox5.Location = new System.Drawing.Point(13, 178);
this.pictureBox5.Name = "pictureBox5";
this.pictureBox5.Size = new System.Drawing.Size(75, 20);
this.pictureBox5.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.pictureBox5.TabIndex = 5;
this.pictureBox5.TabStop = false;
//
// raceButton
//
this.raceButton.Location = new System.Drawing.Point(538, 377);
this.raceButton.Name = "raceButton";
this.raceButton.Size = new System.Drawing.Size(75, 23);
this.raceButton.TabIndex = 6;
this.raceButton.Text = "RACE!";
this.raceButton.UseVisualStyleBackColor = true;
this.raceButton.Click += new System.EventHandler(this.raceButton_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(625, 412);
this.Controls.Add(this.raceButton);
this.Controls.Add(this.pictureBox5);
this.Controls.Add(this.pictureBox4);
this.Controls.Add(this.pictureBox3);
this.Controls.Add(this.pictureBox2);
this.Controls.Add(this.racetrackPictureBox);
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.racetrackPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox5)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.PictureBox racetrackPictureBox;
private System.Windows.Forms.PictureBox pictureBox2;
private System.Windows.Forms.PictureBox pictureBox3;
private System.Windows.Forms.PictureBox pictureBox4;
private System.Windows.Forms.PictureBox pictureBox5;
private System.Windows.Forms.Button raceButton;
}
}
You are running a tight loop in the button click handler handler, which is monopolizing the main UI thread. When the dog moves forward, it is up to the form to repaint itself to "erase" where the dog previously was. Since the code is stuck in the loop, however, it cannot redraw itself. Similarly, when the race is restarted, the dogs do not disappear from the finish line for the same reason.
One possible "quick fix" is to call Application.DoEvents(); in the code to allow the form to update itself. That would look like this:
private void raceButton_Click(object sender, EventArgs e)
{
bool winner = false;
int winningDog = 0;
for (int eachDog = 0; eachDog < Dogs.Length; eachDog++)
{
Dogs[eachDog].TakeStartingPosition();
}
Application.DoEvents();
while (!winner)
{
for (int i = 0; i < 4; i++)
{
if (Dogs[i].Run())
{
winner = true;
winningDog = i+1;
}
Application.DoEvents();
System.Threading.Thread.Sleep(1);
}
}
MessageBox.Show("Winning Dog is #" + winningDog);
}
This, however, is simply a band-aid on top of the real problem: You shouldn't be monopolizing the main UI thread with a long running loop in the button click handler.
One possible solution is reset the dogs in the button click handler, then start a Timer. In the Tick() event of the Timer() you would call each dogs Run() method and check for a winner. When the race has been won, turn the Timer back off.

Displaying tooltip over a disabled control

I'm trying to display a tooltip when mouse hovers over a disabled control. Since a disabled control does not handle any events, I have to do that in the parent form. I chose to do this by handling the MouseMove event in the parent form. Here's the code that does the job:
void Form1_MouseMove(object sender, MouseEventArgs e)
{
m_toolTips.SetToolTip(this, "testing tooltip on " + DateTime.Now.ToString());
string tipText = this.m_toolTips.GetToolTip(this);
if ((tipText != null) && (tipText.Length > 0))
{
Point clientLoc = this.PointToClient(Cursor.Position);
Control child = this.GetChildAtPoint(clientLoc);
if (child != null && child.Enabled == false)
{
m_toolTips.ToolTipTitle = "MouseHover On Disabled Control";
m_toolTips.Show(tipText, this, 10000);
}
else
{
m_toolTips.ToolTipTitle = "MouseHover Triggerd";
m_toolTips.Show(tipText, this, 3000);
}
}
}
The code does handles the tooltip display for the disabled control. The problem is that when mouse hovers over a disabled control, the tooltip keeps closing and redisplay again. From the display time I added in the tooltip, when mouse is above the parent form, the MouseMove event gets called roughly every 3 seconds, so the tooltip gets updated every 3 seconds. But when mouse is over a disabled control, the tooltip refreshes every 1 second. Also, when tooltip refreshes above form, only the text gets updated with a brief flash. But when tooltip refreshes above a disabled control, the tooltip windows closes as if mouse is moving into a enabled control and the tooltip is supposed to be closed. but then the tooltip reappears right away.
Can someone tell me why is this? Thanks.
you can show the tooltip only once when mouse hits the disbled control and then hide it when mouse leaves it. Pls, take a look at the code below, it should be showing a tooltip message for all the disabled controls on the form
private ToolTip _toolTip = new ToolTip();
private Control _currentToolTipControl = null;
public Form1()
{
InitializeComponent();
_toolTip.SetToolTip(this.button1, "My button1");
_toolTip.SetToolTip(this.button2, "My button2");
_toolTip.SetToolTip(this.textBox1, "My text box");
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Control control = GetChildAtPoint(e.Location);
if (control != null)
{
if (!control.Enabled && _currentToolTipControl == null)
{
string toolTipString = _toolTip.GetToolTip(control);
// trigger the tooltip with no delay and some basic positioning just to give you an idea
_toolTip.Show(toolTipString, control, control.Width/2, control.Height/2);
_currentToolTipControl = control;
}
}
else
{
if (_currentToolTipControl != null) _toolTip.Hide(_currentToolTipControl);
_currentToolTipControl = null;
}
}
hope this helps, regards
The answer turned out to be a bit simpler, but needed to be applied at all times.
void OrderSummaryDetails_MouseMove(object sender, MouseEventArgs e)
{
Control control = GetChildAtPoint(e.Location);
if (control != null)
{
string toolTipString = mFormTips.GetToolTip(control);
this.mFormTips.ShowAlways = true;
// trigger the tooltip with no delay and some basic positioning just to give you an idea
mFormTips.Show(toolTipString, control, control.Width / 2, control.Height / 2);
}
}
In case of TextBox control, making it as readonly solved the issue.
I tried many but ended up using this simple trick which I think it is more effective.
Create a subclass(CustomControl with just base control in it) which extends UserControl
then instead of setting "Enabled" property to false create a Method which disables just basecontrol in it instead of whole CustomControl.
Set the tool tip on CustomControl still will be able to fire eventhandlers setting the basecontrol disabled. This works wherever CustomControl is in use rather than coding on every form you use with.
Here is the hint.. :)
public partial class MyTextBox : UserControl
{
...
...
...
public void DisableMyTextBox()
{
this.txt.Enabled = false; //txt is the name of Winform-Textbox from my designer
this.Enabled = true;
}
public void EnableMyTextBox()
{
this.txt.Enabled = true;
this.Enabled = true;
}
//set the tooltip from properties tab in designer or wherever
}
Since no one ever pointed this out, this works for any control that exposes ToolTipService:
ToolTipService.ShowOnDisabled="True"
As in this example:
<Button Content="OK"
ToolTipService.ShowOnDisabled="True" />
/*
Inspired by the suggestions above in this post, i wrapped it up as an extended ToolTip control specially works for disabled control.
// Reference example
var td = new ToolTipOnDisabledControl();
this.checkEdit3.Enabled = false;
td.SetTooltip(this.checkEdit3, "tooltip for disabled 3333333333333");
*/
using System;
using System.Windows.Forms;
namespace TestApp1
{
public class ToolTipOnDisabledControl
{
#region Fields and Properties
private Control enabledParentControl;
private bool isShown;
public Control TargetControl { get; private set; }
public string TooltipText { get; private set; }
public ToolTip ToolTip { get; }
#endregion
#region Public Methods
public ToolTipOnDisabledControl()
{
this.ToolTip = new ToolTip();
}
public void SetToolTip(Control targetControl, string tooltipText = null)
{
this.TargetControl = targetControl;
if (string.IsNullOrEmpty(tooltipText))
{
this.TooltipText = this.ToolTip.GetToolTip(targetControl);
}
else
{
this.TooltipText = tooltipText;
}
if (targetControl.Enabled)
{
this.enabledParentControl = null;
this.isShown = false;
this.ToolTip.SetToolTip(this.TargetControl, this.TooltipText);
return;
}
this.enabledParentControl = targetControl.Parent;
while (!this.enabledParentControl.Enabled && this.enabledParentControl.Parent != null)
{
this.enabledParentControl = this.enabledParentControl.Parent;
}
if (!this.enabledParentControl.Enabled)
{
throw new Exception("Failed to set tool tip because failed to find an enabled parent control.");
}
this.enabledParentControl.MouseMove += this.EnabledParentControl_MouseMove;
this.TargetControl.EnabledChanged += this.TargetControl_EnabledChanged;
}
public void Reset()
{
if (this.TargetControl != null)
{
this.ToolTip.Hide(this.TargetControl);
this.TargetControl.EnabledChanged -= this.TargetControl_EnabledChanged;
this.TargetControl = null;
}
if (this.enabledParentControl != null)
{
this.enabledParentControl.MouseMove -= this.EnabledParentControl_MouseMove;
this.enabledParentControl = null;
}
this.isShown = false;
}
#endregion
#region Private Methods
private void EnabledParentControl_MouseMove(object sender, MouseEventArgs e)
{
if (e.Location.X >= this.TargetControl.Left &&
e.Location.X <= this.TargetControl.Right &&
e.Location.Y >= this.TargetControl.Top &&
e.Location.Y <= this.TargetControl.Bottom)
{
if (!this.isShown)
{
this.ToolTip.Show(this.TooltipText, this.TargetControl, this.TargetControl.Width / 2, this.TargetControl.Height / 2, this.ToolTip.AutoPopDelay);
this.isShown = true;
}
}
else
{
this.ToolTip.Hide(this.TargetControl);
this.isShown = false;
}
}
private void TargetControl_EnabledChanged(object sender, EventArgs e)
{
if (TargetControl.Enabled)
{
TargetControl.EnabledChanged -= TargetControl_EnabledChanged;
enabledParentControl.MouseMove -= EnabledParentControl_MouseMove;
}
}
#endregion
}
}
Here is how I solved this problem
I have an application that generates code automatically for a PIC32MX.
The application has 3 Tab Pages text = PWM, ADC and UART.
On each Tab Page I have one Check Box text = RPA0
The intention is, when a peripheral uses RPA0, the other peripheral is prevented
from using that pin, by disabling it on the other pages, and a tooltip text must pop up
on the disabled check boxs saying (example "Used by PWM")
what peripheral is using that pin.
The problem is that the tooltip text won't pop up on a disabled check box.
To solve the problem, I just removed the text of the check boxes and inserted labels with the text the check box should have.
When a check box is checked, the other check boxes are disabled and the label next to it takes a tool tip text.
As the label is enabled, the tooltip text pops up, even on a disabled check box.
Double the work, half the complexity.
Here is the code and the designer for C# 2010
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void cb_ADC_RPA0_CheckedChanged(object sender, EventArgs e)
{
/* Disable pin on other peripherals */
cb_UART_RPA0.Enabled = !((CheckBox)sender).Checked;
cb_PWM_RPA0.Enabled = !((CheckBox)sender).Checked;
SetTootTip((CheckBox)sender, lbl_PWM_RPA0, lbl_UART_RPA0, "ADC");
}
private void cb_PWM_RPA0_CheckedChanged(object sender, EventArgs e)
{
/* Disable pin on other peripherals */
cb_UART_RPA0.Enabled = !((CheckBox)sender).Checked;
cb_ADC_RPA0.Enabled = !((CheckBox)sender).Checked;
SetTootTip((CheckBox)sender, lbl_ADC_RPA0, lbl_UART_RPA0, "PWM");
}
private void cb_UART_RPA0_CheckedChanged(object sender, EventArgs e)
{
/* Disable pin on other peripherals */
cb_ADC_RPA0.Enabled = !((CheckBox)sender).Checked;
cb_PWM_RPA0.Enabled = !((CheckBox)sender).Checked;
SetTootTip((CheckBox)sender, lbl_ADC_RPA0, lbl_PWM_RPA0, "UART");
}
void SetTootTip(CheckBox sender, Label lbl1, Label lbl2, string text)
{
/* Update tooltip on the other labels */
if (sender.Checked)
{
toolTip1.SetToolTip(lbl1, "Used by " + text);
toolTip1.SetToolTip(lbl2, "Used by " + text);
}
else
{
toolTip1.SetToolTip(lbl1, "");
toolTip1.SetToolTip(lbl2, "");
}
}
}
}
namespace WindowsFormsApplication1
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tpPWM = new System.Windows.Forms.TabPage();
this.tpUART = new System.Windows.Forms.TabPage();
this.tpADC = new System.Windows.Forms.TabPage();
this.cb_PWM_RPA0 = new System.Windows.Forms.CheckBox();
this.cb_ADC_RPA0 = new System.Windows.Forms.CheckBox();
this.lbl_PWM_RPA0 = new System.Windows.Forms.Label();
this.lbl_ADC_RPA0 = new System.Windows.Forms.Label();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.lbl_UART_RPA0 = new System.Windows.Forms.Label();
this.cb_UART_RPA0 = new System.Windows.Forms.CheckBox();
this.tabControl1.SuspendLayout();
this.tpPWM.SuspendLayout();
this.tpUART.SuspendLayout();
this.tpADC.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tpPWM);
this.tabControl1.Controls.Add(this.tpUART);
this.tabControl1.Controls.Add(this.tpADC);
this.tabControl1.Location = new System.Drawing.Point(12, 12);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(629, 296);
this.tabControl1.TabIndex = 0;
//
// tpPWM
//
this.tpPWM.Controls.Add(this.lbl_PWM_RPA0);
this.tpPWM.Controls.Add(this.cb_PWM_RPA0);
this.tpPWM.Location = new System.Drawing.Point(4, 22);
this.tpPWM.Name = "tpPWM";
this.tpPWM.Padding = new System.Windows.Forms.Padding(3);
this.tpPWM.Size = new System.Drawing.Size(621, 270);
this.tpPWM.TabIndex = 0;
this.tpPWM.Text = "PWM";
this.tpPWM.UseVisualStyleBackColor = true;
//
// tpUART
//
this.tpUART.Controls.Add(this.cb_UART_RPA0);
this.tpUART.Controls.Add(this.lbl_UART_RPA0);
this.tpUART.Location = new System.Drawing.Point(4, 22);
this.tpUART.Name = "tpUART";
this.tpUART.Padding = new System.Windows.Forms.Padding(3);
this.tpUART.Size = new System.Drawing.Size(621, 270);
this.tpUART.TabIndex = 1;
this.tpUART.Text = "UART";
this.tpUART.UseVisualStyleBackColor = true;
//
// tpADC
//
this.tpADC.Controls.Add(this.lbl_ADC_RPA0);
this.tpADC.Controls.Add(this.cb_ADC_RPA0);
this.tpADC.Location = new System.Drawing.Point(4, 22);
this.tpADC.Name = "tpADC";
this.tpADC.Padding = new System.Windows.Forms.Padding(3);
this.tpADC.Size = new System.Drawing.Size(621, 270);
this.tpADC.TabIndex = 2;
this.tpADC.Text = "ADC";
this.tpADC.UseVisualStyleBackColor = true;
//
// cb_PWM_RPA0
//
this.cb_PWM_RPA0.AutoSize = true;
this.cb_PWM_RPA0.Location = new System.Drawing.Point(17, 65);
this.cb_PWM_RPA0.Name = "cb_PWM_RPA0";
this.cb_PWM_RPA0.Size = new System.Drawing.Size(15, 14);
this.cb_PWM_RPA0.TabIndex = 0;
this.cb_PWM_RPA0.UseVisualStyleBackColor = true;
this.cb_PWM_RPA0.CheckedChanged += new System.EventHandler(this.cb_PWM_RPA0_CheckedChanged);
//
// cb_ADC_RPA0
//
this.cb_ADC_RPA0.AutoSize = true;
this.cb_ADC_RPA0.Location = new System.Drawing.Point(17, 65);
this.cb_ADC_RPA0.Name = "cb_ADC_RPA0";
this.cb_ADC_RPA0.Size = new System.Drawing.Size(15, 14);
this.cb_ADC_RPA0.TabIndex = 1;
this.cb_ADC_RPA0.UseVisualStyleBackColor = true;
this.cb_ADC_RPA0.CheckedChanged += new System.EventHandler(this.cb_ADC_RPA0_CheckedChanged);
//
// lbl_PWM_RPA0
//
this.lbl_PWM_RPA0.AutoSize = true;
this.lbl_PWM_RPA0.Location = new System.Drawing.Point(38, 65);
this.lbl_PWM_RPA0.Name = "lbl_PWM_RPA0";
this.lbl_PWM_RPA0.Size = new System.Drawing.Size(35, 13);
this.lbl_PWM_RPA0.TabIndex = 1;
this.lbl_PWM_RPA0.Text = "RPA0";
//
// lbl_ADC_RPA0
//
this.lbl_ADC_RPA0.AutoSize = true;
this.lbl_ADC_RPA0.Location = new System.Drawing.Point(38, 66);
this.lbl_ADC_RPA0.Name = "lbl_ADC_RPA0";
this.lbl_ADC_RPA0.Size = new System.Drawing.Size(35, 13);
this.lbl_ADC_RPA0.TabIndex = 2;
this.lbl_ADC_RPA0.Text = "RPA0";
//
// lbl_UART_RPA0
//
this.lbl_UART_RPA0.AutoSize = true;
this.lbl_UART_RPA0.Location = new System.Drawing.Point(37, 65);
this.lbl_UART_RPA0.Name = "lbl_UART_RPA0";
this.lbl_UART_RPA0.Size = new System.Drawing.Size(35, 13);
this.lbl_UART_RPA0.TabIndex = 4;
this.lbl_UART_RPA0.Text = "RPA0";
//
// cb_UART_RPA0
//
this.cb_UART_RPA0.AutoSize = true;
this.cb_UART_RPA0.Location = new System.Drawing.Point(16, 65);
this.cb_UART_RPA0.Name = "cb_UART_RPA0";
this.cb_UART_RPA0.Size = new System.Drawing.Size(15, 14);
this.cb_UART_RPA0.TabIndex = 5;
this.cb_UART_RPA0.UseVisualStyleBackColor = true;
this.cb_UART_RPA0.CheckedChanged += new System.EventHandler(this.cb_UART_RPA0_CheckedChanged);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(758, 429);
this.Controls.Add(this.tabControl1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.tabControl1.ResumeLayout(false);
this.tpPWM.ResumeLayout(false);
this.tpPWM.PerformLayout();
this.tpUART.ResumeLayout(false);
this.tpUART.PerformLayout();
this.tpADC.ResumeLayout(false);
this.tpADC.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tpPWM;
private System.Windows.Forms.Label lbl_PWM_RPA0;
private System.Windows.Forms.CheckBox cb_PWM_RPA0;
private System.Windows.Forms.TabPage tpUART;
private System.Windows.Forms.TabPage tpADC;
private System.Windows.Forms.Label lbl_ADC_RPA0;
private System.Windows.Forms.CheckBox cb_ADC_RPA0;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.CheckBox cb_UART_RPA0;
private System.Windows.Forms.Label lbl_UART_RPA0;
}
}
I created a new UserControl which only contains a button.
public partial class TooltipButton : UserControl
{
public TooltipButton()
{
InitializeComponent();
}
public new bool Enabled
{
get { return button.Enabled; }
set { button.Enabled = value; }
}
[Category("Appearance")]
[Description("The text displayed by the button.")]
[EditorBrowsable(EditorBrowsableState.Always)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Bindable(true)]
public override string Text
{
get { return button.Text; }
set { button.Text = value; }
}
[Category("Action")]
[Description("Occurs when the button is clicked.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public new event EventHandler Click;
private void button_Click(object sender, EventArgs e)
{
// Bubble event up to parent
Click?.Invoke(this, e);
}
}
I found Serge_Yubenko's code worked on disabled buttons , but in order to stop the flashing make sure the tooltip pops up away from the button - just don't position it half way down the control but do this:
mFormTips.Show(toolTipString, control, control.Width / 2, control.Height);
instead of
mFormTips.Show(toolTipString, control, control.Width / 2, control.Height / 2);
This seems to follow the usual tooltip placement too...
So, I came across this post in my efforts to do the same thing, being the top result on Google. I had already considered the mouse move event and while the answers here did help, they didn't provide me with exactly what I wanted - that being a perfect recreation of the original show tooltip event.
The problem I discovered was this: For whatever reason in the API, ToolTip.Show turns the Mouse Move Event into effectively a Mouse Hover Event. Which is why the tooltip keeps flashing.
The workaround as suggested was to keep the tooltip on always show, or to display the tooltip away from the control, but that wouldn't be a faithful recreation, from the show to the timed fade. The answer would suggest that a block to prevent further execution of the code is needed - the reality was 2 blocks in the event code (One of which has no earthly reason existing and yet without it a timed event fires twice ***), a double delclaration of the control location, one inside the event, one class wide, and another class wide to check if the mouse is over a control, a class wide timer, and a Mouse Leave event to clean up due to too fast mouse movement away from the panel housing the control.
As you will see there are two events on the timer, both functions for them are in the event code as they need to reference variables get/set in the code. They can be moved out, but would then need class wide declarations on the variables, and they cause no harm where they are. FYI: "ToolTips" in the code is referencing the ToolTip control I have on the form.
*** Just to expand. If you look at the code you'll see that IsTipReset could be replaced with IsShown - after all they end up at the same value as each other. The reason for IsTipRest is this: If IsShown is used then while moving the mouse inside the control while the tootip is showing will cause a slight hiccup when the tooltip fades and very very very briefly another tooltip will popup. Using IsTipReset stops that. I have no idea why and maybe someone will spot it because I sure can't! Lol.
This is my first post here, and I realise it is an old thread, but I just wanted to share the fruits of my labour. As I said, my goal was a faithful recreation of tooltip and I think I achieved it. Enjoy!
using Timer = System.Windows.Forms.Timer;
private readonly Timer MouseTimer = new();
private Control? Ctrl;
private bool IsControl = false;
private void TopMenuMouseMove (object sender, MouseEventArgs e) {
Panel Pnl = (Panel)sender;
Control Area = Pnl.GetChildAtPoint (e.Location);
bool IsShown = false;
bool IsTipReset = false;
if (Area != null && Area.Enabled == false && Area.Visible == true) {
Ctrl = Pnl.GetChildAtPoint (e.Location);
Point Position = e.Location;
if (IsControl) { IsShown = true; } else if (!IsControl) { IsControl = true; IsShown = false; }
if (!IsShown) {
MouseTimer.Interval = ToolTips.InitialDelay;
MouseTimer.Tick += new EventHandler (TimerToolTipShow!);
MouseTimer.Start ();
}
void TimerToolTipShow (object sender, EventArgs e) {
if (!IsTipReset) {
MouseTimer.Dispose ();
string Txt = ToolTips.GetToolTip (Ctrl) + " (Disabled)";
Position.Offset (-Ctrl.Left, 16);
ToolTips.Show (Txt, Ctrl, Position);
MouseTimer.Interval = ToolTips.AutoPopDelay;
MouseTimer.Tick += new EventHandler (TimerToolTipReset!);
MouseTimer.Start ();
IsShown = true;
IsTipReset = true;
}
}
void TimerToolTipReset (object sender, EventArgs e) {
if (IsShown) {
MouseTimer.Dispose ();
IsShown = false;
ToolTips.Hide (Ctrl);
}
}
}
else if (Area == null) {
if (Ctrl != null) {
MouseTimer.Dispose ();
IsShown = false;
IsControl = false;
ToolTips.Hide (Ctrl);
Ctrl = null;
}
}
}
private void TopMenuMouseLeave (object sender, EventArgs e) {
if (Ctrl != null) {
MouseTimer.Dispose ();
IsControl = false;
ToolTips.Hide (Ctrl);
Ctrl = null;
}
}

Categories