How to display different tool tips on different regions of a control - c#

I'm trying to display different tool tip text depending on what part of a control the user has their mouse over. As a simplified example, this will display "Left" if the user hovers over the left half, and "Right" if the user hovers over the right half:
public partial class TestForm : Form
{
private ToolTip toolTip = new ToolTip();
public TestForm() {
InitializeComponent();
}
private void TestForm_MouseMove(object sender, MouseEventArgs e) {
if (e.X < Width / 2) {
toolTip.SetToolTip(this, "Left");
} else {
toolTip.SetToolTip(this, "Right");
}
}
}
The problem is that this causes the tool tip to display immediately and also follow the cursor around. Is there any way to make it behave like the different regions are separate controls?

You can add timer that will trigger tooltip on delay when mouse is not moved.
Add timer with some interval, say, 1000 ms (1 sec).
public TestForm()
{
InitializeComponent();
timer1.Enabled = true;
}
While mouse moves it should run
private void TestForm_MouseMove(object sender, MouseEventArgs e) {
//if (e.X < Width / 2) {
// toolTip.SetToolTip(this, "Left");
//} else {
// toolTip.SetToolTip(this, "Right");
//}
timer1.Stop();
timer1.Start();
}
And if stopped, trigger a tooltip
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
if (PointToClient(Cursor.Position).X < this.Width / 2)
{
toolTip.SetToolTip(this, "Left");
}
else
{
toolTip.SetToolTip(this, "Right");
}
}

Related

Zooming in ComboBox not working as expected

I have an application that scans an image to display on the application. After the image is scanned, I have the option to zoom the image. There's a combo box on the application that display the zoom percentage as well.
I can zoom fine using my mouse wheel and the combo box % changes accordingly which is fine. The problem happens if I manually select the combo box and select a zoom percentage, say 50%, then there's no changes at all.
Code:
private void ImageBox_ZoomLevelsChanged(object sender, EventArgs e)
{
this.FillZoomLevels();
}
private void ZoomComboBox_Click(object sender, EventArgs e)
{
}
private void FillZoomLevels()
{
ZoomComboBox.Items.Clear();
foreach (int zoom in ImageBox.ZoomLevels)
ZoomComboBox.Items.Add(string.Format("{0}%", zoom));
}
Am I doing anything wrong? Appreciate any help.
When there is more than one control on the Panel, and the ScrollBars are shown, the MouseWheel event of the Panel would be raised, but if there's only a PictureBox on the Panel whose Image large enough to make the panel's ScrollBars visible, then the MouseWheel event of the Panel won't be raised, instead, the Form's MouseWheel event fires.
In the following sample, I just add a PictureBox onto the Panel without any other controls added, set a large Image to the PictureBox which make the Panel's ScrollBars invisible, since there's only one control on the panel, the MouseWheel event of the Panel won't be raised, but the Form's MouseWheel event be raised, we handle this event instead, handle the Form's KeyDown and KeyUp events as well.
Furthermore, set the SizeMode of the PictureBox to StretchImage instead of AutoSize, thus when we change the size of the PictureBox, the Image in the PictureBox will resize to fit the PictureBox.
The PictureBox.Scale() method won't help you in this screnario, change the size of the PictureBox instead.
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
}
bool ctrlKeyDown;
bool shiftKeyDown;
private void Form4_Load(object sender, EventArgs e)
{
this.ctrlKeyDown = false;
this.shiftKeyDown = false;
//If there's only PictureBox control on the panel, the MouseWheel event of the form raised
//instead of the MouseWheel event of the Panel.
this.MouseWheel += new MouseEventHandler(Form4_MouseWheel);
this.KeyDown += new KeyEventHandler(Form4_KeyDown);
this.KeyUp += new KeyEventHandler(Form4_KeyUp);
//this is important for zooming the image
this.pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
}
void Form4_KeyUp(object sender, KeyEventArgs e)
{
this.ctrlKeyDown = e.Control;
this.shiftKeyDown = e.Shift;
}
void Form4_KeyDown(object sender, KeyEventArgs e)
{
this.ctrlKeyDown = e.Control;
this.shiftKeyDown = e.Shift;
}
void Form4_MouseWheel(object sender, MouseEventArgs e)
{
bool IsGoUp = e.Delta > 0 ? true : false;
if (this.ctrlKeyDown)
{
if (IsGoUp && this.panel1.HorizontalScroll.Value > 5)
{
this.panel1.HorizontalScroll.Value -= 5;
}
if (!IsGoUp && this.panel1.HorizontalScroll.Value < this.panel1.HorizontalScroll.Maximum - 5)
{
this.panel1.HorizontalScroll.Value += 5;
}
}
else if (this.shiftKeyDown)
{
int hStep = (int)(this.pictureBox2.Image.Width * 0.02);
int vStep = (int)(this.pictureBox2.Image.Height * 0.02);
if (IsGoUp)
{
this.pictureBox2.Width += hStep;
this.pictureBox2.Height += vStep;
}
else
{
this.pictureBox2.Width -= hStep;
this.pictureBox2.Height -= vStep;
}
}
else
{
if (IsGoUp && this.panel1.VerticalScroll.Value > 5)
{
this.panel1.VerticalScroll.Value -= 5;
}
if (!IsGoUp && this.panel1.VerticalScroll.Value < this.panel1.VerticalScroll.Maximum - 5)
{
this.panel1.VerticalScroll.Value += 5;
}
}
}
}

WPF DragMove() causes issues

I'm trying to figure out if there's an elegant solution to the problem I've been faced with.
So basically, I designed a borderless loading splash screen which is completely movable via dragging. I find that this happens if the splash screen gets hidden via Hide(), then displays a window via ShowDialog() with the owner set to the splash screen. Things get extremely buggy, but only if you're in mid-drag (with left mouse button down). You become unable to click or move anything, even Visual Studio becomes unresponsive unless you explicitly alt-tab out of the application.
Considering I know when I'm going to spawn the window, I was thinking maybe there'd be a way to cancel the DragMove operation, but I'm having no luck. What I've figured out is that DragMove is synchronous, so I'd guess it'd have to be cancelled from a different thread or in an event callback.
Edit:
public partial class Window_Movable : Window
{
public Window_Movable()
{
InitializeComponent();
}
public Boolean CanMove { get; set; } = true;
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (CanMove == false)
return;
Task.Factory.StartNew(() => {
System.Threading.Thread.Sleep(1000);
Dispatcher.Invoke(() => {
Hide();
new Window_Movable() {
Title = "MOVABLE 2",
CanMove = false,
Owner = this
}.ShowDialog();
});
});
DragMove();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("DING");
}
}
I had the same problem and found out that DragMove() method is a problem.
https://groups.google.com/forum/#!topic/wpf-disciples/7OcuXrf2whc
To solve I decided to refuse from using it and implement moving logic.
I've combined some solutions from
https://www.codeproject.com/Questions/284995/DragMove-problem-help-pls
and
C# WPF Move the window
private bool _inDrag;
private Point _anchorPoint;
private bool _iscaptured;
private void AppWindowWindowOnMouseMove(object sender, MouseEventArgs e)
{
if (!_inDrag)
return;
if (!_iscaptured)
{
CaptureMouse();
_iscaptured = true;
}
var mousePosition = e.GetPosition(this);
var mousePositionAbs = new Point
{
X = Convert.ToInt16(_appWindowWindow.Left) + mousePosition.X,
Y = Convert.ToInt16(_appWindowWindow.Top) + mousePosition.Y
};
_appWindowWindow.Left = _appWindowWindow.Left + (mousePositionAbs.X - _anchorPoint.X);
_appWindowWindow.Top = _appWindowWindow.Top + (mousePositionAbs.Y - _anchorPoint.Y);
_anchorPoint = mousePositionAbs;
}
private void AppWindowWindowOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_inDrag)
{
_inDrag = false;
_iscaptured = false;
ReleaseMouseCapture();
}
}
private void AppWindowWindowOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_anchorPoint = e.GetPosition(this);
_anchorPoint.Y = Convert.ToInt16(_appWindowWindow.Top) + _anchorPoint.Y;
_anchorPoint.X = Convert.ToInt16(_appWindowWindow.Left) + _anchorPoint.X;
_inDrag = true;
}
I've spend all yesterday evening and find a more hacky but more functional solution. Which support Maximized state and not require manual coordinate calculation.
private bool _mRestoreForDragMove;
private void OnAppWindowWindowOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
if (_appWindowWindow.ResizeMode != ResizeMode.CanResize &&
_appWindowWindow.ResizeMode != ResizeMode.CanResizeWithGrip)
{
return;
}
_appWindowWindow.WindowState = _appWindowWindow.WindowState == WindowState.Maximized
? WindowState.Normal
: WindowState.Maximized;
}
else
{
_mRestoreForDragMove = _appWindowWindow.WindowState == WindowState.Maximized;
SafeDragMoveCall(e);
}
}
private void SafeDragMoveCall(MouseEventArgs e)
{
Task.Delay(100).ContinueWith(_ =>
{
Dispatcher.BeginInvoke((Action)
delegate
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
_appWindowWindow.DragMove();
RaiseEvent(new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent
});
}
});
});
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (_mRestoreForDragMove)
{
_mRestoreForDragMove = false;
var point = PointToScreen(e.MouseDevice.GetPosition(this));
_appWindowWindow.Left = point.X - (_appWindowWindow.RestoreBounds.Width * 0.5);
_appWindowWindow.Top = point.Y;
_appWindowWindow.WindowState = WindowState.Normal;
_appWindowWindow.DragMove();
SafeDragMoveCall(e);
}
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_mRestoreForDragMove = false;
}
The thing is to send LeftMouseButtonUp event after a short delay to avoid blocking from DragMove()
Sources for last solve:
DragMove() and Maximize
C# WPF - DragMove and click
And one more completely different solution. To make a window movable you can use CaptionHeight of WindowChrome.
i.e.
var windowChrome =
WindowChrome.GetWindowChrome(appWindow.Window);
windowChrome.CaptionHeight = MainWindowToolbar.Height;
WindowChrome.SetWindowChrome(appWindow.Window, windowChrome);
but you should also set attached property WindowChrome.IsHitTestVisibleInChrome="True" for all controls in the window.

How to make sidebar?

I am using C# programming language.
I have created a new borderless Windows Form in Visual Studio 2012. I have two panels. First panel (panel1) is docked at top and second panel (panel2) is docked at left and its Visible status is false. I also added a button (buttonMenu) in panel1 and docked it on left. What I am trying to achieve is that on buttonMenu click, panel2 slides from left to its original location (left dock) and when buttonMenu clicked again slides away.
I have tried to set the following code:
private void buttonMenu_Click(object sender, EventArgs e)
{
panel2.Visible = !panel2.Visible;
}
And it works but i am trying to make it slide with an animation.
The easier way to do it would be to create a timer and in its Tick event change the panel width like so
private Timer timer = new Timer();
bool closing = false;
int desiredWidth = 300
private void ConfigureTimer()
{
timer.Tick += timer_Tick;
timer.Interval = 16;
}
private void timer_Tick(object sender, EventArgs e){
{
if(closing)
{
panel2.Width-=15;
if(panel2.Width < 0)
{
panel2.Width = 0;
timer.Stop();
}
}else{
panel2.Width+=15;
if(panel2.Width >= desiredWidth)
{
panel2.Width = desiredWidth;
timer.Stop();
}
}
}
private void SwitchPanelState()
{
closing = !closing;
timer.Start();
}
But if you want to make you life easy and more object oriented, you should create an Control with the specified behavior and just add it in your form

How to gradually resize a picturebox?

I have been trying to code this for a while and after a couple of weeks of searching the web for an answer I decided to ask
All I want to do is gradually resize pictureBox1 to a set limit from a variable starter value when the mouse hovers over it, the furthest I got was using a forloop which made it instantly change size. I would like it to also change height and width at the same time (pictureBox1 will be a square and i just want it to be a bigger square with a bit of smooth movement)
Also I need it to gradually change back to the original size once the mouse moves off of pictureBox1.
I have been toying about with a couple of solutions found on websites but none seem to work properly, also you might need to know that I have two forms involved in this code; Form1 and frmMenu and because of a mass amount of errors I commented out the bottom two methods.
I do not get any errors but it just doesn't work.
public partial class frmMenu : Form
{
//private int size = 100;
public Timer timer1;
public frmMenu()
{
InitializeComponent();
pictureBox1.MouseEnter += new EventHandler(pictureBox1_MouseEnter);
//pictureBox1.MouseLeave += new EventHandler(pictureBox1_MouseLeave);
}
private string frmMenu_Load
{
set
{
timer1.Interval = 1;
}
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
//for (int i = 140; i > size; size++)
//{
//}
{
timer1.Interval = 1;
}
timer1.Enabled = true;
if (pictureBox1.Height <= 140)
{
pictureBox1.Size = new Size(pictureBox1.Size.Width, pictureBox1.Size.Height + 1);
}
else
{
timer1.Enabled = false;
}
}
// private void pictureBox1_MouseLeave(object sender, EventArgs e)
// {
// if (size > 100)
// for (int i = size; i > 100; i--)
// {
// size = i;
// }
// pictureBox1.Height = pictureBox1.Width = size;
// }
// private void pictureBox1_Click(object sender, EventArgs e)
// {
// var Form1 = new Form1();
// Form1.Show();
// var Menu = new frmMenu();
// Menu.Close();
// }
}
This is my first time asking so sorry if I haven't given enough information ^.^
Try this code:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public partial class Form1 : Form
{
bool mouseHover;
int width;
int height;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 25;
timer1.Tick += timer1_Tick;
width = pictureBox1.Width;
height = pictureBox1.Height;
timer1.Start();
}
void timer1_Tick(object sender, EventArgs e)
{
if (mouseHover)
{
pictureBox1.Width += (pictureBox1.Width < 100) ? 5 : 0;
pictureBox1.Height += (pictureBox1.Height < 100) ? 5 : 0;
}
else
{
pictureBox1.Width += (pictureBox1.Width > width) ? -5 : 0;
pictureBox1.Height += (pictureBox1.Height > height) ? -5 : 0;
}
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
mouseHover = true;
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
mouseHover = false;
}
}
}
You can adjust the interval to how you like it, but increasing at 5 pixels horizontally/vertically every 25 milliseconds is pretty smooth. You need to set the initial height and width so you can go back to that size after the mouse leaves the picture box. I use the null coalescing operator so you don't have to stop the timer. As long as the condition on the left side of the ? is true, it will evaluate to the value on the left side of the :. When the condition is false, it evaluates to the right side of the :.

How to prevent tooltip from flickering in custom control?

I have made a custom control and when a condition is met, I want to show a tooltip:
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var plannedItem = GetPlannedItemByPosition(e.Location);
if (plannedItem != null)
_tooltip.SetToolTip(this, plannedItem.Description);
else
_tooltip.RemoveAll();
}
This code works fine, excepts for the face that the tooltip flickers.
This custom control, paints all the information in the OnPaint event, maybe this has something to do with it? And if it does, how can I prevent the tooltip from flickering?
Remember last mouse position and set the tooltip only when the mouse position changes.
public partial class Form1 : Form
{
private int lastX;
private int lastY;
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (e.X != this.lastX || e.Y != this.lastY)
{
toolTip1.SetToolTip(button1, "test");
this.lastX = e.X;
this.lastY = e.Y;
}
}
This will happen when you display the tooltip at the mouse cursor position. As soon as the tip window shows up, Windows notices that the mouse is located in that window and posts a MouseMove message. Which makes the tooltip disappear. Which makes Windows send a MouseMove message to your control, running your OnMouseMove() method. Which makes the tooltip appear again. Etcetera, you'll see the tooltip rapidly flickering.
Solve this by any of the following methods:
show the tooltip well away from the mouse position so it won't overlap the mouse cursor
only update/show the tooltip when it needs to be changed
set the control's Capture property to true so the tooltip won't get a MouseMove message
Since this is a painted custom control, I think it might be easier to just have a variable hold the last shown tip, and instead of always "setting" the tooltip, just show it.
Simple example (using just a form):
public partial class Form1 : Form {
private List<TipRect> _Tips = new List<TipRect>();
private TipRect _LastTip;
private ToolTip _tooltip = new ToolTip();
public Form1() {
InitializeComponent();
_Tips.Add(new TipRect(new Rectangle(32, 32, 32, 32), "Tip #1"));
_Tips.Add(new TipRect(new Rectangle(100, 100, 32, 32), "Tip #2"));
}
private void Form1_Paint(object sender, PaintEventArgs e) {
foreach (TipRect tr in _Tips)
e.Graphics.FillRectangle(Brushes.Red, tr.Rect);
}
private void Form1_MouseMove(object sender, MouseEventArgs e) {
TipRect checkTip = GetTip(e.Location);
if (checkTip == null) {
_LastTip = null;
_tooltip.Hide(this);
} else {
if (checkTip != _LastTip) {
_LastTip = checkTip;
_tooltip.Show(checkTip.Text, this, e.Location.X + 10, e.Location.Y + 10, 1000);
}
}
}
private TipRect GetTip(Point p) {
TipRect value = null;
foreach (TipRect tr in _Tips) {
if (tr.Rect.Contains(p))
value = tr;
}
return value;
}
}
Here is the TipRect class I created to simulate whatever your PlannedItem class is:
public class TipRect {
public Rectangle Rect;
public string Text;
public TipRect(Rectangle r, string text) {
Rect = r;
Text = text;
}
}
I imagine your mouse does move a little when you think it is still. I suggest you do some kind of caching here - only call _tooltip.SetToolTip if the plannedItem has changed.
For the visitors of this thread, here is what I did, following suggestions above (VB.NET):
Dim LastToolTip As String
Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
Dim NewToolTip = CalculateTooltipText(e.X, e.Y)
If LastToolTip <> NewToolTip Then
ToolTip1.SetToolTip(PictureBox1, NewToolTip)
LastToolTip = NewToolTip
End If
End Sub
It stopped the flickering.
c# (works on tooltip chart):
Point mem = new Point();
private void xxx_MouseMove(MouseEventArgs e){
// start
Point pos = e.Location;
if (pos == mem) { return; }
// your code here
// end
mem = pos
}

Categories

Resources