I am creating a custom control with a black background but have some issues with the designer. Truth to be told I have a base control class that inherits from UserControl and then some subclasses that represent the final controls that I will use in my GUI. In that base class I override the BackColor property, add the DefaultValue attribute and set the default value to BackColor in the constructor. As an example my code looks something like this:
public partial class MyControl1 : UserControl
{
public MyControl1()
{
InitializeComponent();
BackColor = Color.Black;
}
[DefaultValue(typeof(Color),"Black")]
public override Color BackColor
{
get
{
return base.BackColor;
}
set
{
base.BackColor = value;
}
}
}
...
public partial class MyControl2 : MyControl1
{
public MyControl2()
{
InitializeComponent();
}
}
The thing is every time I open the designer for MyControl2, BackColor in the properties dialog reverts to System.Drawing.SystemColors.Control and my control is painted grey. If I invoke Reset on BackColor it properly returns to Color.Black, though. Also, the designer doesn't serialize the change to System.Drawing.SystemColors.Control until I make another change to the control.
So, what did I try?
I thought it could be related to BackColor being an ambient property so I tried adding the attribute AmbientValue(false). Of course it didn't work.
I tried erasing the overridden property, leaving only BackColor=Color.Black in the constructor. Surprisingly it fixed the problem with the designer but now resetting the property reverted it to a default value of System.Drawing.SystemColors.Control. Overriding ResetBackColor() didn't solve this last problem.
By the way, I am working under Visual Studio 2010 and my project was created as a .NET 2.0 Windows Forms Application.
I would be glad whether anyone could help me to find whatever is wrong in my code. It is not something that would prevent me from finishing the project but it is pretty annoying. Thank you very much in advance!
This may help - there appears to be some voodoo in the winforms designer (a bit like the XML serializer) that will look for properties which are named a specific way because the DefaultValue doesn't work as you might expect:
The following is an example from another post, I know you are not subclassing a DataGridView, but the principle ought to be the same.
public class MyGridView : DataGridView {
public MyGridView() {
this.BackgroundColor = DefaultBackgroundColor;
}
public new Color BackgroundColor {
get { return base.BackgroundColor; }
set { base.BackgroundColor = value; }
}
private bool ShouldSerializeBackgroundColor() {
return !this.BackgroundColor.Equals(DefaultBackgroundColor);
}
private void ResetBackgroundColor() {
this.BackgroundColor = DefaultBackgroundColor;
}
private static Color DefaultBackgroundColor {
get { return Color.Red; }
}
}
Incidently - this isn't my code - it's some more pure genius from Hans Passant... link to original with a full explanation: https://stackoverflow.com/a/20838280/685341
Related
When I create a checklistbox on a form and populate the y's, g's, etc. get cut-off by the next item.
I've found similar questions answered from years ago (How to change CheckedListBox item vertical space) and tried implementing their fixes but there's not enough details to work it out.
Right now, I go to add -> new item -> class and add a class to my solution. The class looks like this
using System.Windows.Forms;
namespace Test_GUI
{
public sealed class MyListBox : CheckedListBox
{
public MyListBox()
{
ItemHeight = 30;
}
public override int ItemHeight { get; set; }
}
}
And the object appears in my toolbox like
this.
but once I drag and drop it to the form it gives me this
If anyone can point out what I'm doing wrong it would be a great help. This has frustrated me to no end. Thanks!
Unfortunately Visual Studio 2017 (2019?) still doesn't play nicely with 64-bit controls in the Toolbox. This is primarily because VS is a 32-bit application.
The normal solution is to build the project that contains your custom control(s) for the "Any CPU" platform. This might even mean creating a separate Class Library project to house them.
The quick and easy solution (subjective), is to add your custom control(s) to your Form in code and avoid the designer.
If changing ItemHeight is the only creative thing you want to do, I'll offer a workaround that uses the standard CheckedListBox control and reflection.
In your Form constructor, after the line InitializeComponent();, do the following:
var heightField = typeof(CheckedListBox).GetField(
"scaledListItemBordersHeight",
BindingFlags.NonPublic | BindingFlags.Instance
);
var addedHeight = 10; // Some appropriate value, greater than the field's default of 2
heightField.SetValue(clb, addedHeight); // Where "clb" is your CheckedListBox
This requires:
using System.Reflection;
This works because, internally, ItemHeight is a read-only property that returns Font.Height + scaledListItemBordersHeight.
[Updated by OP's comment]
If you need inherit some class, here is step for you.
Add user control to your project
Right click your project -> add -> User control. Visual studio will create 2 files.
(UserControl.cs and UserControl.Designer.cs)
Basically when you create user control, it inherit from UserControl.
So change it to CheckedListBox.
Add : base() at constructor as below.
Remove line to prevent compile error.
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
at UserControl.Designer.cs.
Add some properties, and if you set it public you can see it designtime as below picture.
If parent class need to be initialized, child class should add base() in constructor of your class.
And basic idea for design time action from Here
//Change parent class UserControl --> CheckedListBox.
//public partial class MyListBox : UserControl
public partial class MyListBox : CheckedListBox
{
/*
Add base() in your constructor.
*/
public MyListBox() : base()
{
InitializeComponent();
}
// Add some properties.
private int _ItemHeightOffSet = 0;
public int ItemHeightOffSet {
get { return _ItemHeightOffSet; }
set {
this._ItemHeightOffSet = value;
/*
This will help you adjust height in design time.
When 'DesignMode' is true --> Control loaded at Visual Studio. : Design time.
When 'DesignMode' is false --> Control loaded at debuging or running. : Run time.
*/
if (DesignMode == true)
{
base.ItemHeight = value + ItemHeightOffSet;
base.Refresh();
}
}
}
public override int ItemHeight
{
get {
return base.ItemHeight + ItemHeightOffSet;
}
set
{
base.ItemHeight = value + ItemHeightOffSet;
}
}
}
I am creating a user interface for an iOS app and I am looking for the correct way to create a reusable custom control. I got it generally working when running the app, but at design time setting my "exported" properties has no visible effect in the designer. I think I am doing something fundamentally wrong, so perhaps someone could give me guidance
What I am doing:
I have created a subclass of UIControl.
In the constructor I call an Initialize method.
In the Initialize method, I add several subviews and constraints to layout them within my control
Here is some hollowed out code that shows the above:
[Register("RangedValueSelector"), DesignTimeVisible(true)]
public sealed class RangedValueSelector : UIControl
{
public RangedValueSelector(IntPtr p)
: base(p)
{
Initialize();
}
public RangedValueSelector()
{
Initialize();
}
public int HorizontalButtonSpacing
{
get { return _horizontalButtonSpacing; }
set
{
_horizontalButtonSpacing = value;
}
}
[Export("LabelBoxVerticalInset"), Browsable(true)]
public int LabelBoxVerticalInset
{
get
{
return _labelBoxVerticalInset;
}
set
{
_labelBoxVerticalInset = value;
}
}
private void Initialize()
{
//Code that creates and add Subviews
//Code that creates and add the required constraints, some of which should depend on the design time properties
}
}
So the control works perfectly fine if I set the exported properties via the designer, however they do not have an immediate effect in the designer itself.
What is the suggested way of having design-time settable properties that change the constraint values? I would like to avoid having to recreate all the subviews each time someone in the code or in the designer sets a property.
You are missing constructor with RectangleF which is used by designer.
public RangedValueSelector(RectangleF bounds):base(bounds){}
The rest seems to be correct.
I have created a custom Button for use in my WinForms applicationusing the followng little class
public class MyButton : Button
{
protected override void OnPaint(PaintEventArgs e)
{
this.BackColor = Color.ForestGreen;
base.OnPaint(e);
}
}
I'm simply looking to make my application cusomizeable so that I only need change button colors (and in due course other controls) in one place, and that change is reflected throughout the whole application.
After creating the custom button using the above code I set about replacing all standard System.Windows.Forms.Buttons() with MyNamespace.MyButton(). However, whilst the new buttons all appear changed on the screen, other controls like text boxes (which I have not modified) simply are not rendered on the screen at all. However if I click and drag a window in my application then all of the missing controls suddenly appear.
I have no idea what is causing this. Can anyone advise me please.
You shouldn't be "setting" the backcolor property in a paint event, that can cause a constant refreshing of the screen.
One option is to try setting the property in the constructor instead:
public class MyButton : Button
{
public MyButton() {
this.BackColor = Color.ForestGreen;
}
}
In order to ignore the serialized BackColor property of the control, you can try to change your button class to something like this:
public class MyButton : Button {
private Color myColor = Color.ForestGreen;
public MyButton() {
base.BackColor = myColor;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Color BackColor {
get { return myColor; }
set { // do nothing
}
}
}
This button control will effectively ignore the BackColor property in the designer. If you want to change the color of all of your buttons, you just have to change your myColor value in code and rebuild.
Here is the picture that shows the problem. Take a look at the bottom right corner.
Anyone knows how to get rid of it?
Setting LayoutStyle to VerticalStackWithOverflow fixes it but also centers the items horizontally which I don't want.
I just want a vertical stack like in the pic, but without that black line in the bottom right corner.
Sorry for being late to the party, but the accepted answer didn't work for my needs. The following solution is what I came up with:
Getting rid of the black line
1) Create a custom renderer:
class CustomToolStripProfessionalRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
{
// Don't draw a border
}
}
2) Use the custom renderer:
toolStrip1.Renderer = new CustomToolStripProfessionalRenderer();
Getting rid of the background
The above solution satisfies the need of the original question, but I didn't like the gradient background on the ToolStrip either. I wanted the ToolStrip to be an "invisible" container:
1) Create a custom color table:
class CustomProfessionalColorTable : ProfessionalColorTable
{
public override Color ToolStripGradientBegin
{
get { return SystemColors.Control; }
}
public override Color ToolStripGradientMiddle
{
get { return SystemColors.Control; }
}
public override Color ToolStripGradientEnd
{
get { return SystemColors.Control; }
}
}
2) Use the custom color table:
class CustomToolStripProfessionalRenderer : ToolStripProfessionalRenderer
{
public CustomToolStripProfessionalRenderer()
: base(new CustomProfessionalColorTable())
{
}
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
{
// Don't draw a border
}
}
In the properties bar, set "RenderMode" to "System" or use
.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
Doing this will change the .BackColor to "Control" but you can change that after if you want.
I think your best shot would be to set the RenderMode to System in the properties and leave the layout properties to HorizontalStackWithOverflow.
But that is if you don't mind changing the tooltip paint style.
I'm using .NET C# with standard WinForms, not WPF.
I have this situation.
I'm creating a user control for a month calendar, similar to the .NET one but with a little more functionality.
I have a user control form, that fills with button objects representing dates.
The buttons can be colored with different color depending on their state(selected, mouse over, weekend...)
The way I'd like it to work is extending the button class to accept states, which determine colors, rather than coloring them from the parent (user control) class. There are 10 colors at the moment and I'd really wouldn't like to mess up the user control code with coloring conditions.
Also I would like to select all the colors at design time, using browsable designer properties.
The problem is that the designer shows only properties defined in the user control class, and not its children (buttons).
Is there any workaround for this problem?
So to put it short I want to change colors using internal button properties, and to be able to select them at design time, using designer properties, and not hard coding them manually.
Ok, I'll try to explain trough code:
For example, I have a user control and a button class.
I want to expose Button properties, and make them visible among MyControl properties in designer.
class MyControl : UserControl
{
private MyButton button;
button.ChangeStyle("Selected");
}
class MyButton : Button
{
private Color buttonColor;
public void ChangeStyle(string styleName)
{
if (styleName == "Selected")
this.BackColor = buttonColor;
}
[Browsable(true)]
[Category("Button style")]
public Color ButtonColor
{
get { return buttonColor; }
set { buttonColor = value; }
}
}
This is a simple example. Normally I have 5 different styles including background and foreground color for each of them. So instead of managing colors in MyControl class, I'd like to define them in MyButton class. But the problem this way is that the properties in the MyButton class aren't visible in designer, because it only focuses on MyControl properties.
Btw. ignore the missing constructors and other basic classes stuff in the code example
I can't use:
[Category("Wonder Control")]
public Color ButtonBackColor { get { return button.BackColor; } set { button.BackColor = value; }
because I have 30 buttons in MyControl (days in month), and I can't reference just a single object.
For a property to be visible in the designer, they have to be public properties with a getter and setter - from what you're saying, the properties are only getters. You could also try specifying BrowsableAttribute and BindableAttribute on the properties to coerce the designer to display them...
There are various things you can do here - you could (although it is a bad answer) expose the controls in question on the public interface - but I'm not sure that is a great idea.
Personally, I would just re-expose the properties I am interested in, perhaps putting them into a different [Category(...)] - making sure to have both setters and getters.
A bit like:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
class MyControl : UserControl
{
private Button button;
private Label label;
public MyControl()
{
button = new Button { Dock = DockStyle.Right, Text = "Click me" };
label = new Label { Dock = DockStyle.Left};
Controls.Add(button);
Controls.Add(label);
}
[Category("Wonder Control")]
public string CaptionText { get { return label.Text; } set { label.Text = value; } }
[Category("Wonder Control")]
public Color ButtonBackColor { get { return button.BackColor; } set { button.BackColor = value; } }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (Form form = new Form())
using (MyControl ctrl = new MyControl())
using (PropertyGrid grid = new PropertyGrid())
{
ctrl.ButtonBackColor = Color.Red;
ctrl.CaptionText = "Caption";
ctrl.Dock = DockStyle.Fill;
grid.Dock = DockStyle.Right;
form.Controls.Add(ctrl);
form.Controls.Add(grid);
grid.SelectedObject = ctrl;
Application.Run(form);
}
}
}
If all of the buttons within the control will share the same appearance, why not put the property at the control level and have the property setter propogate any changes to all of the buttons? Also, using 30 individual button controls seems like lot of overhead... have you considered drawing the labels for the days and handling mouse click/hover events to determine when a particular day is clicked?