How do I implement scrolling for a Custom Control? - c#

How do I implement scrolling for my Custom Control? My control is fully custom drawn and its height is variable, and a part of the control contains a menu so if there are many items in the control, I'll need to be able to put scroll bars there. I've not really been able to find any clues on how to do this. I did see something about ScrollableControl, but I'm still not sure if that's what I need.
Also, how will my control know when it needs to show the scroll bars? Because my control is fully custom drawn so there's no real "controls" in there it's just a bunch of pixels that are drawn onto it so it's not like I can just set AutoScroll to true and I can't do that anyway because it's not the main part of the control that needs scrolling, it's a particular location on the control that will need to have the scrollbars.

If your custom control inherits from the Panel control, you just set the size of the content yourself in the custom control by this setting:
this.AutoScrollMinSize = New Size(yourWidth, yourHeight);
If your control's ClientSize.Height is greater than yourHeight, you won't get any scrollbars. If it's less, then you get a scrollbar.
In your paint method, add this to the beginning:
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.TranslateTransform(this.AutoScrollPosition.X,
this.AutoScrollPosition.Y);
Now everything you paint gets automatically transformed to the scrolling coordinates.

You have two options.
The good new is that it is possible and both are not really hard.
The bad new is that for one option you will have to adapt all your drawing code:
First make your Control, I use a Panel, to have Autoscroll=true;
Then you need to add one dummy control, I use another Panel, maybe like this, far enough to the right and bottom to force the ScrollBars to show:
public Form1()
{
InitializeComponent();
Panel panelDummy = new Panel();
panelDummy.Size = new Size(1,1);
panelDummy.Location = new Point(yourMaxX,yourMaxY);
panel1.Controls.Add(panelDummy);
}
And then you need to adapt your drawing code. Here is how:
private void panel1_Paint(object sender, PaintEventArgs e)
{
int xx = panel1.HorizontalScroll.Value;
int yy = panel1.VerticalScroll.Value;
e.Graphics.FillRectangle(Brushes.Wheat, new Rectangle(11 - xx, 22 - yy, 22, 311));
e.Graphics.FillRectangle(Brushes.RosyBrown, new Rectangle(11 - xx, 280 - yy, 22, 3));
}
private void panel1_Scroll(object sender, ScrollEventArgs e)
{
panel1.Invalidate();
}
I have added an Invalidate to the Scroll event to avoid messed up painting results.
The other option is simpler: Make your control large enough to hold all your drawn controls and the put it inside a Panel with AutoScroll=true; this will delegate the whole scrolling business to the containing Panel.

you can use aPanel with AutoScroll = true. After that, if you put any controls inside it, as long as their size is larger than the size of panel, the panel will automatically show scroll bars.. The trick can be used for any custom control asa well, as long as you place it inside a AutoScroll panel, and size it as large as you need it to be.

I've partially solved this problem by creating a custom control that inherits from Control and that is completely drawn in OnPaint. My solution so far is to use ScrollBarRenderer to draw my scroll buttons, then I define a Rectangle for the scrollable area. I then create an Image to draw my scrollable content onto, and use TranslateTransform Scroll that Image into position before using DrawImage to draw into the Scrollable Content Rectangle that I created. It's showing promise, although I haven't gotten it completely working yet since I have to handle all of the MouseOver and Click events on my own.

Related

C# splitContainer1 resize child control

I have a splitContainer. I want to resize the form inside the splitContaner's panel to scale with when I move the splitter as below. But my form doesn't get redrawn. Any suggestion, thank so much!
private void splitContainer1_SplitterMoved(System.Object sender, System.Windows.Forms.SplitterEventArgs e)
{
// Define what happens when the splitter is no longer moving.
Cursor.Current = System.Windows.Forms.Cursors.Default;
statictisTableDisplayForm1.ClientSize = new Size(statictisTableDisplayForm1.Width, splitContainer1.SplitterDistance);
statictisTableDisplayForm1.Invalidate();
statictisTableDisplayForm1.Refresh();
Refresh();
}
Form supposed to be a top-level control which represents a window of your application. You should not embed forms as controls into other forms (well, unless there is no other option).
Usually, you should not resize and/or move controls manually. There are several layout options which allow automatic resizing of controls when the size of their container changes: Anchor, Dock.
So better create a UserControl which will contain controls and logic of your StatictisTableDisplayForm and place it to SplitContainer panel with Dock set to Fill. That will automatically resize user control when you move the splitter.
Note: if you have to use StatictisTableDisplayForm on it's own too, then just place same user control to this form.

C# controls outside of parent's bounds?

I found out that child controls are not drawn outside of their parent's bounds. So if I have a button that is sticking out of the panels right side it's drawn half way to the part where the panel ends.
2 questions:
Is there any way to have child controls outside of parents bounds (with some trick maybe) ?
What is the reason for that limitation ? (I want to understand that)
Example with 2 buttons: b1 is the big parent, b2 the smaller child:
public Form1()
{
Size = new Size(800, 600);
Button b1 = new Button();
b1.Text = "hello world";
b1.SetBounds(0,0,256,128);
Controls.Add(b1);
Button b2 = new Button();
b2.Text = "abcdefghijklmnopqrstuvwxyz";
b2.SetBounds(192,32,128,64);
b1.Controls.Add(b2);
}
Edit:
It's about controls inside controls not controls inside forms.
No, no fancy UI. I know that I can use regions to create crazy desktop apps. That's not what I want.
Thanks!
A Form is a control as well, so it behaves very much like other controls.
The rendering limitation comes from the fact that controls are windows in the operating system which just represent a rectangular area of the display. A window cannot draw anything outside of its own bounds so if a child control is placed outside those bounds, it'll not be rendered (or only partially, depending on its position).
There's no easy way around this and the whole concept of child controls revolves around the parent controlling what gets drawn. If you want to draw a child control outside the bounds of its parent, remove the "child" from the parent, since the relationship doesn't really apply.
You can come up with some custom rendering code that would work around this but then controls wouldn't be true children of other containers and you'd have to write a fair amount of rendering code to get it working. There may be some situations where this would be the desired behavior but in most cases the default system behavior is better (and means less work for the programmer).
You can get a visual effect of a button being "outside" the form by custom rendering the form (that is, drawing it smaller than it actually is) and then rendering the button in the empty space. This, however, is a lot of work and I'm not sure you'd want to take that route.
If you're trying to do a fancy UI where some controls are shaped like a blob (not like a rectangle) or they appear floating (detached from the "main" UI), similar to some media players, etc.: those programs do the entire custom-rendering thing; their windows are still rectangular windows but during rendering they use transparent images and regions to render the form.
It'd help if you added more detail to your question as to why do you want to do this.
You can't draw outside of the controls/parents bounds. However you can place the control in one of the parents ancestors.
This is some code I use to create a drop down menu when this custom control is toggled:
private void Me_ToggledChanged(System.Object sender, System.EventArgs e)
{
if (this.Parent != null) {
Control topParent = this;
int x = 0;
int y = 0;
while (topParent.Parent != null) {
x += topParent.Left;
y += topParent.Top;
topParent = topParent.Parent;
}
if (this.Toggled) {
if (!topParent.Controls.Contains(dropDownMenu)) {
topParent.Controls.Add(dropDownMenu);
}
dropDownMenu.Location = new Point(x, y);
dropDownMenu.Visible = true;
dropDownMenu.BringToFront();
} else {
if (topParent.Controls.Contains(dropDownMenu)) {
this.Parent.Controls.Remove(dropDownMenu);
}
dropDownMenu.Visible = false;
}
}
}
The dropDownMenu is a control that is setup elsewhere in the constructor of this control.
I start by getting the top most parent, as I check each parent I also get a running x and y value of the controls position relative to the parent.
If this control is toggled then I add it to the top most parent and set its position, otherwise I remove it.
I'm sure there are more complex ways and this may not work in all circumstances however this solution work perfect for me writing a .NET CF menu button.
In WinForms it is not possible to draw outside of a control's bounds.
The easiest way to limit a button to the inside of a panel is to use the Anchor property appropriately. Usually buttons are right aligned. If you want to keep this alignment when the panel resizes, dock the button to the right side. If the button is at the bottom anchor it to the bottom as well, otherwise to the top.
If you want the button to resize with the panel width, then dock it to the left and to the right side at the same time and additionally to the top or bottom.
Image found here: Getting Started with Windows Forms: Anchor the Buttons to the Bottom Right.

Winform: Panel with scroll needs to have their width/height defined?

I've a winform usercontrol, that has a Panel, which is containing some TableLayout(which have also some other user controls).
All my components have a Dock=Fill and Autosize=True properties.
Currently, when I resize the windows, I don't have any scrollbar, the overflow is just no show.
I found that if I set the AutoScrollMinSize of my panel to something, I'm having those scrollbar appearing when I reach the set size.
My problem is that I add/remove elements on runtime, and I've also some things that I display or not depending the configuration. So for me it's very hard to hardcode here a value, either I've scrollbar too soon, or too late.
I'm sure there should be a way to configure my userControl, without having to calculate myself the size, to have the component displaying scrollbar, when the children's content cannot be displayed, do you know how?
Thank you!
You can change the AutoScrollMinSize value on the panel resize event or the form's resize event. That way, it won't be a fixed value and there will be a scrollbar available if the panel's child controls go beyond the panel edges -
private void panel1_Resize(object sender, EventArgs e)
{
panel1.AutoScrollMinSize = new System.Drawing.Size(panel1.Width, panel1.Height);
}

How can I make the rectangles clickable, C#

The code can generate rectangles (Rectangle rectangle) at runtime. The position of rectangles may change according to users' choices.
I want to add code in the method where it creates rectangles to make the rectangles clickable. And after user clicking the rectangle, there will be a new window to show content just like text.
You can use Contains method of the Rectangle object.
private Rectangle _myRectangle;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (this._myRectangle.Contains(e.Location))
{
}
}
Create a label control with the border property and transaparent background(so that it will look as rectangle) and add click event handler for each label you add. It will be good if you create your own Rectangle control by deriving from Label class or you can create your own control(Many other solutions).
I would consider handling the click event on the window itself (or whatever your "background" control is), getting its coordinates, and comparing those to the known locations/sizes of your rectangles.

Removing Windows' ugly Selection marker thing from Splitter in SpitContainer Control

I have a SplitContainer control, and the Splitter in the middle is very ugly. By setting the BackColor of the SplitContainer to (insert color here), then setting the BackColor of Panel1 and Panel2 to white, I can have my splitter looking nice. But by default, Windows puts the selection mark over the Splitter, even before it's selected.
How can I make sure that the selection mark never shows on the Splitter?
I think by "Selection Marker Crap", you mean the fuzzy line that indicates the control is selected. If you don't want this showing up, set some other control to be selected at startup. Something like:
Textbox1.Selected = true;
This should solve your issue if it is just one of it not being selected. However, this will come back if you select the item to resize something. In that case, you could put something in the mouse_up event to move the selection off of the control. That way, the user moves the splitter bar and then when they let go, the selection gets cleared off of the splitter.
Another way would be to make the splitter bar narrow enough that the gray fuzzy line doesn't show up. To do this, you could do the following (tested):
splitContainer1.BorderStyle = BorderStyle.FixedSingle;
splitContainer1.SplitterWidth = 1;
I experienced the same problem, and fixed it by setting TabStop to False in the Properties window for SplitContainer1.
This could annoy people who depend or insist on using the keyboard to operate every aspect of your form, but other than that it will work. Controls inside the SplitContainer will remain tab-able, just not the SplitContainer itself.
This code will move the focus from the splitContainer to TreeView shortly after moved.
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) {
if(this.splitContainer1.CanFocus) {
this.splitContainer1.ActiveControl = this.treeView1;
}
}
You could add an event handler to steal the focus from the container on MouseUp's... It's a little messy but it works. :)
I tried a lot to remove splitter but nothing work. I did some different why we need to use splitter for that we can use picture box control make it width (or) height depend upon your project set 5 or 3 .... after picture box mouse move event write code like...
picturebox property-cursor change the cursor type Hsplit its look like splitter
private void picturebox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)//this for mouse left click its work
{
//write you code here if you use panel set panel height or width it reaches...
Cursor.Position = new Point(e.X, e.Y); // this for mouse cursor position according your //project do some fine tune you will get its work...
}
its work because i tried lot for this and i itself found this method...
I set the TabStop to false and it went away.
Most simple solution i found/made - create a button, select it, and hide it.
All via code. there is not side effects or problems with this, place it in the forms load event.
Button DeSelectButton = new Button();
this.Controls.Add(DeSelectButton);
DeSelectButton.Select();
DeSelectButton.Visible = false;

Categories

Resources