Display custom tooltip as Balloon - winforms

I am using a custom tooltip class in my WinForm application. It works great. But i would like to show it as a Balloon. Here is my code-
class CustomToolTip : ToolTip
{
public CustomToolTip()
{
this.OwnerDraw = true;
this.Popup += new PopupEventHandler(this.OnPopup);
this.Draw += new DrawToolTipEventHandler(this.OnDraw);
}
private void OnPopup(object sender, PopupEventArgs e) // use this event to set the size of the tool tip
{
e.ToolTipSize = new Size(200, 100);
}
private void OnDraw(object sender, DrawToolTipEventArgs e) // use this event to customise the tool tip
{
Graphics g = e.Graphics;
LinearGradientBrush b = new LinearGradientBrush(e.Bounds,
Color.GreenYellow, Color.MintCream, 45f);
g.FillRectangle(b, e.Bounds);
g.DrawRectangle(new Pen(Brushes.Red, 1), new Rectangle(e.Bounds.X, e.Bounds.Y,
e.Bounds.Width - 1, e.Bounds.Height - 1));
g.DrawString(e.ToolTipText, new Font(e.Font, FontStyle.Bold), Brushes.Silver,
new PointF(e.Bounds.X + 6, e.Bounds.Y + 6)); // shadow layer
g.DrawString(e.ToolTipText, new Font(e.Font, FontStyle.Bold), Brushes.Black,
new PointF(e.Bounds.X + 5, e.Bounds.Y + 5)); // top layer
b.Dispose();
}
}
Any suggestion?
Thanking you in anticipation.

You could just draw an ellipse then add a piece to point to the target.
But why not use the default tooltip control.
It has a IsBalloon flag that when set to true looks like this:

Related

How to draw dropshadow effect in a geometry in WPF

I'm drawing the following Shape in a Canvas.
I would like to highlight it when it's selected by changing its color (the easy part) and drawing an small halo around it:
This is how I did using SASS: http://codepen.io/aaromnido/pen/zKvAwd/
How coud I draw in WPF? Remember that I'm drawing using the Shape's OnRender method.
Set some defaults in constructor.
One of these defaults is Shape.Effect, as it will be animated on MouseEnter event.
Construct VisualStates for Normal , and MouseEnter scenarios.
Change the VisualState of the element using VisualStateManager.GoToElementState() in MouseEnter and MouseLeave event handlers.
You can expose various properties using DPs for customization.
NewShape.cs
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows;
using System.Windows.Media.Effects;
namespace WpfStackOverflow.NewShape
{
public class CNewShape : Shape
{
public CNewShape()
{
// setting the defaults
this.Width = 40;
this.Height = 40;
this.Stroke = new SolidColorBrush() { Color = Colors.Red };
this.StrokeThickness = 5;
this.Effect = new DropShadowEffect() {
Color = Colors.Transparent,
BlurRadius = 1,
Direction = -150,
ShadowDepth = 1
};
// constructing the VisualStates
_constructVisualStates();
// event handlers
this.MouseEnter += CNewShape_MouseEnter;
this.MouseLeave += CNewShape_MouseLeave;
}
#region EventHandlers
void CNewShape_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
VisualStateManager.GoToElementState(this, "VSNormal", false);
}
void CNewShape_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
VisualStateManager.GoToElementState(this, "VSMouseEnter", false);
}
#endregion
#region Overrides
// This needs to be implemented as it is abstract in base class
GeometryGroup geo = new GeometryGroup();
protected override Geometry DefiningGeometry
{
get { return geo; }
}
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
Pen pen = new Pen(this.Stroke, StrokeThickness);
drawingContext.DrawEllipse(Brushes.Transparent, pen, new Point(Width/2, Height/2), 40, 40);
drawingContext.DrawEllipse(Stroke, null, new Point(Width / 2, Height / 2), 30, 30);
base.OnRender(drawingContext);
}
#endregion
#region Helpers
private void _constructVisualStates()
{
VisualStateGroup vsg1 = new VisualStateGroup();
#region VSNormal (Normal Visual State)
VisualState stateVSNormal = new VisualState() { Name = "VSNormal" };
Storyboard sbVSNormal = new Storyboard();
ObjectAnimationUsingKeyFrames oa = new ObjectAnimationUsingKeyFrames();
Storyboard.SetTargetProperty(oa, new PropertyPath("Effect"));
DiscreteObjectKeyFrame dokf = new DiscreteObjectKeyFrame(null);
oa.KeyFrames.Add(dokf);
sbVSNormal.Children.Add(oa);
stateVSNormal.Storyboard = sbVSNormal;
vsg1.States.Add(stateVSNormal);
#endregion
#region VSMouseEnter (MouseEnter Visual State)
VisualState stateVSMouseEnter = new VisualState() { Name = "VSMouseEnter" };
Storyboard sbVSMouseEnter = new Storyboard();
ColorAnimation caStrokeColor = new ColorAnimation();
caStrokeColor.To = (Color)ColorConverter.ConvertFromString("#FF24BCDE");
Storyboard.SetTargetProperty(caStrokeColor, new PropertyPath("(Shape.Stroke).(SolidColorBrush.Color)"));
sbVSMouseEnter.Children.Add(caStrokeColor);
ColorAnimation caEffectColor = new ColorAnimation();
caEffectColor.To = (Color)ColorConverter.ConvertFromString("#FFA4E1F3");
Storyboard.SetTargetProperty(caEffectColor, new PropertyPath("(Shape.Effect).(Color)"));
sbVSMouseEnter.Children.Add(caEffectColor);
DoubleAnimation daBlurRadius = new DoubleAnimation();
daBlurRadius.To = 10;
Storyboard.SetTargetProperty(daBlurRadius, new PropertyPath("(Shape.Effect).(BlurRadius)"));
sbVSMouseEnter.Children.Add(daBlurRadius);
DoubleAnimation daDirection = new DoubleAnimation();
daDirection.To = -190;
Storyboard.SetTargetProperty(daDirection, new PropertyPath("(Shape.Effect).(Direction)"));
sbVSMouseEnter.Children.Add(daDirection);
stateVSMouseEnter.Storyboard = sbVSMouseEnter;
vsg1.States.Add(stateVSMouseEnter);
#endregion
VisualStateManager.GetVisualStateGroups(this).Add(vsg1);
}
#endregion
}
}
Usage
<local:CNewShape Canvas.Left="70" Canvas.Top="52" Stroke="#FF374095" StrokeThickness="10" Width="100" Height="100" />
Output
Quality of the image is bad. On screen actual output looks good.
Whatever your trigger is that your control enters the Highlighted state, in that trigger just set the Effect property. For my test the "trigger" is a property:
public static readonly DependencyProperty ShowShadowProperty =
DependencyProperty.Register ("ShowShadow", typeof (bool), typeof (TestShape), new PropertyMetadata (false, ShowShadowChanged));
public bool ShowShadow
{
get { return (bool)GetValue (ShowShadowProperty); }
set { SetValue (ShowShadowProperty, value); }
}
private static void ShowShadowChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TestShape)d).OnShowShadow ();
}
private void OnShowShadow ()
{
if (ShowShadow)
{
Effect = new DropShadowEffect { Direction = 0, ShadowDepth = 20, BlurRadius = 33, Opacity = 1, Color = Colors.Black};
}
else
{
Effect = null;
}
}
Which means you don't need to do anything in OnRender.

How to draw a Plus/Minus on a Toggle Button based on Toggle State in WinForms

public partial class Form1 : Form
{
CheckBoxExt checkBox;
public Form1()
{
InitializeComponent();
checkBox = new CheckBoxExt();
this.Controls.Add(checkBox);
}
}
public class CheckBoxExt : CheckBox
{
public CheckBoxExt()
{
this.Size = new Size(20, 20);
this.Location = new Point(200, 200);
this.Appearance = Appearance.Button;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var point = this.PointToScreen(new Point(0, 0));
e.Graphics.DrawLine(new Pen(ColorTranslator.FromHtml("#666666"), 2), new Point(point.X + 5, point.Y + 10), new Point(point.X + 15, point.Y + 10));
if (this.Checked)
e.Graphics.DrawLine(new Pen(ColorTranslator.FromHtml("#666666"), 2), new Point(point.X + 10, point.Y + 5), new Point(point.X + 10, point.Y + 15));
}
/// <summary>
/// to remove the focus dotted border over the control
/// </summary>
protected override bool ShowFocusCues
{
get
{
return false;
}
}
}
Here is my code, i customized CheckBox and sets it Appearance as Button so that it will act as Toggle button.
i need to draw a small horizontal line which will display as minus on the Button, and draw a small horizontal and vertical line which will display as Plus button. Hence it need to toggled, if it's in checked state plus need to be shown and if it's unchecked minus need to be shown over the toggle button
Thanks in Advance
I would use an imageList control and populated it with plus and minus images respectively with desired resolution.
The BackgroundImage property of the control can be used to set "+" and "-" images.
And, don't forget to register CheckedChanged event of the control to toggle the BackgroundImage of the control.
Something like this for example -
public Form1()
{
InitializeComponent();
var checkBox = new CheckBoxExt();
//My imageList contain two images, for "+" and "-"
//Register chacked changed event for the control
checkBox.CheckedChanged += checkBox_CheckedChanged;
//Set the initial image as "-"
checkBox.BackgroundImage = this.imageList1.Images[1];
this.Controls.Add(checkBox);
}
void checkBox_CheckedChanged(object sender, EventArgs e)
{
if (((CheckBox)sender).Checked)
{
((CheckBox)sender).BackgroundImage = this.imageList1.Images[0];
}
else
{
((CheckBox)sender).BackgroundImage = this.imageList1.Images[1];
}
}

WPF Animate border background color

I'm trying to animate the color of the brush for the background of a custom class that inherits from Border. I've tried the MSDN link here:
http://msdn.microsoft.com/en-us/library/system.windows.media.animation.coloranimation.aspx
This is not exactly what I'm looking for, but can get me to a point with no errors but still, nothing is animating. The problem with the example is that they are defining the logic within a class that isn't the rectangle. I am trying to define from within the rectangle (actually the border).
Below is my code that I tried to extrapolate from MSDN for my situation.
public class PrettyButton : System.Windows.Controls.Border
{
private System.Windows.Media.SolidColorBrush hoverColor = new System.Windows.Media.SolidColorBrush();
private System.Windows.Media.SolidColorBrush origColor = new System.Windows.Media.SolidColorBrush();
private System.Windows.Media.Animation.Storyboard story = new System.Windows.Media.Animation.Storyboard();
public PrettyButton()
{
hoverColor.Color = System.Windows.Media.Color.FromArgb(255, 50, 200, 0);
origColor.Color = System.Windows.Media.Color.FromArgb(0, 0, 0, 0);
this.MouseEnter += PrettyButton_MouseEnter;
this.MouseLeave += PrettyButton_MouseLeave;
//Animate in logic
System.Windows.Media.Animation.ColorAnimation color = new System.Windows.Media.Animation.ColorAnimation(hoverColor.Color, System.TimeSpan.FromMilliseconds(400));
System.Windows.Media.Animation.Storyboard.SetTargetProperty(color, new System.Windows.PropertyPath(System.Windows.Media.SolidColorBrush.ColorProperty));
story.Children.Add(color);
}
and below in the mouseEvent I have
void PrettyButton_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
story.Begin(this);
}
Unfortunately, I'm not getting any more errors so the trail has gone cold for me. I am also certain that I could probably find 10 solutions in XAML, but I would like for this class to be reusable in the future, and redefining this logic is not ideal.
Instead of System.Windows.Media.SolidColorBrush.ColorProperty, try setting "(Border.Background).(SolidColorBrush.Color)"
Property Path.
System.Windows.Media.Animation.Storyboard.SetTargetProperty(color, new System.Windows.PropertyPath("(Border.Background).(SolidColorBrush.Color)"));
Also set Background in constructor of PrettyButton as like this :
public PrettyButton()
{
.....
origColor.Color = System.Windows.Media.Color.FromArgb(0, 0, 0, 0);
this.Background= new SolidColorBrush(origColor.Color);
..
....
}
UPDATE :
public class PrettyButton : System.Windows.Controls.Border
{
private System.Windows.Media.SolidColorBrush hoverColor = new System.Windows.Media.SolidColorBrush();
private System.Windows.Media.SolidColorBrush origColor = new System.Windows.Media.SolidColorBrush();
public PrettyButton()
{
hoverColor.Color = System.Windows.Media.Color.FromArgb(255, 50, 200, 0);
origColor.Color = System.Windows.Media.Color.FromArgb(0, 0, 0, 0);
this.Background= new SolidColorBrush(origColor.Color);
this.MouseEnter += PrettyButton_MouseEnter;
this.MouseLeave += PrettyButton_MouseLeave;
}
private void PrettyButton_MouseLeave(object sender, MouseEventArgs e)
{
System.Windows.Media.Animation.Storyboard story = new System.Windows.Media.Animation.Storyboard();
System.Windows.Media.Animation.ColorAnimation color = new System.Windows.Media.Animation.ColorAnimation(origColor.Color, System.TimeSpan.FromMilliseconds(400));
System.Windows.Media.Animation.Storyboard.SetTargetProperty(color, new System.Windows.PropertyPath("(Border.Background).(SolidColorBrush.Color)"));
story.Children.Add(color);
story.Begin(this);
}
private void PrettyButton_MouseEnter(object sender, MouseEventArgs e)
{
System.Windows.Media.Animation.Storyboard story = new System.Windows.Media.Animation.Storyboard();
System.Windows.Media.Animation.ColorAnimation color = new System.Windows.Media.Animation.ColorAnimation(hoverColor.Color, System.TimeSpan.FromMilliseconds(400));
System.Windows.Media.Animation.Storyboard.SetTargetProperty(color, new System.Windows.PropertyPath("(Border.Background).(SolidColorBrush.Color)"));
story.Children.Add(color);
story.Begin(this);
}
}

DataGridView Custom control enable button in Unedited mode

Does anyone know how to get the button visible all the time?.(Not only in edit mode of the cell)
I would like to take your attention to the answer of this question.
how to add ellipse button and textbox in current cell of datagridview in winforms
I could enhance this solution to see the button control in the cell for all the time. What I want is to get the popup box for the first click of the cell. This is the code to paint the button in uneditted mode.
// Finally paint the NumericUpDown control
Rectangle srcRect = new Rectangle(0, 0, valBounds.Width, valBounds.Height);
if (srcRect.Width > 0 && srcRect.Height > 0)
{
Bitmap renderingBitmap = new Bitmap(22, 18);
new TextButton().button.DrawToBitmap(renderingBitmap, srcRect);
graphics.DrawImage(renderingBitmap, new Rectangle(new Point(cellBounds.X+cellBounds.Width-24, valBounds.Location.Y-2), valBounds.Size),
srcRect, GraphicsUnit.Pixel);
}
A better option would be to embed a button on your DataGridView. This would give you more control over the use of DataGridView. See the following snippet:
Button b1 = new Button();
int cRow = 0, cCol = 0;
private void Form1_Load(object sender, EventArgs e)
{
b1.Text = "...";
b1.Visible = false;
this.dataGridView1.Controls.Add(b1);
b1.BringToFront();
this.dataGridView1.Paint += new PaintEventHandler(dataGridView1_Paint);
this.b1.Click += new EventHandler(b1_Click);
}
void b1_Click(object sender, EventArgs e)
{
//Implement your logic here
}
void dataGridView1_Paint(object sender, PaintEventArgs e)
{
if(cRow != 0 && cCol != 0)
{
Rectangle rect = new Rectangle();
rect = dataGridView1.GetCellDisplayRectangle(cRow ,cCol , false);
rect.X = rect.X + (2*dataGridView1.Columns[1].Width / 3);
rect.Width = dataGridView1.Columns[1].Width / 3;
b1.Bounds = rect;
b1.Visible = true;
}
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
cRow = e.RowIndex;
cCol = e.ColumnIndex;
}
In the above snippet the location of ellipses button is set to last clicked cell. The visibility is always true after a cell is clicked. In my opinion this would provide a far better control over the button's function,and is easier to maintain.

Recommendation for Windows.Forms Control that Supports Mouse Panning and Zooming

I found Customize a panel with Autoscroll property at http://www.codeproject.com/KB/miscctrl/CustomAutoScrollPanel.aspx that is wrapper around a Panel with AutoScroll = True.
I like this control because it provides the "performScrollHorizontal" and "performScrollVertical" methods. Yet, it uses the Windows API functions instead of ScrollableControl and its VerticalScroll and HorizontalScroll properties.
Is this a good control to use? I think it should be using ScrollableControl instead of the Windows API. What do you think? Is there a better control available? Do I even need a control? I would think that ScrollableControl provides everything I would need.
EDIT: I found the HScrollBar and VScrollBar controls. Should I be using them?
These two other controls are nice but do not give me a way to control the scroll bars like the above control does.
A scrollable, zoomable, and scalable picture box at
http://www.codeproject.com/KB/miscctrl/ScalablePictureBox.aspx
Pan and Zoom Very Large Images at
http://www.codeproject.com/KB/GDI-plus/PanZoomExample.aspx
What I really want is a control:
that scrolls when the user moves the mouse toward the edge of the control,
allows the user to pan
allows the user to zoom
supports using the mouse with Shift or Ctrl or Alt keys pressed
Any recommendations, help, or areas to look at is greatly appreciated. A control would be nice as I am not that good yet.
Some code to play with. It supports focus, panning and scrolling. Zooming is work-to-do, my laptop's mousepad got in the way of testing it. Use a timer to implement auto-scrolling at the edges.
using System;
using System.Drawing;
using System.Windows.Forms;
class ZoomPanel : Panel {
public ZoomPanel() {
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.AutoScroll = this.TabStop = true;
}
public Image Image {
get { return mImage; }
set {
mImage = value;
Invalidate();
mZoom = 1.0;
this.AutoScrollMinSize = (mImage != null) ? mImage.Size : Size.Empty;
}
}
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
this.Cursor = Cursors.SizeAll;
mLastPos = e.Location;
this.Focus();
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) this.Cursor = Cursors.Default;
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
this.AutoScrollPosition = new Point(
-this.AutoScrollPosition.X - e.X + mLastPos.X,
-this.AutoScrollPosition.Y - e.Y + mLastPos.Y);
mLastPos = e.Location;
Invalidate();
}
base.OnMouseMove(e);
}
protected override void OnMouseWheel(MouseEventArgs e) {
if (mImage != null) {
mZoom *= 1.0 + 0.3 * e.Delta / 120;
this.AutoScrollMinSize = new Size((int)(mZoom * mImage.Width),
(int)(mZoom * mImage.Height)); \
// TODO: calculate new AutoScrollPosition...
Invalidate();
}
base.OnMouseWheel(e);
}
protected override void OnPaint(PaintEventArgs e) {
if (mImage != null) {
var state = e.Graphics.Save();
e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
e.Graphics.DrawImage(mImage,
new Rectangle(0, 0, this.AutoScrollMinSize.Width, this.AutoScrollMinSize.Height));
e.Graphics.Restore(state);
}
//if (this.Focused) ControlPaint.DrawFocusRectangle(e.Graphics,
// new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height));
base.OnPaint(e);
}
protected override void OnEnter(EventArgs e) { Invalidate(); base.OnEnter(e); }
protected override void OnLeave(EventArgs e) { Invalidate(); base.OnLeave(e); }
private double mZoom = 1.0;
private Point mLastPos;
private Image mImage;
}

Resources