I've got a really weird problem and i'm wondering if it's a visual's bug or if i'm doing something wrong. Here's a simple code of an overriden Panel class:
public class MyPanel : Panel
{
private TableLayoutPanel table = new TableLayoutPanel();
public MyPanel()
{
this.Controls.Add(table);
table.BackColor = Color.Green;
}
public override System.Drawing.Color BackColor
{
get
{
return table.BackColor;
}
set
{
table.BackColor = value;
}
}
}
If i put the control on a form and build the project, visual will generate an error and opening the project again will be impossible. However if i change TableLayoutPanel to TextBox, it works fine. Also, if i set the BackColor in the constructor before adding the control to the Controls collection, it also works fine.
What is the problem? or is it just a bug?
I suspect recursion may be an issue; by default (if not set explicitly) a control inherits color from the parent. This leads to the scenario where the child's color (if not set) asks the parent, which asks the child (forever).
TextBox, however, overrides this behaviour to return SystemColors.Window if there isn't an explicit color set. Hence no recursion.
Either way, I'm not sure this is a good idea - the designer might start duplicating controls if you aren't careful.
Related
How do I get rid of this ugly line?
Draw a default bindingnavigator on an empty Form and you will see the problem. RenderMode is ManagerRenderMode. I want this render mode so the mouse over colors is correct. However, If I switch to System as rendermode the ugly line disapears, but then mouse over color/effect gets ugly.
I have been looking around for a solution for some time now, but nothing. Maybe someone here have seen this problem before?
It's not a BindingNavigator specific issue, but the ToolStrip which BindingNavigator inherits.
It's caused by the DrawToolStripBorder method when the ToolStripProfessionalRenderer class RoundedEdges property is true (the default).
In order to turn it off, I can suggest the following helper method:
public static class WindowsFormsExtensions
{
public static void DisableRoundedEdges(this ToolStripRenderer renderer)
{
var professionalRenderer = renderer as ToolStripProfessionalRenderer;
if (professionalRenderer != null)
professionalRenderer.RoundedEdges = false;
}
}
Now you can turn it off for the specific control (it's not available at design time, so it has to be at run time inside your form/control constructor or load event):
this.bindingNavigator1.Renderer.DisableRoundedEdges();
or to disable it globally, add the following in your Main method before calling Application.Run:
ToolStripManager.Renderer.DisableRoundedEdges();
We have several winforms applications we want to restyle and make consistent (styling wise). We want to create some base classes for some of the windows controls such as buttons and toolstrip. All my base classes intend to do is specify the specific styling we wish to apply. This works the first time I bring into my form but later on if I wish to update a property it doesn't apply it back to the forms that previously already have pulled it in. Anyway to get this to work? Here's an example, lets say I want to create a base class for a toolstrip. All I want to do is set the backcolor to green (I really want to set more properties but this is to demo my issue). So I create a class library called BaseControls and create a class called BaseToolStrip and set the backcolor to green in its constructor.
public class BaseToolStrip : ToolStrip
{
public BaseToolStrip()
{
BackColor = Color.Green;
}
}
Now if I create a demo project and add an instance of BaseToolStrip to the form it works. It creates a green toolstrip. Later if I decided I want my base toolstrip to have a backcolor of red if I update the constructor in my BaseToolStrip class and set the BackColor = Color.Red and run my solution the color is still green. It appears that once I've brought that control in it set backcolor = green. So even though in my base class I set backcolor = red in my form that instantiates it still has backcolor = green. Any way to override all instances and set backcolor equal to the color specified in my BaseToolStrip class?
Your problem is that the WinForms designer is saving the current value in InitializeComponent() in any designer that uses that control.
That happens after your ctor runs and replaces your value.
You can prevent that by overriding / shadowing the property and adding [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] (you'll need to reopen & resave all designers that use it to remove the property).
Try setting the default value for the property (as well as setting the actual value in the ctor):
[DefaultValue(typeof(Color), "Green")]
public override Color BackColor {
get { return base.BackColor; }
set { base.BackColor = value; }
}
I do not have VS available to test right now, but I think that will cause the designer to notice that the current value is the default, and therefore doesn't need to be saved in the Designer.cs.
So if you change the color in both places later, it should update everywhere.
Of course, this will only work for controls that you drag onto a form after you add this code, since the others already have the value saved.
SCENARIO
When a user drops a TextBox control on the WindowsForms designer, the designer shows only two sizing selectors to resize the width of the control:
...Unless the TextBox.MultiLine property is manually enabled.
But if we add a RichTextBox, it shows 8 sizing selectors:
...even when the RichTextBox.MultiLine property is enabled.
QUESTION
What I would like to do is subclass a RichTextBox class to mimic the sizing behavior that a TextBox has by default at design-time, this means prevent height/corner sizing if the RichTextBox is not multiline.
To be exact, I would like to REMOVE/HIDE the height and corner sizing selectors at design-time, so the subclassed RichTextBox should show only two sizing selectors to resize the width of the control, like in the image above of the TextBox.
I'm aware of the methodology to override SetBoundsCore method to prevent height resizing at design-time, however I would like to go a little bit more far than that solution, because that solution does not remove those sizing selectors ...and just letting the size selectors visible is a ugly and confussing behavior at design-time.
I inspected the official TextBox class source-code to see what happens when the TextBox.MultiLine property value is changed, but I didn't seen anything relevant.
Maybe the DesignerAttribute() class assigned to the TextBox class (System.Windows.Forms.Design.TextBoxBaseDesigner) is involved and maybe it is who decides the sizing behavior at design-time?, in that case what I could do and how to do it?.
Those are called Sizing Handles and are determined by the SelectionRules() method in the designer associated with your control. One thing to keep in mind is that the default for a regular TextBox is MultiLine = False but it is the opposite for a RichTextBox.
The reason you could not find anything relevant in the Reference Source is because the System.Windows.Forms.Design.TextBoxDesigner is internal / Friend. Note also that changing the MultiLine property causes the control to be recreated (RecreateHandle(); in the source).
Imports System.Windows.Forms.Design
<Designer(GetType(RTBElektroDesigner))>
Public Class RTBElektro
Inherits RichTextBox
Public Sub New()
End Sub
End Class
Public Class RTBElektroDesigner
Inherits System.Windows.Forms.Design.ControlDesigner
Public Overrides ReadOnly Property SelectionRules() As SelectionRules
Get
Dim rtb = TryCast(MyBase.Control, RTBElektro)
If rtb Is Nothing Then
Return MyBase.SelectionRules
Else
If rtb.Multiline Then
Return SelectionRules.AllSizeable Or
SelectionRules.Moveable
Else
Return SelectionRules.LeftSizeable Or
SelectionRules.RightSizeable Or
SelectionRules.Moveable
End If
End If
End Get
End Property
End Class
Result:
This behavior is implemented by the TextBoxBaseDesigner. Also the base class for the RichTextBoxDesigner so you're good with the designer. What is missing here is the AutoSize property, RichTextBox hides it. It needs to be set to True when you change the Multiline property to False. You can't do that from the designer because it is hidden and the default value is False.
That's easily fixable by deriving your own class from RichTextBox:
using System;
using System.ComponentModel;
using System.Windows.Forms;
class RichTextBoxEx : RichTextBox {
public RichTextBoxEx() {
base.AutoSize = true;
base.Multiline = false;
}
[DefaultValue(true), Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public override bool AutoSize {
get => base.AutoSize;
set => base.AutoSize = value;
}
[DefaultValue(false)]
public override bool Multiline {
get => base.Multiline;
set {
base.Multiline = value;
base.AutoSize = !base.Multiline;
}
}
}
I'm experiencing some strange behavior. Let me try to explain, I stripped my code down to the bare minimum and I'm still having the problem. So first of all, I'm using VS2013 with .NET 4.0 and I'm on Windows 8.1.
So I have a custom UserControl with a TextBox that's being used through a ToolStripControlHost, if I focus on this textbox and hit TAB, it only cycles through the controls to the LEFT of this textbox. If I have it focused and hit SHIFT+TAB, it cycles through the buttons to the right of it.
So this is an example of my form. The textbox in the middle is a custom control. My code (as simplified as possible) looks like:
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)]
public class ToolStripTestControl : ToolStripControlHost
{
public ToolStripTestControl() : this(new TestControl()) { }
public ToolStripTestControl(Control c) : base(c) { }
}
public class TestControl : UserControl
{
private TextBox _textBox = new TextBox();
public TestControl()
{
_textBox.Dock = DockStyle.Fill;
this.Controls.Add(_textBox);
}
protected override Size DefaultMinimumSize { get { return new Size(100, 22); } }
}
Simply creating a new WinForms (.NET4) project and following these steps will allow you to replicate the problem:
Add new class file and paste the code above.
Build
Add a ToolStrip to your form
On the ToolStrip add a Button, my custom control, and another Button (through the designer is how I've been doing it)
Run
Once running...
Focus in the custom control
Hit TAB a few times, it should only focus on controls to the left.
Hit SHIFT+TAB a few times and it will only focus to the right.
Does anyone know what the problem is - or how I can fix this? I've been tearing my hair out all day trying to fix this. I finally stripped my code down and I can't seem to get it to work. I even tried overriding much of the OnEnter/OnGotFocus functionality and doing it myself, but that became a nightmare.
Thanks!
Update1: So a few extra tid-bits.
If I change the custom control to inherit from TextBox instead of UserControl, tabbing/focus works as expected.
If I change it to be a Control instead of a UserControl the tabbing works fine, as well, however the focus never gets inside my inner TextBox - the focus seems to be lost (or presumably on the outer parent control but not being passed down to the inner TextBox).
I do see a MS Connect item added that describes this problem from 2009, but this link only seems to work if I'm NOT logged in to Microsoft Connect. Which means, I can't vote on it or comment... http://connect.microsoft.com/VisualStudio/feedback/details/472592/tab-cycling-through-controls-with-usercontrol-on-toolstrip-doesnt-perform-as-expected
The .NET 2.0 ToolStripItem classes have been a major bug factory. They are window-less controls, but reproducing the exact behavior of a Windows window isn't that easy. There is an enormous amount of code underneath, most of it internal so you can't tinker with it. And with quirks when they don't emulate the behavior of a window perfectly. You could call them "airspace" issues, pretty similar to the kind of problems that WPF has.
The airspace issue here is focus, entirely unambiguous for a true window but it needs to be faked for a ToolStripItem. It is actually the item's parent that has the focus, it needs to be emulated for the item. It is the transition that bytes, ToolStrip expects a window-based control to have a reliable Focus property.
Trouble is, your custom host doesn't. It is the inner control that has the focus. This could arguably be blamed on an omission in the ToolStripControlHost class. Probably. The trouble with emulating a window, there's never enough code :)
Anyhoo, fix your problem by adding this sliver of code to your host:
public override bool Focused {
get { return _textBox.Focused; }
}
I have a user control panel that has two buttons on it. Other user controls inherit from this control and set a property to true if the buttons should be visible. Everything runs how I want it to but what I'm looking for is a way to clear these buttons from the designer window for forms where this property is left at false.
looks like:
[DefaultValue(false)]
public bool ShowButtons{
set
{
mShowButtons = value;
UpdateButtons();
}
get
{
return mShowButtons;
}
}
This property shows in the properties window and the buttons are always shown in the designer window. Is there some way to have the designer evaluate this when the property is changed to get the buttons to clear from the inheriting form? I was unable find a designer attribute to do this.
Try adding a get:
bool mShowButtons;
[DefaultValue(false)]
public bool ShowButtons
{
get
{
return mShowButtons;
}
set
{
mShowButtons = value;
UpdateButtons();
}
}
Now when editing your derived class in the Designer, you should be able to see a ShowButtons property in properties window when the derived UserControl is selected. (It will be in the "Misc" section unless you add the appropriate attribute). If you set it there, it should have the appropriate affect in the Designer (Assuming the contents of the UpdateButtons() function work correctly)).
A property must be public and have bot get and set in order to display in the Properties editor window. Once it is, then setting the value in the properties window will "save" that setting for the designed control in the control's resources/implementation.
I use this functionality quite often to specialize derived UserControls, so I know it should work for you (although there may be other issues at play).