Drawing over RichTextBox - c#

I'm trying to draw borders around words and paragraphs in RichTextBox but when I turn on UserPaint it doesn't draw text anymore while my custom painting seems to work. May be I just forgot to turn on something else? Here is what I have
public partial class RichTextBoxEx : RichTextBox
{
public RichTextBoxEx()
{
InitializeComponent();
SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
//Do some painting here
}
}
Using info from this question didn't help me

This worked ok for me:
class RichBox : RichTextBox {
private const int WM_PAINT = 15;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_PAINT) {
this.Invalidate();
base.WndProc(ref m);
using (Graphics g = Graphics.FromHwnd(this.Handle)) {
g.DrawLine(Pens.Red, Point.Empty,
new Point(this.ClientSize.Width - 1,
this.ClientSize.Height - 1));
}
} else {
base.WndProc(ref m);
}
}
}

The events in rich textbox are a pain in the back since they don't fire the way you think they should fire. Here's a post where someone posted the minimum required code to have a new control that exposes the proper paint events for you by hosting a rich textbox and intercepting the windows paint requests. It is in VB.Net but should be easy to translate it for your use.

Related

How to display/hide a control on mouse hover event

I have been developing an application lately, and found myself stuck on a simple but annoying problem.
I would like to make a specific control visible/not visible when I enter its parent, and being able to perform events (e.g.: click) on this control. The problem is, the mouse hover even does not work on the parent when I enter the very control I want to display. This result in a flickering of the control I want to display (mouse hover works -> control is displayed -> mouse hover does not work anymore -> control is hidden -> mouse hover works -> etc).
I have found this "solution" to help me have something "stable".
// Timer to make the control appearing properly.
private void Timer_Elapsed(object o, ElapsedEventArgs e)
{
try
{
ItemToHideDisplay.Visible = true;
var mousePoint = this.PointToClient(Cursor.Position);
if (mousePoint.X > this.Width ||
mousePoint.X < 0 ||
mousePoint.Y > this.Height ||
mousePoint.Y < 0)
{
HideDisplayTimer.Stop();
ItemToHideDisplay.Visible = false;
base.OnMouseLeave(e);
}
}
catch
{
// We don't want the application to crash...
}
}
protected override void OnMouseEnter(EventArgs e)
{
HideDisplayTimer.Start();
base.OnMouseEnter(e);
}
Basically, when I enter the object, a timer starts and checks every 50ms if the mouse is in the parent. If so, the control is displayed. If not, the timer is stopped and the control hidden.
This works. Yay. But I find this solution very ugly.
So my question is: is there another approach, another solution more beautiful than this one?
Tell me if I am not clear enough :)
Thanks in advance!
EDIT: Hey I think I have found it myself!
The trick is to override OnMouseLeave of the parent control with this:
protected override void OnMouseLeave(EventArgs e)
{
var mousePoint = this.PointToClient(Cursor.Position);
if (mousePoint.X > this.Width ||
mousePoint.X < 0 ||
mousePoint.Y > this.Height ||
mousePoint.Y < 0)
{
base.OnMouseLeave(e);
}
}
This way, when entering the control I have displayed (entering the parent control), the mouse leave event is not triggered!
It works!
Thanks for your answers. You can continue to post your ideas I guess, because I don't see a lot of solutions out there on the internet :)
You can make a control "transparent" to mouse events. So mouse events will just pass through it.
You have to write your own class inheriting your desired control. If, for example, a Label is your specific control, then create a new class inheriting Label - you get the point. :-)
In your new class you then make use of the window messages to make your control ignore mouse events:
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 0x0084;
const int HTTRANSPARENT = -1;
switch(m.Msg)
{
case WM_NCHITTEST:
m.Result = (IntPtr)HTTRANSPARENT;
break;
default:
base.WndProc(ref m);
}
}
You can read more about WndProc at MSDN.
You could register a message filter for your form and pre-process the mouse move events of your form. Thanks to this, you don't have to override your child controls etc.
The message filter, once registered in the parent form, will work for the child forms too, so even when a part of your form is covered by a child form, your target control should still appear/dissapear depending on the mouse position.
In the following example, there is a panel on a form, and that panel has a button inside.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
internal void CaptureMouseMove(Point location)
{
if (panel1.RectangleToScreen(panel1.ClientRectangle).Contains(location))
{
button1.Visible = true;
Console.WriteLine(location + "in " + panel1.RectangleToScreen(panel1.ClientRectangle));
}
else
{
button1.Visible = false;
Console.WriteLine(location + "out " + panel1.RectangleToScreen(panel1.ClientRectangle));
}
}
internal bool Form1_ProcessMouseMove(Message m)
{
Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
Control ctr = Control.FromHandle(m.HWnd);
if (ctr != null)
{
pos = ctr.PointToScreen(pos);
}
else
{
pos = this.PointToScreen(pos);
}
this.CaptureMouseMove(pos);
return false;
}
private MouseMoveMessageFilter mouseMessageFilter;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// add filter here
this.mouseMessageFilter = new MouseMoveMessageFilter();
this.mouseMessageFilter.TargetForm = this;
this.mouseMessageFilter.ProcessMouseMove = this.Form1_ProcessMouseMove;
Application.AddMessageFilter(this.mouseMessageFilter);
}
protected override void OnClosed(EventArgs e)
{
// remove filter here
Application.RemoveMessageFilter(this.mouseMessageFilter);
base.OnClosed(e);
}
private class MouseMoveMessageFilter : IMessageFilter
{
public Form TargetForm { get; set; }
public Func<Message, bool> ProcessMouseMove;
public bool PreFilterMessage(ref Message m)
{
if (TargetForm.IsDisposed) return false;
//WM_MOUSEMOVE
if (m.Msg == 0x0200)
{
if (ProcessMouseMove != null)
return ProcessMouseMove(m);
}
return false;
}
}
}
I would do a trick here :) I would wrap the control into a new control :) check this out.
XAML:
<UserControl MouseEnter="Border_MouseEnter" MouseLeave="UserControl_MouseLeave" Margin="100" Background="Transparent">
<UserControl x:Name="ControlToHide" Background="Red">
<Button Content="hi" Width="100" Height="100"/>
</UserControl>
</UserControl>
Code behind:
private void Border_MouseEnter(object sender, MouseEventArgs e)
{
this.ControlToHide.Visibility = System.Windows.Visibility.Hidden;
}
private void UserControl_MouseLeave(object sender, MouseEventArgs e)
{
this.ControlToHide.Visibility = System.Windows.Visibility.Visible;
}
it's light, easy and working . Enjoy

Removing the padding around a Tab control in WinForms [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I remove the border padding on container controls in WinForms?
I've developed a Winforms application in Visual studio 2008. On the main form I have a Tab control. Now I am trying to use a background image to the tab page. The problem that I am running into is that the tab control seems to have a thick border around it. Also the tab control does not cover the entire form leaving a line of space over the top between the form and tab page. (I have the tab pages alignment set at bottom). So a border around the tab control and line of space at the top making my page look ugly. I tried to give the same image as the background to form, but the tab control padding playing the spoilsport.
Any ideas to make my design better would be appreciated.
I agree with the majority of comments made here. The standard TabControl is drawn very poorly on Microsoft's Part...even in Windows Vista / 7 it doesn't look great! You would be better to write your own custom implementation by inheriting the TabControl and then drawing the additional stuff you want.
You might consider using this as a template for your new control. You just need to add some cool design / drawing work to the OnPaint and OnPaintBackground methods.
namespace CustomControls
{
#region USING
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
#endregion
public class CustomTabControl : TabControl
{
#region VARIABLES
private int hotTrackTab = -1;
#endregion
#region INSTANCE CONSTRUCTORS
public CustomTabControl() : base()
{
this.InitializeComponent();
}
#endregion
#region INSTANCE METHODS
private void InitializeComponent()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.DrawMode = TabDrawMode.OwnerDrawFixed;
}
private int GetTabUnderCursor()
{
Point cursor = this.PointToClient(Cursor.Position);
for (int index = 0; index < this.TabPages.Count; index++)
{
if (this.GetTabRect(index).Contains(cursor))
{
return index;
}
}
return -1;
}
private void UpdateHotTrack()
{
int hot = GetTabUnderCursor();
if (hot != this.hotTrackTab)
{
if (this.hotTrackTab != -1)
{
this.Invalidate(this.GetTabRect(this.hotTrackTab));
}
this.hotTrackTab = hot;
if (this.hotTrackTab != -1)
{
this.Invalidate(this.GetTabRect(this.hotTrackTab));
}
this.Update();
}
}
#endregion
#region OVERRIDE METHODS
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
this.UpdateHotTrack();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
this.UpdateHotTrack();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
this.UpdateHotTrack();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
switch (this.Alignment)
{
case TabAlignment.Bottom:
case TabAlignment.Left:
case TabAlignment.Right:
case TabAlignment.Top:
default:
throw new NotImplementedException();
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
base.OnPaintBackground(pevent);
}
#endregion
}
}
Bear in mind that the code above draw an absolutely blank TabControl that only displays the DisplayRectangle. Everything else including the tabs you will need to do yourself!
Additionally, to draw backgrounds for individual TabPages, you may need to override and custom implement TabPage as well, however you might be able to achieve the result you are looking for with just a custom tab control.
Check this out
http://www.codeproject.com/Articles/42046/Customized-TabControl-by-Repainting-Microsoft-s-Pa
IMHO This is better...
http://www.codeproject.com/Articles/38014/KRBTabControl
IMHO this is better still...
http://www.codeproject.com/Articles/91387/Painting-Your-Own-Tabs-Second-Edition
also look on VB Forums...I know I've seem some awesome custom tab controls on there!

How to avoid deselecting text on Winforms TextBox when clicking on selected text?

I would like to implement a drag&drop operation from TextBox to another control.
The problem is that when you select some part of text and then click on the TextBox text is deselected. Therefore when I perform DoDragDrop in MouseDown event the textBox.SelectedText is already empty.
Is there any way to avoid such behavior? I found following example but I don't want to loose the possibility to drag&drop only a part of text.
I found solution. You need to inherit TextBox and override OnMouseDown and WndProc:
public class DragTextBox : TextBox
{
private string dragText;
private const int WM_LBUTTONDOWN = 0x201;
protected override void OnMouseDown(MouseEventArgs e)
{
if (dragText.Length > 0)
{
SelectionStart = Text.IndexOf(dragText);
SelectionLength = dragText.Length;
DoDragDrop(dragText, DragDropEffects.Copy);
SelectionLength = 0;
}
base.OnMouseDown(e);
}
protected override void WndProc(ref Message m)
{
if ((m.Msg == WM_LBUTTONDOWN))
dragText = SelectedText;
base.WndProc(ref m);
}
}
Original code author post here

Forwarding child control MouseMove events to the parent seamlessly

I have a custom UserControl that I am drawing using the GDI+. It is a transparent control which draws small shapes on top of the parent control.
All the parent window does is creates the control, gives it a Rectangle in which to draw itself, and then it receives events if the user clicks on the non-transparent areas.
The drawing part works perfectly, but right now what I need to do is to as seamlessly as possible forward all MouseMove, MouseClick, etc. events to the parent control IF those events occurred outside the shapes.
The shapes are drawn using GraphicsPath, and I am already able to detect if the mouse position is over a shape using GraphicsPath.IsVisible().
I want to do this in a way that requires zero or minimal extra code on the parent. The parent should not necessarily know whether the MouseMove event was forwarded from the child control or not, it should treat them all equally.
Do I have to pinvoke/SendMessage() to do this? Or is there an easier way using the .NET framework?
This is possible in the winapi, the WM_NCHITTEST message is sent by the window manager to ask what part of the control the mouse is on top of. What you can do is return HTTRANSPARENT and it will ask the parent window. Here's a sample UserControl that implements this. Catching the message requires overriding WndProc():
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
paths = new List<GraphicsPath>();
GraphicsPath example = new GraphicsPath();
example.AddEllipse(new Rectangle(10, 10, 50, 30));
paths.Add(example);
}
List<GraphicsPath> paths;
protected override void OnPaint(PaintEventArgs e) {
foreach (var path in paths) e.Graphics.FillPath(Brushes.Blue, path);
base.OnPaint(e);
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
// Trap WM_NCHITTEST on the client area
if (m.Msg == 0x84 && m.Result == (IntPtr)1) {
Point pos = new Point(m.LParam.ToInt32());
pos = this.PointToClient(pos);
bool oncurve = false;
foreach (var path in paths)
if (path.IsVisible(pos)) oncurve = true;
if (!oncurve) m.Result = (IntPtr)(-1); // HTTRANSPARENT
}
}
}
Test code in the form:
private void userControl11_MouseMove(object sender, MouseEventArgs e) {
Console.WriteLine("On shape {0}", e.Location);
}
private void Form1_MouseMove(object sender, MouseEventArgs e) {
Console.WriteLine("On form {0}", e.Location);
}

How to move WinForm window without border but with a panel covering the whole area

I saw the code posted on this forum for moving the WinForm without borders but my dialog (C#) has a panel covering the whole area. I know I have to use WndProc to do this. I don't know what to do at this point. My window doesn't move unless I expose some of it by shrinking the size of the panel. Thank you.
The code I have:
protected override void WndPro(ref Message m)
{
switch(m.Msg)
{
case 0x84:m.Result = new intPtr(0x2);
return
}
base.wndProc(ref m);
}
You'll need to give the panel the same kind of treatment, except that you return HTTRANSPARENT. That makes it transparent to hit tests and the form will get the message. Now it works. Add a class to your project and paste the code shown below. Compile. Replace your existing panel with this one.
using System;
using System.Windows.Forms;
class BackPanel : Panel {
protected override void WndProc(ref Message m) {
if (m.Msg == 0x84) m.Result = (IntPtr)(-1);
else base.WndProc(ref m);
}
}

Categories