Transparent TextBox - OnPaint not firing when changin text - winforms

My control properly starts as transparent, but once I enter text, it's going to a regular white background. If I move my mouse over the control, it fires the OnPaint again, and is able to draw the control with text correctly. How can I get it to paint the transparent background when text is being entered?
Default:
Directly after entering text:
After moving mouse over, to force OnPaint to fire:
ref class CustomTextBox : System::Windows::Forms::TextBox
{
public:
CustomTextBox()
{
SetStyle(ControlStyles::SupportsTransparentBackColor, true);
SetStyle(ControlStyles::OptimizedDoubleBuffer, true); //Tried this both ways
SetStyle(ControlStyles::AllPaintingInWmPaint, true);
SetStyle(ControlStyles::ResizeRedraw, true);
SetStyle(ControlStyles::UserPaint, true);
BackColor = Color::Transparent;
ForeColor = Color::Red;
}
virtual void OnPrint(PaintEventArgs^ e) override
{
System::Windows::Forms::TextBox::OnPrint(e);
Debug::WriteLine("OnPrint: " + this->Text);
}
virtual void OnPaintBackground(PaintEventArgs^ e) override
{
System::Windows::Forms::TextBox::OnPaintBackground(e);
Debug::WriteLine("OnPaintBackground: " + this->Text);
}
virtual void OnPaint(PaintEventArgs^ e) override
{
//System::Windows::Forms::TextBox::OnPaint(e);
//Paint Background
Graphics^ g = e->Graphics;
RectangleF^ bounds = gcnew RectangleF(0, 0, Convert::ToSingle(this->Width - 1), Convert::ToSingle(this->Height - 1));
e->Graphics->FillRectangle(gcnew SolidBrush(this->BackColor), *bounds);
//Paint text
g->DrawString(this->Text,this->Font,gcnew SolidBrush(Color::Red),1,1);
Debug::WriteLine("OnPaint: " + this->Text);
}
};

FYI - Found the solution:
1) Tie the TextChanged event to a method that will fire every time text is entered.
2) In the new method, mark the control: control->Invalidate() to trigger the re-painting.

Related

WinForm : Make control unfocusable

My winform contains a TextBox that is the main control of the form. When I do CtrL + C, and often end up with an empty clipboard because for some reason the ActiveControl of the form is set to another control, like for instance the TabControl, the SplitContainer, etc. I tried to set those control TabStop = fasle, but to avail. Is there a way to prevent all controls from getting focus ? Not only regarding the mouse clicks, but also the tab keys.
I can imagine trying with some native windows messages like WM_SetFocus, or some native windows styles like WS_TABSTOP, or some control styles like ControlStyles.Selectable, or ControlStyles.UserMouse, but I'd say you cannot find a generic solution which handles all controls.
What I'm proposing here (according to your comments, looking for a better way of handling focus rather than trying to make things focusable, which makes more sense in UX point of view as well) is handling TabControl and SplitterContainer focus related events:
Tab control: Handle SelectedIndexChanged, and then move the focus to the first focusable control of the tab.
Splitter container: Handle MouseDown to trap the start of splitting, then store the active panel; later handle SplitterMoved and move the focus to he first focusable control of the active panel.
Here is what worked for me:
SplitterPanel activePanel;
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
{
splitContainer1.SelectNextControl(activePanel,
forward: true, tabStopOnly: true, nested: true, wrap: true);
}
private void splitContainer1_MouseDown(object sender, MouseEventArgs e)
{
activePanel = splitContainer1.Panel1.ContainsFocus ? splitContainer1.Panel1 :
splitContainer1.Panel2.ContainsFocus ? splitContainer1.Panel2 : null;
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
var page = this.tabControl1.SelectedTab;
page.SelectNextControl(null,
forward: true, tabStopOnly: true, nested: true, wrap: true);
}
Instead of preventing focus, which might cause problems for those trying to navigate your app using only the keyboard, implement the IMessageFilter interface and trap Ctrl-C for your whole app. Then you can simply put the contents of your "main textbox" on the clipboard manually:
public partial class Form1 : Form, IMessageFilter
{
public Form1()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg == 0x0100 &&
(Keys)m.WParam.ToInt32() == Keys.C &&
ModifierKeys == Keys.Control)
{
Console.WriteLine("Ctrl-C Trapped!");
if (textBox1.SelectionLength > 0)
{
Clipboard.SetText(textBox1.SelectedText);
}
else
{
Clipboard.SetText(textBox1.Text);
}
return true;
}
return false;
}
}

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];
}
}

Transparent control not capturing mouse events

I have a window with a transparent background (not null). Inside that window, I have a user control, also with a transparent background.
The window receives mouse events, but the user control does not.
If I change the background of the user control from Transparent to #01000000, then the user control starts to receive mouse events. However, controls hosted within the user control (which are themselves visible) never receive mouse events, regardless of the user control's background.
Any ideas?
There must be something handling the event or you have some configuration issue, since it works ok for me: http://share.linqpad.net/ijx3vb.linq. (Get Linqpad free.)
var t = new TextBlock() { Text = "test" };
var uc1 = new UserControl() { Background = new SolidColorBrush(Colors.Transparent) };
uc1.MouseEnter += (s, args) => { t.Text = "UC1"; };
var uc2 = new UserControl() { Background = new SolidColorBrush(Color.FromArgb(01, 00, 00, 00)) };
uc2.MouseEnter += (s, args) => { t.Text = "UC2"; };
var g = new Grid();
g.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
g.RowDefinitions.Add(new RowDefinition());
g.RowDefinitions.Add(new RowDefinition());
g.Children.Add(t);
g.Children.Add(uc1);
g.Children.Add(uc2);
Grid.SetRow(t, 0);
Grid.SetRow(uc1, 1);
Grid.SetRow(uc2, 2);
PanelManager.DisplayWpfElement(g);

Can I make a WPF ListBox change selection on left button press only

I have a WPF ListBox operating in single selection mode. I am adding drag and drop to move items around. Currently the ListBox selection responds to both left button pressed and then with mouse moves with left button down. So after I wait for the MinimumVerticalDragDistance to start a drag operation, a different item could be selected. Dragging either the unselected orginal item or dragging the new selected item is confusing. Adding 'e.Handled=true' in xxx_MouseMove or xxx_PreviewMouseMove does not do anything. Any ideas on suppressing this selection due to mouse moves with left button down?
The best kludge I came up with is to cancel the ListBox's "Selection by dragging" in the IsMouseCapturedChanged event.
public partial class MainWindow : Window
{
Rect? dragSourceGestureRect;
bool busy;
public MainWindow()
{
InitializeComponent();
listBox.ItemsSource = Enumerable.Range(1, 9);
listBox.PreviewMouseLeftButtonDown += listBox_PreviewMouseLeftButtonDown;
listBox.IsMouseCapturedChanged += listBox_IsMouseCapturedChanged;
listBox.MouseMove += listBox_MouseMove;
}
void listBox_IsMouseCapturedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (busy)
return;
if (!listBox.IsMouseCaptured)
dragSourceGestureRect = null;
else if (dragSourceGestureRect.HasValue)
{
busy = true;
{
//tell the ListBox to cancel it's "Selection by dragging"
listBox.ReleaseMouseCapture();
//Now recapture the mouse for canceling my dragging
listBox.CaptureMouse();
}
busy = false;
}
}
void listBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var center = e.GetPosition(listBox);
dragSourceGestureRect = new Rect(
center.X - SystemParameters.MinimumHorizontalDragDistance / 2,
center.Y - SystemParameters.MinimumVerticalDragDistance / 2,
SystemParameters.MinimumHorizontalDragDistance,
SystemParameters.MinimumVerticalDragDistance);
}
void listBox_MouseMove(object sender, MouseEventArgs e)
{
if (!dragSourceGestureRect.HasValue || dragSourceGestureRect.Value.Contains(e.GetPosition(listBox)))
return;
dragSourceGestureRect = null;
var data = new DataObject(DataFormats.UnicodeText, "The Data");
DragDrop.DoDragDrop(listBox, data, DragDropEffects.Copy);
e.Handled = true;
}
}

Change border color of Windows Forms Control on focus

Is there a way to change a border color of some common controls in Windows Forms (TextBox, ComboBox, MaskedTextBox, ...) when they are in focus? I would like to achieve that in my dialog, so when control is in focus it's border becomes blue?
I suggest to draw a rectangle around the active control as the following:
I need a method to get all the controls which in the form, even which they're in nested Panel or GroupBoxe.
The method:
// Get all controls that exist in the form.
public static List<Control> GetAllControls(IList controls)
{
List<Control> controlsCollectorList = new List<Control>();
foreach (Control control in controls)
{
controlsCollectorList.Add(control);
List<Control> SubControls = GetAllControls(control.Controls);
controlsCollectorList.AddRange(SubControls);
}
return controlsCollectorList;
}
Then.. Drawing functionality..
The code:
public Form1()
{
InitializeComponent();
// The parents that'll draw the borders for their children
HashSet<Control> parents = new HashSet<Control>();
// The controls' types that you want to apply the new border on them
var controlsThatHaveBorder = new Type[] { typeof(TextBox), typeof(ComboBox) };
foreach (Control item in GetAllControls(Controls))
{
// except the control if it's not in controlsThatHaveBorder
if (!controlsThatHaveBorder.Contains(item.GetType())) continue;
// Redraw the parent when it get or lose the focus
item.GotFocus += (s, e) => ((Control)s).Parent.Invalidate();
item.LostFocus += (s, e) => ((Control)s).Parent.Invalidate();
parents.Add(item.Parent);
}
foreach (var parent in parents)
{
parent.Paint += (sender, e) =>
{
// Don't draw anything if this is not the parent of the active control
if (ActiveControl.Parent != sender) return;
// Create the border's bounds
var bounds = ActiveControl.Bounds;
var activeCountrolBounds = new Rectangle(bounds.X - 1, bounds.Y - 1, bounds.Width + 1, bounds.Height + 1);
// Draw the border...
((Control)sender).CreateGraphics().DrawRectangle(Pens.Blue, activeCountrolBounds);
};
}
}
Good luck!

Resources