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?
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 want change the small check-box color beside to item color in checklist in c#
public ColorControl()
{
KnownColor[] colors = Enum.GetValues(typeof(KnownColor)) as KnownColor[];
foreach (var item in colors)
{
this.Items.Add(item.ToString());
}
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
Color ItemColor = Color.FromName(this.Items[e.Index].ToString());
e.Graphics.DrawString(this.Items[e.Index].ToString(),this.Font,new SolidBrush(ItemColor) ,e.Bounds);
base.OnDrawItem(e);}
Not sure if this is worthy of an answer but here is a shot.
As is mentioned in the answer by Cody Grey The checkbox is not really simple to override. Another alternative besides making your own class inheriting from CheckListBox is to make your own control inheriting from Control. You can use buttons with changing background colors for the checkboxes. And add whatever other features you want.
Something like
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace CustomButton
{
public partial class NeatButton : Control
{
//Some globals
private bool _Pressed = false;
private bool _Activated = false;
//you will want to put your code for clicking checkboxes in the Mouse overrides. The OnPaint override is where you decide how the boxes look.
protected override void OnMouseDown(MouseEventArgs e){...}
protected override void OnMouseUp(MouseEventArgs e){...}
protected override void OnPaint(PaintEventArgs pe){...}
//You will want some propeties
public new string Text
{
get { return base.Text; }
set
{
if (value == base.Text)
return;
base.Text = value;
Invalidate(); //Keeps text showing changes in real time
}
}
/// <summary>
/// Works with Pressed to determine if the button should do something when clicked. **Use a property like this for the checkboxes**
/// </summary>
private bool Activated
{
get { return _Activated; }
set
{
if (value == _Activated)
return;
_Activated = value;
Invalidate();
}
}
/// <summary>
/// Works with Activated to determine if the button should do something when clicked
/// </summary>
private bool Pressed
{
get { return _Pressed; }
set
{
if (value == _Pressed)
return;
_Pressed = value;
Invalidate();
}
}
While this is not a complete example it should show a very basic set up for a custom control.
Once the custom control is working then want to build it and grab the exe from where ever you built it. Then in the form you want to use it in you drop the exe in to the project folder. Then in the solution explorer right click on References and then Add reference. Then browes to your exe and check it off and click 'OK'. Save your project and open up the form designer. Right click on the ToolBox and click Choose Items. Find your exe and check it off and hit ok. The Checkbox you made should now appear in the tool box and can be dragged and dropped onto a form and used like any other control.
Good luck.
The checkbox is drawn by the operating system. It is intentionally designed so that it looks like every other checkbox on the screen. This massively helps with usability. Users will not know what they can click on a red box and use it like a checkbox.
If you insist on making your application difficult to use, you will have to owner-draw the control. It will not be easy, CheckedListBox is not designed to support owner-drawing. The DrawItem event is documented as being "not relevant to this class." The control is already owner-drawn by the framework.
At the very least, you will need to create a new class that inherits from CheckedListBox so that you can override the OnDrawItem method.
public class ColorCheckBoxListBox : CheckedListBox
{
protected override void OnDrawItem(DrawItemEventArgs e)
{
// ...
}
}
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
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.
My app root visual is a border panel which stays fixed the whole time.
What do changes is the Child of the border panel : I have made several screens (UserControl) and want to switch between them when certain buttons within each screen are clicked.
I want the transitions between the screens to be visually appealing so that each screen will have two StoryBoards , one for slide in animation and one for slide out animation. ( To make it clearer , The slide in animation of screen1 ,for example, will move screen1's buttons and fields in a specific way into the view area until they reach their final position ).
My goal is to achieve these screen transitions with a Behavior , and apply them to screens with as less code as possible ( Best will be XAML only).
First, I defiend a new Interface which all my app screens will implement :
public interface IScreenWithTransitions
{
void beginOutTransition();
void beginInTransition();
event EventHandler outTransitionEnded;
}
And then screen1 code behind will look like this:
public partial class Screen1 : UserControl, IScreenWithTransitions
{
public Screen1()
{
// Required to initialize variables
InitializeComponent();
}
public void beginOutTransition()
{
AnimationOut.Completed += outTransitionEnded;
AnimationOut.Begin();
}
public void beginInTransition()
{
AnimationIn.Begin();
}
public event EventHandler outTransitionEnded;
}
AnimationOut and AnimationIn are StoryBoards which I created with Blend for each screen.
Now, I want to write a Behavior to manage the transition. This Behavior will act on a Button. It will have two properties. One is OldScreenContainer which is of type Panel and represents the container of the screen we want to remove. Second is NewScreen which is of type IScreenWithTransitions and represents the new screen that we want to put inside the container.
public class SwitchScreensBehavior : Behavior<Button>
{
public static readonly DependencyProperty NewScreenProperty = DependencyProperty.Register("NewScreen", typeof(IScreenWithTransitions), typeof(ChangeButtonTextBehavior), null);
public static readonly DependencyProperty OldScreenContainerProperty = DependencyProperty.Register("OldScreenContainer", typeof(Panel), typeof(ChangeButtonTextBehavior), null);
public IScreenWithTransitions NewScreen
{
get { return (IScreenWithTransitions)GetValue(SwitchScreensBehavior.NewScreenProperty); }
set { SetValue(SwitchScreensBehavior.NewScreenProperty, value); }
}
public Panel OldScreenContainer
{
get { return (Panel)GetValue(SwitchScreensBehavior.OldScreenContainerProperty); }
set { SetValue(SwitchScreensBehavior.OldScreenContainerProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Click += AssociatedObject_Click;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_Click;
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
IScreenWithTransitions oldPage = (IScreenWithTransitions)OldScreenContainer.Children[0];
oldPage.outTransitionEnded += new EventHandler(oldPage_outTransitionEnded);
oldPage.beginOutTransition();
}
void oldPage_outTransitionEnded(object sender, EventArgs e)
{
OldScreenContainer.Children.Clear();
OldScreenContainer.Children.Add((UserControl)NewScreen);
NewScreen.beginInTransition();
}
}
For Example, lets say my root visual is called Border1 and is now containg a child called Screen1 which have a Button called Button1. when the button is clicked i want to switch to Screen2 , so i will put a SwitchScreensBehavior on Button1 with Border1 as OldScreenContainer property and Screen2 as NewScreen property.
This code compiles. I see this Behavior in Blend and can drag it onto a button. But how can i set the two properties of the behavior ? Blend shows me them but I can't figure how to point them to the new screen and to the container ( and what if the new screen is created in runtime) . So I tried to define those properties through the XAML , and again and don't know how to do it. Maybe define them in-code ?
Or maybe this functionality is to big for a Behavior and other elegant solution exists (which relies on as much XMAL as possible) ?
You can bind values of dependency properties in XAML, so what I'd do is to pick the Panel in Blend and bind the other screen via the ViewModel. So in your behavior you'd end up with something like:
OldScreenContainer="{Binding ElementName=MyPanel}" NewScreen="{Binding MyNewScreen}"
This is how you can bind in Blend after clicking on the small rectangle to the right of the property field:
To be able to reference a UserControl you might have to create a ViewModel and set it as DataContext, at least I can't think of any easier / cleaner way to do it at the moment. Actually, wait, do you have those screens added in your XAML? Could you perhaps paste in your main XAML to your question?
Let me know if that's clear or you need any clarification!