how to call Resize Event in C# - c#

I'm trying to create new Button with custom event. It's new for me. I'm trying to call "Resize". I wanna create "switch" like in android.
I'm trying to do this like other existing controls. I've been doing this for 2 days and i still have nothing. I belive that you will able to help me :)
Here is my code:
public abstract class SwitchBase : Control
{
private Button first;
private Button second;
public SwitchBase()
{
InitializeMySwitch();
}
private void InitializeMySwitch()
{
Controls.Add(first = new Button());
Controls.Add(second = new Button());
//first
first.Text = "first";
//second
second.Text = "second";
second.Location = new System.Drawing.Point(first.Location.X + first.Width, first.Location.Y);
}
public delegate void ChangedEventHandler(object source, EventArgs args);
public event ChangedEventHandler Changed;
protected virtual void OnSwitchChanged()
{
if (Changed != null)
Changed(this, EventArgs.Empty);
}
public delegate void ResizeEventHandler(object source, EventArgs args);
public event ResizeEventHandler Resize;
protected virtual void OnResize()
{
Resize(this, EventArgs.Empty);
}
}
public class Switch : SwitchBase
{
public Switch()
{
}
protected override void OnSwitchChanged()
{
base.OnSwitchChanged();
}
protected override void OnResize()
{
base.OnResize();
}
}
In another button I change the size of my switch

From reading your code, I gather that by "call Resize" you mean to raise the event. What you are doing is correct... although it should be noted that by the default event implementation, it will be null if there are no subscribers...
In fact, another thread could be unsubscribing behind your back. Because of that the advice is to take a copy.
You can do that as follows:
var resize = Resize;
if (resize != null)
{
resize(this, EventArgs.Empty)
}
It should be noted that the above code will call the subscribers to the event, but will not cause the cotrol to resize. If what you want is to change the size of your control, then do that:
this.Size = new Size(400, 200);
Or:
this.Width = 400;
this.Height = 200;
Note: I don't know what Control class you are using. In particular, if it were System.Windows.Forms.Control it already has a Resize event, and thus you won't be defining your own. Chances are you are using a Control class that doesn't even have Size or Width and Height.
Edit: System.Web.UI.Control doesn't have Resize, nor Size or Width and Height. But System.Windows.Controls.Control has Width and Height even thought it doesn't have Resize.

Related

How to add functions and properties to multiple controls in C #

I have classes called ButtonDesign, TextBoxDesign, and a few more. Everyone’s realization is exactly the same. I add a number of properties and functions to each control, the question is how can this be achieved without duplicate code, i.e .: is there a way to create only one class with the same attributes - and these attributes will be added to all the controls I want? .
If I create a primary class that inherits from the Control class - then these attributes will only be in the Control class and not in all the controls I want.
This is the code I'm trying to:
class ButtonDesign : Button
{
private Control save_properties = new Control();
public Color OnMouseHoverColor { get; set; }
public ButtonDesign()
{
this.MouseEnter += ButtonDesign_MouseEnter;
this.MouseLeave += ButtonDesign_MouseLeave;
}
private void ButtonDesign_MouseEnter(object sender, EventArgs e)
{
save_properties.BackColor = this.BackColor;
this.BackColor = this.OnMouseHvetColor;
}
private void ButtonDesign_MouseLeave(object sender, EventArgs e)
{
this.BackColor = save_properties.BackColor;
}
}
class TextBoxDesign : TextBox
{
private Control save_properties = new Control();
public Color OnMouseHoverColor { get; set; }
public TextBoxDesign()
{
this.MouseEnter += ButtonDesign_MouseEnter;
this.MouseLeave += ButtonDesign_MouseLeave;
}
private void TextBoxDesign_MouseEnter(object sender, EventArgs e)
{
save_properties.BackColor = this.BackColor;
this.BackColor = this.OnMouseHvetColor;
}
private void TextBoxDesign_MouseLeave(object sender, EventArgs e)
{
this.BackColor = save_properties.BackColor;
}
}
Congratulations, you've encountered the diamond inheritance problem. C# doesn't support this scenario, so what you're attempting to do is unfortunately not possible.
Your only options are:
Create a subclass of Control and make your custom controls inherit from it. This means you have to recreate the behaviour of the standard WinForms controls in your subclasses, which is painful at best.
public class MyControl : Control {}
public class MyButton : MyControl {}
Create an interface that your common controls implement. Have the interface implementations delegate to a shared library that does what needs to be done:
public interface IMyControl
{
void MySharedOperation();
}
public class MyButton : Button, IMyControl
{
public void MySharedOperation()
=> MySharedOperationHandler.MySharedOperation(this);
}
public static class MySharedOperationHandler
{
public static void MySharedOperation(Control control) {}
}
You'll end up with a fair amount of method implementations that do nothing more than delegate, but IMO this is far better than reinventing the control wheel as in the previous option.
If you want this, your class ButtonDesign is not a Button, it has a Button and a Layout. Similarly your class TextBoxDesign has a TextBox and a Layout.
In other words: don't use inheritance, use aggregation!
Every property that are both in Buttons, TextBoxes, and other Controls that have the properties that you want to change for all items in one statement, create a class that contains all these properties, with the proper events.
For every property you need a private member, a public get/set property and an event that will be raised when the property changes. Something like this:
class Layout
{
private Color backColor; // TODO: give proper initial values
private Color foreColor;
... // other properties that you want to change for all Controls
// Events:
public event EventHander BackColorChanged;
public event EventHandler ForeColorChanged;
... // etc
// Event raisers:
protected virtual void OnBackColorChanged()
{
this.BackColorChanged?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnForColorChanged()
{
this.ForeColorChanged?.Invoke(this, EventArgs.Empty);
}
...
// Properties:
public Color BackColor
{
get => this.backColor;
set => if (this.BackColor != value) this.OnBackColorChanged();
}
public Color ForeColor ...
public Size Size ...
}
Your DesignButton will be a UserControl that has a Docked Button and a Layout. The constructor subscribes to the events. When raise the corresponding property on the button is set.
Use the visual studio designer to create the UserControl. Code will be similar to the following:
class MyButton : UserControl
{
private Button button; // visual studio designer will create this
private Layout layout;
public Layout Layout
{
get => this.layout;
set => if (this.Layout != value) this.ChangeLayout;
}
// you can't set the design properties. Only get.
protected Button Button => this.button;
Color BackColor
{
public get => this.Button.BackColor;
private set => this.Button.BackColor = value;
}
// etc for ForeColor, Text, ...
protected virtual void ChangeLayout(Layout newLayout)
{
// TODO: if there is an old Layout: desubscribe from all events from old Layout
this.layout = newLayout;
// subscribe to all events:
this.layout.BackColorChanged += this.BackColor_Changed;
this.layout.forColorChanged += this.ForColor_Changed;
...
this.BackColor = this.BackColor;
// TODO: if desired, for completeness add an event: LayoutChanged
}
Now whenever Layout raises event BackColorChanged you handle this event and assign the value to the Button. You'll get the gist.
Do something similar for TextBoxes, ComboBoxes, etc
Usage:
Layout commonLayout = new Layout
{
BackColor = Color.Yellow,
ForeColor = color.Black,
...
};
MyButton button1 = new MyButton
{
Layout = commonLayout,
};
MyTextBox textBox1 = new MyTextBox
{
Layout = commonLayout,
}
MyComboBox comboBox1 = ...
// Change the backgroundColor for all items:
commonLayout.BackColor = Color.Red;
If you have a lot of properties, consider to use generic classes
class LayoutProperty<T>
{
private T propertyValue;
public event eventHandler PropertyValueChanged
protected void OnPropertyValueChanged()
{
this.PropertyValueChanged?.Invoke(this, EventArgs.Empty);
}
public T PropertyValue
{
get => this.propertyValue;
set => if (this.PropertyValue != value) this.OnPropertyValueChanged();
}
}
class Layout
{
private PropertyValue<Color> backColor;
private PropertyValue<Color> foreColor;
// etc, see above for subscribtion and raising events.
And for all Controls:
public MyControl
{
private Control control;
private Layout layout;
// etc, see above for the event handling.
}
public MyButton : MyControl {Control = new Button()}
public MyTextBox : MyControl {Control = new Textbox()}

How to prevent flickering in WinForms when quickly updating a value?

I have a simple Winforms GUI which has a TrackBar in a GroupBox its title I'm using to display the value of the contained TrackBar:
private void trackBar1_Scroll(object sender, EventArgs e)
{
UpdateTrackBarPositionText();
}
private void Form1_Load(object sender, EventArgs e)
{
UpdateTrackBarPositionText();
}
private void UpdateTrackBarPositionText()
{
groupBox1.Text = ORIGINAL_TRACKBAR_TEXT + qualitySelector.Value.ToString();
}
The problem is that when scrubbing over the bar, both it and the label of the group are flickering, especially when scrubbing quickly. Also CPU usage is fairly high for such a simple action. How can I improve this implementation? I want to update the value in real time, not just after letting go, so that the users sees the value while selecting it. DoubleBuffered = true; does nothing.
EDIT:
I used reflection at instantiation to set the GroupBox and the bar to double buffered. That helped with the Box, but the slider still flickers :(
Do you have Double Buffering set. If not start there and then other methods as needed.
This is a classical use case for a timer. Make it update the label at, say, 10 times per second. Any more than that is really an overkill because nobody can read it that fast anyway.
You can have the timer permanently enabled, or (as an optimization) you can enable it at trackBar1_Scroll and disable it in the timer itself, if the value hasn't changed for the past few ticks. In fact, if the value hasn't changed since the last update, don't update the label either (might save some extra flickering).
This does not address this question, but it creates a workaround with a custom control that does the same thing.
public partial class SliderControl : Control
{
public event EventHandler ValueChanged;
public int MinValue { get; set; }
public int MaxValue { get; set; }
public int Value { get; set; }
public SliderControl()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint|ControlStyles.OptimizedDoubleBuffer, true);
this.MinValue=0;
this.MaxValue=100;
this.Value=50;
this.Text="Value";
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
SetBoundsCore(Left, Top, Width, 32, BoundsSpecified.Height);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
pe.Graphics.DrawRectangle(Pens.Black, 0, 0, Width-1, Height-16);
using (var gp = new GraphicsPath())
{
var rect = new Rectangle(0, 0, Value*(Width-1)/MaxValue-1, Height-16);
gp.AddRectangle(rect);
using (var br = new LinearGradientBrush(rect, Color.SteelBlue, Color.LightBlue, LinearGradientMode.Horizontal))
{
pe.Graphics.FillPath(br, gp);
pe.Graphics.DrawPath(Pens.DarkBlue, gp);
}
}
var text = $"{this.Text} = {this.Value}";
var sz = pe.Graphics.MeasureString(text, SystemFonts.SmallCaptionFont);
pe.Graphics.DrawString(text, SystemFonts.SmallCaptionFont, Brushes.Black, Width/2-sz.Width/2, Height-16);
}
private void SetClickValue(Point click_point)
{
int x = (click_point.X+1)*MaxValue/Width;
this.Value=x;
this.Refresh();
this.ValueChanged?.Invoke(this, new EventArgs());
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
if (e.Button==MouseButtons.Left)
{
SetClickValue(e.Location);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Button==MouseButtons.Left)
{
SetClickValue(e.Location);
}
}
}

RichTextBox blocks DragDrop event

I have DragDrop and DragEnter events on my SplitContainer.Panel:
splitContainer.Panel.DragDrop += new System.Windows.Forms.DragEventHandler(this.splitContainerPanelDragDrop);
splitContainer.Panel.DragEnter += new System.Windows.Forms.DragEventHandler(this.splitContainerPanelDragEnter);
It works perfect with every control inside SplitContainer.Panel except RichTextBox controls.
How it looks like:
So DragDrop/DragEnter works perfectly in every control inside SplitContainer except controls which is marked yellow color.
What I tried:
1) Set
RichTextBox.AllowDrop = false;
So I even DragEnter is unavailable with "action is not allowed" cursor.
2) Set
RichTextBox.AllowDrop = true;
After this cursor is ok, but it doesnt work because expects additional DragEventHandler in other case it doesnt work.
3) Set
RichTextBox.EnableAutoDragDrop=false;
RichTextBox.AllowDrop=true;
Same result as 2) variant.
I dont want to set DragDrop/DragEnter event for every RichTextBox inside SplitContainer because inside FlowLayoutPanel they are created dynamically.
The question is: is there any method like e.PreventDefault analog in C#? Or what can I do except setting events for every RichTextBox to make it work?
This worked for me
I created 2 custom controls
Custom SplitControl
public partial class SplitControlCustom : SplitContainer
{
public SplitControlCustom()
{
InitializeComponent();
}
public void ForceDrageDrop(DragEventArgs eventArgs)
{
OnDragDrop(eventArgs);
}
public void ForceDragEnter(DragEventArgs eventArgs)
{
OnDragEnter(eventArgs);
}
}
Custom RichTextBox
public partial class RichTextBoxCustom : RichTextBox
{
public RichTextBoxCustom()
{
InitializeComponent();
this.AllowDrop = true;
}
protected override void OnDragEnter(DragEventArgs drgevent)
{
SplitControlCustom parentSplitControl = Parent.Parent as SplitControlCustom;
if (parentSplitControl != null)
{
parentSplitControl.ForceDragEnter(drgevent);
}
}
protected override void OnDragDrop(DragEventArgs drgevent)
{
SplitControlCustom parentSplitControl = Parent.Parent as SplitControlCustom;
if (parentSplitControl != null)
{
parentSplitControl.ForceDrageDrop(drgevent);
}
}
}
Please let me know if it worked
I don't see how you can make this work directly. But then, since you are already willing to add a few lines of code while generating the controls, why not add the necessary events via a few lines of Lambda..:
Let's assume you have just created a RichTextBox and are ready to add it to some Controls collection..:
RichTextBox richTextBox = new RichTextBox ();
...
richTextBox.AllowDrop = true;
richTextBox.DragEnter += (ss, ee) => { ee.Effect = DragDropEffects.Copy; };
richTextBox.DragOver += (ss, ee) => { ee.Effect = DragDropEffects.Copy; };
richTextBox.DragDrop += (ss, ee)
=> { splitContainer.Panel_DragDrop(splitContainer.Panel, ee); };
The first two lambdas set the effect to copy without any checks; of course you will want to add those and pick the appropriate effect.
The third lambda passes the DragEventArgs on the the DragDrop event of the containing panel, so now the RTB is actually 'D&D-through' ..
Just create a custom RichTextBox and override it's DragDrop Events.
public class CustomRichTextBox : RichTextBox
{
#region Methods
#region Overrides
protected override void OnDragEnter(DragEventArgs e)
{
// base.OnDragEnter(e);
}
protected override void OnDragOver(DragEventArgs e)
{
// base.OnDragOver(e);
}
protected override void OnDragLeave(DragEventArgs e)
{
// base.OnDragLeave(e);
}
protected override void OnDrop(DragEventArgs e)
{
// base.OnDrop(e);
}
#endregion
#endregion
}
For some reason RichTextBoxes seem to handle all DragDrop events by default.
In WPF the events will propagate till it gets to the control that expects these events. I'm not sure about WinForms though.
This is what resolved this issue for me.
I had these two events defined, which should have been good enough
MyRichTextBox.DragEnter += MyRichTextBox_DragEnter;
MyRichTextBox.DragDrop += MyRichTextBox_DragDrop;
I found that this one is also apparently needed when using a RichTextBox embedded in
certain controls.
MyRichTextBox.DragOver += MyRichTextBox_DragOver;
private void MyRichTextBox_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}

Is there any way to hide the arrow on a ToolStripMenuItem?

Is there any way to hide the arrow on a ToolStripMenuItem? The arrow is enclosed in the red square.
I've found this is very helpful, you can create your own custom ToolStripRenderer inherits from ToolStripProfessionalRenderer, like this:
public class CustomToolStripRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{
e.ArrowRectangle = Rectangle.Empty;//Don't draw arrow
base.OnRenderArrow(e);
}
}
//and update the Renderer property of your MenuStrip
menuStrip1.Renderer = new CustomToolStripRenderer();
UPDATE
For your requirement, there are some ways to do but I think this is a good way:
public class CustomToolStripRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{
if (RenderArrow != null) RenderArrow(this, e);
base.OnRenderArrow(e);
}
public new event ToolStripArrowRenderEventHandler RenderArrow;//This will hide the default RenderArrow event which can't help you change the e argument because the default is fired after the Arrow is rendered.
}
//Now you have to create your own List<ToolStripItem> to contain all the items whose arrows should not be rendered
List<ToolStripItem> ItemsWithoutArrow = new List<ToolStripItem>();
//Add a method to add an item to that list
private void SuppressDrawArrow(ToolStripItem item)
{
if (!ItemsWithoutArrow.Contains(item)) ItemsWithoutArrow.Add(item);
}
//Assign custom ToolStripRenderer for your MenuStrip
menuStrip1.Renderer = new CustomToolStripRenderer();
//Now add a RenderArrow event handler, this RenderArrow event is the new we created in the class CustomToolStripRenderer
((CustomToolStripRenderer)menuStrip1.Renderer).RenderArrow += (s, e) =>
{
if(ItemsWithoutArrow.Contains(e.Item)) e.ArrowRectangle = Rectangle.Empty;
};
//Add some item to the ItemsWithoutArrow to test
SuppressDrawArrow(item1ToolStripMenuItem);
Another solution (I like many solutions to a problem :)
public class CustomToolStripRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{
if(!itemsWithoutArrow.Contains(e.Item)) base.OnRenderArrow(e);
}
public void SuppressDrawArrow(ToolStripItem item){
if (!itemsWithoutArrow.Contains(item)) itemsWithoutArrow.Add(item);
}
public void AllowDrawArrow(ToolStripItem item){
itemsWithoutArrow.Remove(item);
}
private List<ToolStripItem> itemsWithoutArrow = new List<ToolStripItem>();
}
//Use in code
CustomToolStripRenderer renderer = new CustomToolStripRenderer();
renderer.SuppressDrawArrow(item1ToolStripMenuItem);
menuStrip1.Renderer = renderer;
//This solution fits your requirement (draw or don't draw arrow) but if you also want to change such as ArrowColor, the previous solution would be better.
I've found that we can render it freely with many options. That's great :)

.NET Collections and Accessing Object Methods

I'm completely new to GUI programming and need a little help with a list of pictureboxes.
The idea is that I have a list of pictureboxes. When a user clicks on one I want to (for example) change the BorderStyle property of the one selected to be Fixed3D, but change the remaining collection borders to FixedSingle (or something like that). What's the proper way to do something like this? I guess the bigger picture is how do I get a method of one class to call a method of another without having any information about it?
class myPicture
{
private int _pictureNumber;
private PictureBox _box;
public myPicture(int order)
{
_box = new List<PictureBox>();
_box.Click += new System.EventHandler(box_click);
_pictureNumber = order;
}
public void setBorderStyle(BorderStyle bs)
{
_box.BorderStyle = bs;
}
public void box_click(object sender, EventArgs e)
{
//here I'd like to call the set_borders from myPicturesContainer, but I don't know or have any knowledge of the instantiation
}
}
class myPicturesContainer
{
private List<myPicture> _myPictures;
//constructor and other code omitted, not really needed...
public void set_borders(int i)
{
foreach(myPicture mp in _MyPictures)
mp.setBorderStyle(BorderStyle.FixedSingle);
if(i>0 && _MyPictures.Count>=i)
_MyPictures[i].setBorderStyle(BorderStyle.Fixed3d);
}
}
You will need to create a Clicked event in your myPicture class and raise that event when it is clicked. Then you will need to attach to this event in your myPicturesContainer for each instance of myPicture that you have.
Here is a very simple example of what I mean:
class myPicture
{
public event Action<Int32> Clicked = delegate { };
private int _pictureNumber;
public void box_click(object sender, EventArgs e)
{
this.Clicked(this._pictureNumber);
}
}
class myPicturesContainer
{
private List<myPicture> _myPictures;
public void set_borders(int i)
{
foreach (myPicture mp in _myPictures)
{
mp.Clicked += pictureClick;
}
}
void pictureClick(Int32 pictureId)
{
// This method will be called and the pictureId
// of the clicked picture will be passed in
}
}

Categories