AutoScroll mode doesn't hide scrollbar properly - c#

I have one Panel and when I click on add Button I add one Control under other added controls. When I click on another Button I remove Control which was added as last.
This works fine. On that panel I have AutoScroll setting set to True and when I add more controls it properly appears and I can use it. When I remove some controls Panel properly hides ScrollBar ONLY if the "animation" on that ScrollBar doesn't run at that time.
If no animation is running on that ScrollBar it disapears properly - doesn't matter if you have mouse over it or not.
If you have mouse over the ScrollBar and quickly move over the remove Button and click before ScrollBars animation is finished the Control is removed, but the inactive ScrollBar is still there. In Buttons click handler I tried to call Invalidate, Update and Refresh methods over the Panel but nothing works.
I tested this only on Windows 7.
If you don't know what I mean please try to look at this short video (20s without sound): http://youtu.be/-0EfRXrGbuc

You forgot to post mcve. So here is one (add panel and two buttons):
private void button1_Click(object sender, EventArgs e)
{
panel1.Controls.Add(new Button() { Top = panel1.Controls.Count * 30 });
}
private void button2_Click(object sender, EventArgs e)
{
if (panel1.Controls.Count > 0)
panel1.Controls.RemoveAt(panel1.Controls.Count - 1);
panel1.Refresh();
}
I am able to reproduce the problem
it's winforms, baby (c).
Possible workaround is to call Refresh() by using e.g. Timer or some of mouse events (it will not prevent issue, but use will easily fix it by e.g. moving mouse inside panel1) or you can postpone the possibility of deleting buttons itself for a short time after panel1.MouseLeave. All this kind of workarounds.

I hoped that there is some better way, but now I don't see any, so based on answer from Sinatr I decided to use Timer and cobined it with checking pixel Color to determine if the ScrollBar is still visible.
private Timer _timer = new Timer {Interval = 500};
public Form1()
{
InitializeComponent();
_timer.Tick += TimerOnTick;
}
private void button2_Click(object sender, EventArgs e)
{
if (panel1.Controls.Count > 0)
{
var wasVisible = panel1.VerticalScroll.Visible;
panel1.Controls.RemoveAt(panel1.Controls.Count - 1);
buttons.RemoveAt(buttons.Count - 1);
if (wasVisible != panel1.VerticalScroll.Visible)
{
_timer.Start();
}
}
}
private bool IsBackgroundColor()
{
var point = panel1.Location;
point.Offset(panel1.Width - 9, panel1.Height - 11);
point = PointToScreen(point);
Image imgScreen = new Bitmap(1, 1);
using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format32bppArgb))
using (Graphics g = Graphics.FromImage(bmp))
using (Graphics gr = Graphics.FromImage(imgScreen))
{
g.CopyFromScreen(point, new Point(0, 0), new Size(1, 1));
gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
gr.DrawImage(bmp, new Rectangle(0, 0, 1, 1));
var color = bmp.GetPixel(0, 0);
return color.R == panel1.BackColor.R && color.G == panel1.BackColor.G && color.B == panel1.BackColor.B;
}
}
private void TimerOnTick(object sender, EventArgs eventArgs)
{
if (!IsBackgroundColor() && !panel1.VerticalScroll.Visible)
{
panel1.Refresh();
}
else
{
_timer.Stop();
}
}
I was not able to use Panel.DrawToBitmap because it doesn't draw ScrollBars. I also start the Timer only when the ScrollBar was visible and now it shouldn't be.
It is important to mention that the pixel Color checking is possible only if you know the Color which should be there if the ScrollBar is hidden. It is not necessary have to be Panel.BackColor.

Related

How to make label reappear as soon as it's going out of panel width

I want to make a moving label seem nicer and smoother than just reappearing the whole thing to the left after it has all gone out of panel width .For example label 'Hello' , as soon as 'lo' goes out of bounds in the right I want it to reappear on the left. Is there any possible solution to this ?
Here's the code I have for the label now .
private void timer2_Tick(object sender, EventArgs e)
{
label5.Location = new Point(label5.Location.X + 3, label5.Location.Y);
if (label5.Location.X > this.Width)
{
label5.Location = new Point(0 - label5.Width, label5.Location.Y);
}
}
Try this, using a Label (here, named lblMarquee and a System.Windows.Forms.Timer).
The scrolling time is regulated by both the Timer.Interval and a float Field (marqueeStep).
The Timer.Tick event just calls lblMarquee.Invalidate(), causing the Label control to repaint itself.
When the scrolling text, in relation to its current position, goes beyond the limits of the Label.ClientRectangle, the section of the text which is not visible anymore is painted at start of the Label.ClientArea:
System.Windows.Forms.Timer marqueeTimer = new System.Windows.Forms.Timer();
string marqueeText = string.Empty;
float marqueePosition = 0f;
float marqueeStep = 4f;
private void form1_Load(object sender, EventArgs e)
{
marqueeText = lblMarquee.Text;
lblMarquee.Text = string.Empty;
marqueeTimer.Tick += (s, ev) => { this.lblMarquee.Invalidate(); };
marqueeTimer.Interval = 100;
marqueeTimer.Start();
}
private void lblMarquee_Paint(object sender, PaintEventArgs e)
{
var marquee = sender as Label;
SizeF stringSize = e.Graphics.MeasureString(marqueeText, marquee.Font, -1, marqueeFormat);
PointF stringLocation = new PointF(marqueePosition, (marquee.Height - stringSize.Height) / 2);
stringLength = marquee.ClientRectangle.Width - stringLocation.X;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
e.Graphics.DrawString(marqueeText, marquee.Font, Brushes.Black, stringLocation, marqueeFormat);
if (marqueePosition >= marquee.ClientRectangle.Width) marqueePosition = 0f;
if (stringSize.Width + stringLocation.X > marquee.ClientRectangle.Width) {
PointF partialStringPos = new PointF(-stringLength, (marquee.Height - stringSize.Height) / 2);
e.Graphics.DrawString(marqueeText, marquee.Font, Brushes.Black, partialStringPos, marqueeFormat);
}
marqueePosition += marqueeStep;
}
A couple of other implementations you might find useful:
How to follow the end of a text in a TextBox with no NoWrap
How to draw a string on two not adjacent areas
You need to have two label controls to do this, but it's not really that difficult. First, create a backup label and set it's properties to look like label5:
// A backup label for our scrolling label5
private Label label5_backup;
private void Form1_Load(object sender, EventArgs e)
{
label5.Text = "This is a scrolling label!";
// Set label5_backup to look like label5
label5_backup = new Label
{
Size = label5.Size,
Text = label5.Text,
Top = label5.Top,
Visible = false
};
Controls.Add(label5_backup);
timer2.Interval = 1;
timer2.Start();
}
Then, in the Tick event, as soon as our label5 starts to leave the client rectangle, set our backup label to the proper distance from the left of the form so that it starts to appear on the other side. And as soon as label5 is completely off the form, set it's location to match the backup label and then hide the backup label again.
Note that you can just set the Left property instead of creating a new Location point each time, which simplifies the code a little:
private void timer2_Tick(object sender, EventArgs e)
{
label5.Left++;
// If label5 starts to go off the right, show our backup on the left side of the form
if (label5.Right > ClientRectangle.Width)
{
label5_backup.Left = label5.Right - ClientRectangle.Width - label5.Width;
label5_backup.Visible = true;
}
// If label5 is all the way off the form now, set it's location to match the backup
if (label5.Left > ClientRectangle.Width)
{
label5.Location = label5_backup.Location;
label5_backup.Visible = false;
}
}
Also, if you want to make the scrolling smoother, only increment the Left by 1 each time and reduce the timer2.Interval to a third of what it was before (unless it's already at 1).

Flickering Tab Control with MouseMove Determining What To Draw

I have been researching this all day, (Go ahead and laugh lol) and I don't see any solutions to the age old Forms problem of flickering controls. My control is a TabControl and I am using DrawMode OwnerDrawFixed. I am hooking the following events. In short I am creating a TabControl with closable "X" buttons that are 12x12 png resources. The close buttons are all gray but if I mouse over one it should use a different image (a red X).
MouseDown: Loops all TabPages and checks if I have clicked on a rectangle where I am drawing my close button image.
MouseLeave: I need to Invalidate when I leave the TabControl to ensure everything is drawn correctly
MouseMove: Loops all TabPages and checks if I have moused over a rectangle where I am drawing my close button image. If I am mousing over then I save the tab page index so my paint can change the image used for the close button.
DrawItem: Here I simply draw the image
Things I have tested but no luck...
Making my own TabControl class which inherits TabControl and in the constructor I SetStyles for OptimizedDoubleBuffering To true (I set the other suggested flags to true)
I tried overriding CreateParams so I could or this value... createParams.ExStyle |= 0x00000020; (I have no idea what this does but read a user suggested to do this.
Setting the form DoubleBuffered (does nothing)
Anyways, I can't think what to do and I have read about this for awhile.
Here is my code for all events. I just want to have close buttons on my tabs that get highlighted when I mouse over them. Thanks.
private int mousedOver = -1;//indicates which close button is moused over
private void tabControl_DrawItem(object sender, DrawItemEventArgs e)
{
e.Graphics.DrawImage(e.Index == mousedOver ? Resources.redX : Resources.grayX, e.Bounds.Right - 15, e.Bounds.Top + 4);
}
private void tabControl_MouseDown(object sender, MouseEventArgs e)
{
TabControl tc = sender as TabControl;
if (tc.TabCount == 1) return;
for (int i = 0; i < tc.TabPages.Count; i++)
{
Rectangle r = tc.GetTabRect(i);
Rectangle closeButton = new Rectangle(r.Right - 15, r.Top + 4, 12, 12);
if (closeButton.Contains(e.Location))
{
TabPage tp = tc.TabPages[i];
tc.TabPages.Remove(tp);
tp.Dispose();
break;
}
}
}
private void tabControl_MouseMove(object sender, MouseEventArgs e)
{
TabControl tc = sender as TabControl;
for (int i = 0; i < tc.TabPages.Count; i++)
{
Rectangle r = tc.GetTabRect(i);
Rectangle closeButton = new Rectangle(r.Right - 15, r.Top + 4, 12, 12);
if (closeButton.Contains(e.Location))
{
mousedOver = i;
tc.Invalidate();
return;
}
}
mousedOver = -1;
tc.Invalidate();
}
private void tabControl_MouseLeave(object sender, EventArgs e)
{
TabControl tc = sender as TabControl;
mousedOver = -1;
tc.Invalidate();
}
It does look like you are invalidating too often. Try filtering it so that you only invalidate it when the control needs to be re-painted:
private void tabControl_MouseMove(object sender, MouseEventArgs e) {
TabControl tc = sender as TabControl;
for (int i = 0; i < tc.TabPages.Count; i++) {
Rectangle r = tc.GetTabRect(i);
Rectangle closeButton = new Rectangle(r.Right - 15, r.Top + 4, 12, 12);
if (closeButton.Contains(e.Location)) {
if (mousedOver != i) {
mousedOver = i;
tc.Invalidate(r);
}
} else if (mousedOver == i) {
int oldMouse = mousedOver;
mousedOver = -1;
tc.Invalidate(tc.GetTabRect(oldMouse));
}
}
}
I would keep the CreateParams override, but as a native windows control, you can probably never totally eliminate some flicker.
You could also try setting the DoubleBuffered property of the control, by doing
Control.DoubleBuffered = true;
I know that this works with DataGridViews, ListViews, Forms and Panels.
Documentation can be found on MSDN.

How can i draw a rectangle around a label control?

I added a label control to form1 designer and assign some text to it.
Then i did label mouse click event:
private void label5_MouseClick(object sender, MouseEventArgs e)
{
DrawRectangleOnLabel = true;
label5.Invalidate();
}
And the label paint event:
private void label5_Paint(object sender, PaintEventArgs e)
{
if (DrawRectangleOnLabel == true)
{
e.Graphics.DrawRectangle(Pens.Red, 0, 0, label5.Width, label5.Height);
}
}
But what i see when i click on the label is half rectangle only the Left and Top the right and bottom of the rectangle not exist/show.
This is because the rectangle is drawn with a pen width of 1 and the right and bottom portions fall outside the bounds of the label. Just make it one pixel smaller :
e.Graphics.DrawRectangle(Pens.Red, 0, 0, label1.Width - 1, label1.Height - 1);
Alternatively, you can use the ControlPaint method instead :
ControlPaint.DrawBorder(e.Graphics, label1.DisplayRectangle,
Color.Red, ButtonBorderStyle.Solid);
This gives you access to various other ButtonBorderStyles (dashed, dotted, inset, outset).
Why don't you try BorderStyle property on MouseClick event. And assign single event for all labels.
label1.MouseClick += new EventHandler(this.AllLable_MouseClick);
label2.MouseClick += new EventHandler(this.AllLable_MouseClick);
label3.MouseClick += new EventHandler(this.AllLable_MouseClick);
private void AllLable_MouseClick(object sender, MouseEventArgs e)
{
Label lbl = (Label)sender;
if (lbl.BorderStyle == BorderStyle.FixedSingle)
lbl.BorderStyle = BorderStyle.None
else
lbl.BorderStyle = BorderStyle.FixedSingle
}
For a simple "box" just add a Forms.Panel and place your controls on it. Then set the Panel border to FixedSingle.

Width of the form can not be less than 140 pixels. Why?

I created default Windows Forms Application project in Visual Studio 2012. When I run program then saw that width of form can not be less than 140 pixels. Why? And how to overcome this strange restriction?
I was looking for a solution and MinimumSize(0,0) didn't had any effect. Figured out, that MinimumSize set to (1,1) actually fixed the problem and after showing my form it was properly sized smaller than 140px.
Column click event on (ListView)_csvLv that should trigger a popup dialog:
var topAnchor = _csvLv.PointToScreen(new Point(
_csvLv
.Columns
.OfType<ColumnHeader>()
.Where(c => c.DisplayIndex < e.Column)
.Sum(c => c.Width),
0));
Left = topAnchor.X;
Top = topAnchor.Y;
MinimumSize = new Size(1,1);
ClientSize = new Size(_csvLv.Columns[e.Column].Width, 100);
ShowDialog();
Users wouldn't be able to use the window's minimize, maximize, and close buttons at that top. I don't believe you can change that behaviour with the Sizable FormBorderStyle. It's a usability thing.
If you remove the border, by setting it to None for example, you can set it to whatever you want programmatically by doing:
form.Width = [...];
You can resize further forms with border types: None, FixedToolWindow, and SizableToolWindow. The ToolWindows won't let you go below a certain amount as well, but None will let you do anything above 2px. You could set it to some value below that, without getting an exception, but it won't do anything.
Try this.
Autosize no
AutosizeMode growOnly
FormBorderStyle SizableToolWindow <== this one did it
I still can move the form and resize it (width) less tan 112
I never use formborders.. I always like to go with FormBorderstyle.None
To resize, you have to add some code.
Put a pictureBox, add a grip img in it and place it in the corner.
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += new MouseEventHandler(Form1_MouseDown);
pictureBox1.MouseMove += new MouseEventHandler(Form1_MouseMove);
pictureBox1.MouseUp += new MouseEventHandler(Form1_MouseUp);
}
void Form1_MouseUp(object sender, MouseEventArgs e)
{
isHolding = false;
}
void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (isHolding)
{
int diffX = this.Width - pictureBox1.Left;
int diffY = this.Height - pictureBox1.Top;
pictureBox1.Left += e.X - curX;
pictureBox1.Top += e.Y - curY;
this.Width = pictureBox1.Left + diffX;
this.Height = pictureBox1.Top + diffY;
}
}
void Form1_MouseDown(object sender, MouseEventArgs e)
{
isHolding = true;
curX = e.X;
curY = e.Y;
}
int curX = 0, curY = 0;
bool isHolding = false;

Button on drawn object

I'm trying to add a button so that it will appear on top of a screen that I'm drawing, in an XNA/Silverlight Windows phone game.
Currently the map is drawing over it, so the button appears invisible. Does anyone know how I could fix this?
The Button switchScreen is created, but not initialized, earlier in the code.
Here is the code where I'm adding the button:
private void OnDraw(object sender, GameTimerEventArgs e)
{
#region CommonStuff
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
#endregion CommonStuff
if (is3D)
{
OnDraw3D(sender, e);
}
else
{
OnDraw2D(sender, e);
}
switchScreen = new Button();
switchScreen.Height = 20.0;
switchScreen.Width = 100.0;
switchScreen.Content = "Switch to Shooting Screen";
switchScreen.Margin = new Thickness(phoneScreen.Height - switchScreen.Width -
20.0, 20.0, 20.0, phoneScreen.Width - switchScreen.Height - 20.0);
switchScreen.Visibility = System.Windows.Visibility.Visible;
}
I'm only testing the OnDraw2D so here's the code for that:
private void OnDraw2D(object sender, GameTimerEventArgs e)
{
spriteBatch.Begin();
// TODO: Add your drawing code here
map.Draw(e.ElapsedTime, e.TotalTime);
// npc.Draw(gameTime);
}
and the map.Draw is here
public override void Draw(TimeSpan elapsedTime, TimeSpan totalTime)
{
// Draw the Sky
gamePage.getSpriteBatch().Draw(background, Position, Color.White);
foreach (Person person in people)
{
person.Draw(elapsedTime, totalTime);
}
base.Draw(elapsedTime, totalTime);
}
background is a Texture.2D and Position is a Vector2.
I think, since you're creating the button manually, you may need to use this code instead (I am assuming that you're using the Windows Forms Button control):
switchScreen.Location = new System.Drawing.Point(xLocation, yLocation);
switchScreen.Name = name;
switchScreen.Size = new System.Drawing.Size(xSize, ySize);
switchScreen.TabIndex = 0;
switchScreen.Text = text;
switchScreen.UseVisualStyleBackColor = true;
Controls.Add(switchScreen);
How are you implementing a Windows Form? As far as I know, you can't add a Button without an underlying form for it to be on.

Categories