the WPF Popup control is nice, but somewhat limited in my opinion. is there a way to "drag" a popup around when it is opened (like with the DragMove() method of windows)?
can this be done without big problems or do i have to write a substitute for the popup class myself?
thanks
Here's a simple solution using a Thumb.
Subclass Popup in XAML and codebehind
Add a Thumb with width/height set to 0 (this could also be done in XAML)
Listen for MouseDown events on the Popup and raise the same event on the Thumb
Move popup on DragDelta
XAML:
<Popup x:Class="PopupTest.DraggablePopup" ...>
<Canvas x:Name="ContentCanvas">
</Canvas>
</Popup>
C#:
public partial class DraggablePopup : Popup
{
public DraggablePopup()
{
var thumb = new Thumb
{
Width = 0,
Height = 0,
};
ContentCanvas.Children.Add(thumb);
MouseDown += (sender, e) =>
{
thumb.RaiseEvent(e);
};
thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
}
There is no DragMove for PopUp. Just a small work around, there is lot of improvements you can add to this.
<Popup x:Name="pop" IsOpen="True" Height="200" Placement="AbsolutePoint" Width="200">
<Rectangle Stretch="Fill" Fill="Red"/>
</Popup>
In the code behind , add this mousemove event
pop.MouseMove += new MouseEventHandler(pop_MouseMove);
void pop_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
pop.PlacementRectangle = new Rect(new Point(e.GetPosition(this).X,
e.GetPosition(this).Y),new Point(200,200));
}
}
Building off of Jobi Joy's answer, I found a re-useable solution that allows you to add as a control within xaml of an existing control/page. Which was not possible adding as Xaml with a Name since it has a different scope.
[ContentProperty("Child")]
[DefaultEvent("Opened")]
[DefaultProperty("Child")]
[Localizability(LocalizationCategory.None)]
public class DraggablePopup : Popup
{
public DraggablePopup()
{
MouseDown += (sender, e) =>
{
Thumb.RaiseEvent(e);
};
Thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
/// <summary>
/// The original child added via Xaml
/// </summary>
public UIElement TrueChild { get; private set; }
public Thumb Thumb { get; private set; } = new Thumb
{
Width = 0,
Height = 0,
};
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
TrueChild = Child;
var surrogateChild = new StackPanel();
RemoveLogicalChild(TrueChild);
surrogateChild.Children.Add(Thumb);
surrogateChild.Children.Add(TrueChild);
AddLogicalChild(surrogateChild);
Child = surrogateChild;
}
}
Another way of achieving this is to set your Popup's placement to MousePoint. This makes the popup initially appear at the position of the mouse cursor.
Then you can either use a Thumb or MouseMove event to set the Popup's HorizontalOffset & VerticalOffset. These properties shift the Popup away from its original position as the user drags it.
Remember to reset HorizontalOffset and VerticalOffset back to zero for the next use of the popup!
The issue with loosing the mouse when moving too fast, could be resolved
This is taken from msdn:
The new window contains the Child content of Popup.
The Popup control maintains a reference to its Child content as a logical child. When the new window is created, the content of Popup becomes a visual child of the window and remains the logical child of Popup. Conversely, Popup remains the logical parent of its Child content.
In the other words, the child of the popup is displayed in standalone window.
So when trying to the following:
Popup.CaptureMouse() is capturing the wrapper window and not the popup itself. Instead using Popup.Child.CaptureMouse() captures the actual popup.
And all other events should be registered using Popup.Child.
Like Popup.Child.MouseMove, Popup.Child.LostCapture and so on
This has been tested and works perfectly fine
Contrary to what others have stated about this, I agree 100% with Jobi Joy's answer (which should honestly be the accepted answer). I saw a comment stating that the solution in the answer would cause memory fragmentation. This is not possible as creating new structs cannot cause memory fragmentation at all; in fact, using structs saves memory because they are stack-allocated. Furthermore, I think that this is actually the correct way to reposition a popup (after all, Microsoft added the PlacementRectangle property for a reason), so it is not a hack. Appending Thumbs and expecting a user to always place a Popup onto a canvas, however, is incredibly hacky and is not always a practical solution.
Private Point startPoint;
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(null);
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point relative = e.GetPosition(null);
Point AbsolutePos = new Point(relative.X + this.Left, relative.Y + this.Top);
this.Top = AbsolutePos.Y - startPoint.Y;
this.Left = AbsolutePos.X - startPoint.X;
}
}
This works for dragging my window, but like it was told if i move the mouse to fast, it would get out of window and stop raising the event. Without mentioning the dragging is not smooth at all. Does anyone knows how to do it properly, nice and smooth dragging, without loosing it when dragged too fast??? Post a simple example if possible, other than a whole tutorial that would get beginners like me lost in code. Thanks!
Related
I have written a custom WPF UserControl. It's a square with a Grid named Base. To that grid I add an ellipse and two labels (volume and location), which are populated with text pulled from the properties of an object which is given as a parameter upon control instantiation.
Here's the XAML:
<UserControl x:Class="EasyHyb.SampleWellControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100">
<Grid x:Name="Base">
</Grid>
</UserControl>
And the constructor/event functions in the codebehind:
public SampleWellControl(int size, SourceSample sample)
{
InitializeComponent();
this.sample = sample;
this.Width = this.Height = size;
this.selected = SelectionStatus.Unselected;
double spacing = size / 4;
volume = new Label();
location = new Label();
volume.Content = String.Format("{0:0.00}", sample.volume);
location.Content = sample.well.well;
volume.HorizontalAlignment = location.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
volume.FontFamily = location.FontFamily = new System.Windows.Media.FontFamily("Meiryo UI");
volume.FontWeight = location.FontWeight = FontWeights.Bold;
volume.Background = location.Background = Base.Background = this.Background = Brushes.Transparent;
volume.Margin = new Thickness(0, spacing, 0, 0);
location.Margin = new Thickness(0, spacing * 2, 0, 0);
well = new Ellipse();
well.Width = well.Height = this.Width;
well.StrokeThickness = 3;
Base.Children.Add(well);
Base.Children.Add(volume);
Base.Children.Add(location);
this.MouseEnter += SampleWellControl_MouseEnter;
this.MouseLeave += SampleWellControl_MouseLeave;
this.MouseUp += SampleWellControl_MouseUp;
this.Cursor = Cursors.Hand;
UpdateFillAndStroke();
}
void SampleWellControl_MouseLeave(object sender, MouseEventArgs e)
{
RevertWell();
}
void SampleWellControl_MouseEnter(object sender, MouseEventArgs e)
{
HighlightWell();
}
public void HighlightWell()
{
if (this.selected == SelectionStatus.Pooled)
{
return;
}
if (this.selected == SelectionStatus.Unselected)
{
this.well.Stroke = this.strokes[SelectionStatus.Selected];
}
else
{
this.well.Stroke = this.strokes[SelectionStatus.Unselected];
}
}
public void RevertWell()
{
if (this.selected == SelectionStatus.Pooled)
{
return;
}
if (this.selected == SelectionStatus.Unselected)
{
this.well.Stroke = this.strokes[SelectionStatus.Unselected];
}
else
{
this.well.Stroke = this.strokes[SelectionStatus.Selected];
}
}
Basically, when the mouse enters the control, the stroke of the ellipse should change unless the well has undergone an operation to give it a "Pooled" status.
When the mouse enters the control, it responds exactly as I expect: the MouseEnter event handler fires. However, when a user moves the mouse over one of the labels inside the control, the MouseLeave event fires. So even though the label is ostensibly part of the control The pictures below show what I'm talking about. Print Screen removes the cursors, but I put blue dots to indicate where the cursor is:
Responding properly:
Now it seems to think the mouse has left the control:
I've tried adding MouseEnter and MouseLeave event handlers to the labels, but they don't fire. The cursor also changes from a hand to a pointer when the labels are moused over. I've tried adding MouseEnter and MouseLeave event handlers to the control after it's instantiated within another class. I added transparent backgrounds to the Grid, control, and labels, but that didn't make any difference either.
I also checked in my MouseLeave event handler to see if the mouse was over the control, and it seems that the control is not detecting the cursor as being over the control itself:
if(!this.IsMouseOver)
{
RevertWell();
}
//also tried IsMouseDirectlyOver
I would like MouseLeave to fire only when the cursor exits the square bounds of the control. How can I accomplish this while keeping the labels?
Use a combination of
IsHitTestVisible="False"
on all of your objects added to Base:
volume = new Label();
volume.IsHitTestVisible="False";
and then your container which has the events, give a background
<Grid x:Name="Base" Background="White">
(Also I wanted to comment but reputation is stupid)
Well shucks, after a lot of searching around it turns out the problem was indeed contained within another control. I had another UserControl class, EmptyWellControl, which had a Label. The text position within the label was calculated using the Label's Height property, which resulted in a nonsense value that made the label extend vertically well beyond the dimensions of the window. The label didn't have a background, but nevertheless interfered with the controls whose path it crossed. Since the empty and sample wells were all laid out on the same grid, every SampleWellControl was affected by these labels.
I have a Form which has got a parent panel and it had got a child panel where I am drawing items using the drawing mechanism it works good as expected, but when I shrink my form from right to left it doesn't call child panels paint event while if I shrink a little from left to right and again spread it then it calls the paint event, how should I fix it?
Below is my code.
private void canvas_Paint(object sender, PaintEventArgs e)
{
drawString(e);
this.Invalidate();
//this.Refresh();
//this.Update();
}
private void drawString(PaintEventArgs e)
{
System.Drawing.Drawing2D.LinearGradientBrush myBrush = new System.Drawing.Drawing2D.LinearGradientBrush(ClientRectangle, Color.Red, Color.Yellow, System.Drawing.Drawing2D.LinearGradientMode.Horizontal);
cBasketItemHelper objHelper = new cBasketItemHelper() { CanvasWidth = this.canvas.Width, CanvasHeight = this.canvas.Height, X = 3, Y = 3 };
objHelper.myBrush = myBrush;
objHelper.currOrder = Program.currOrder;
objHelper.g = e.Graphics;//this.canvas.();//this.canvas.Graphics;
objHelper.DrawBasketItems();
e.Dispose();
}
The Panel class was designed to be just a container for other controls, it is not expected to do any painting of its own beyond drawing the background. Somewhat heavy-handedly it optimizes the painting, a resize only paints the parts that were revealed, not the entire client area.
You however want OnPaint to always run when the size changes, even when you make it smaller. Derive your own class from Panel and set the ResizeRedraw property to true in the constructor:
class Canvas {
public Canvas() {
this.ResizeRedraw = true;
this.DoubleBuffered = true; // extra goodie
}
}
Build. Drop the new Canvas control from the top of the toolbox, replacing your existing panel control. If you don't need the scrolling support that Panel provides then using a PictureBox gets you both without needing to derive.
I have the following code
<Window x:Class="Netspot.DigitalSignage.Client.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" WindowStyle="SingleBorderWindow"
WindowStartupLocation="CenterScreen"
WindowState="Normal" Closing="Window_Closing">
Any attempt to get the height / width return NaN or 0.0
Can anyone tell me a way of getting it ?
These 2 methods don't work
//Method1
var h = ((System.Windows.Controls.Panel)Application.Current.MainWindow.Content).ActualHeight;
var w = ((System.Windows.Controls.Panel)Application.Current.MainWindow.Content).ActualWidth;
//Method2
double dWidth = -1;
double dHeight = -1;
FrameworkElement pnlClient = this.Content as FrameworkElement;
if (pnlClient != null)
{
dWidth = pnlClient.ActualWidth;
dHeight = pnlClient.ActualWidth;
}
The application will not be running full screen.
1.) Subscribe to the window re size event in the code behind:
this.SizeChanged += OnWindowSizeChanged;
2.) Use the SizeChangedEventArgs object 'e' to get the sizes you need:
protected void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
double newWindowHeight = e.NewSize.Height;
double newWindowWidth = e.NewSize.Width;
double prevWindowHeight = e.PreviousSize.Height;
double prevWindowWidth = e.PreviousSize.Width;
}
Keep in mind this is very general case, you MAY (you may not either) have to do some checking to make sure you have size values of 0.
I used this to resize a list box dynamically as the main window changes. Essentially all I wanted was this control's height to change the same amount the window's height changes so its parent 'panel' looks consistent throughout any window changes.
Here is the code for that, more specific example:
NOTE I have a private instance integer called 'resizeMode' that is set to 0 in the constructor the Window code behind.
Here is the OnWindowSizeChanged event handler:
protected void OnWindowSizeChanged (object sender, SizeChangedEventArgs e)
{
if (e.PreviousSize.Height != 0)
{
if (e.HeightChanged)
{
double heightChange = e.NewSize.Height - e.PreviousSize.Height;
if (lbxUninspectedPrints.Height + heightChange > 0)
{
lbxUninspectedPrints.Height = lbxUninspectedPrints.Height + heightChange;
}
}
}
prevHeight = e.PreviousSize.Height;
}
You can get the width and height that the window was meant to be in the constructor after InitializeComponent has been run, they won't return NaN then, the actual height and width will have to wait until the window has been displayed.
When WindowState == Normal You can do this one from Width / Height after IntializeComponent().
When WindowState == Maximized You could get the screen resolution for this one with
System.Windows.SystemParameters.PrimaryScreenHeight;
System.Windows.SystemParameters.PrimaryScreenWidth;
You have to try to get the ActualWidth/ActualHeight values once the window is Loaded in the UI. Doing it in Window_Loaded works well.
XAML
<Grid x:Name="Grid1">
<Image x:Name="Back_Image" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
CS
MainWindow() after InitializeComponent();
Back_Image.Width = Grid1.Width;
Back_Image.Height = Grid1.Height;
WPF does the creation of controls and windows in a deferred manner. So until the window is displayed for the first time, it might not have gone through layouting yet, thus no ActualWidth/ActualHeight. You can wait until the window is loaded and get the properties then, or yet better bind these properties to a target where you need them. You could also force the layouting via UpdateLayout().
Just want to add: try to minimize the amount of size dependant logic, it is almost always possible to avoid it. Unless you are writing a layout panel of course.
Notice that when you use controls with 100% of with, they have a NaN size till they are being represented
You can check the ActualHeigh or ActualWidth just when the Loaded event is fired, but never try to verify it before the windows controls are created. Forget the idea to control that in the constructor.
In my oppinion, the best place to control this kind of things is The SizeChanged event
Hi you can get the height of the current screen in WPF with
System.Windows.SystemParameters.PrimaryScreenHeight
I just add a variable name to my window and then get the width or height of the window i want from its width or height property respectifly.
XAML:
<Window x:Name="exampleName"
...
</Window>
C#:
exampleName.Height;
or:
exampleName.Width;
My solution in case of MVVM...
I have a ViewModelBase, which is the base class for all ViewModel of the app.
1.) I created in the ViewModelBase a virtual method, which could be overriten in the child ViewModels.
public virtual void OnWindowSizeChanged(object sender, SizeChangedEventArgs e) {}
2.) The ctr of the MainWindow : Window class
public MainWindow(object dataContext)
{
InitializeComponent();
if (dataContext is ViewModelBase)
{
this.SizeChanged += ((ViewModelBase)dataContext).OnWindowSizeChanged;
DataContext = dataContext;
}
}
3.) Eg. in the MainViewModel, which is extends the ViewModelBase
public override void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
Console.WriteLine(e.NewSize.Height);
}
When I add my UserControls to a FlowLayoutPanel, they display properly. When I change the Dock or Anchor properties on the UserControls before adding them, they are still added but do not render.
According to "How to: Anchor and Dock Child Controls" this should be possible.
I can tell that the controls are added (despite not drawing) because adding enough of them causes a vertical scrollbar to appear.
Setting the "Dock" property of the UserControls to "Left" or "None" will cause them to render, but none of the other options.
Setting the "Anchor" property on the UserControls to anything but Top | Left does not render.
Setting the dock before or after adding the control makes no difference (Add, Dock vs. Dock, Add).
The FlowLayoutPanel is itself is docked (Fill), has FlowDirection set to TopDown, has WrapContents set to false, has AutoScroll set to true, and is otherwise default.
I am using .NET 3.5.
In answer to a comment, the two commented lines are the locations I tried to change the dock. The second spot definitely makes more sense, but I tried the other because it couldn't hurt.
public void CreateObjectControl( object o )
{
ObjectControl oc = new ObjectControl();
oc.MyObject = o;
//This was a spot I mentioned:
//oc.Dock = DockStyle.Fill;
ObjectDictionary.Add( o, oc );
flowLayoutPanel1.Controls.Add( oc );
//This is the other spot I mentioned:
oc.Dock = DockStyle.Fill;
}
try using SuspendLayout and Resumelayout function for the controls before making any amendments which need rendering for proper viewing.
You could see the code from Designer.cs for that particular control
Syntax
control.SuspendLayout();
{Your code for designer amendments}
control.resumeaLayout();
I think I may have found a workaround (read: dirty trick) ... this answer helped to point me in the right direction. Here's an excerpt from the MS article that you also linked to:
For vertical flow directions, the FlowLayoutPanel control calculates the width of an implied column from the widest child control in the column. All other controls in this column with Anchor or Dock properties are aligned or stretched to fit this implied column.
The behavior works in a similar way for horizontal flow directions. The FlowLayoutPanel control calculates the height of an implied row from the tallest child control in the row, and all docked or anchored child controls in this row are aligned or sized to fit the implied row.
This page does not specifically mention that you can't Dock/Anchor the tallest/widest control. But as this control defines the layout behaviour of the FlowLayoutPanel, and thus influences the way all other sibling controls are displayed, it is well possible that Dock and Anchor don't work properly for that 'master control'. Even though I can't find any official documentation regarding that, I believe it to be the case.
So, which options do we have? At runtime, we could add a panel control of height 0 and width of the FlowLayoutPanel client area before you add your usercontrol. You can even set that panel's visibility to false. Subscribing to some Resize/Layout events of the FlowLayoutPanel to keep that panel's size will to the trick. But this does not play nicely at design time. The events won't fire and thus you can't really design the surface the way you want it to look.
I'd prefer a solution that "just works" at design time as well. So, here's an attempt at an "invisible" control that I put together, to fix the controls resizing to zero width if no other control is present. Dropping this as first control onto the FlowLayoutPanel at design time seems to provide the desired effect, and any control subsequently placed on the FlowLayoutPanel is anchorable to the right without shrinking to zero width. The only problem is that, once this invisible control is there, it seems I can't remove it anymore via the IDE. It probably needs some special treatment using a ControlDesigner to achieve that. It can still be removed in the form's designer code though.
This control, once placed onto the FlowLayoutPanel, will listen for resize events of it's parent control, and resize itself according to the ClientSize of the parent control. Use with caution, as this may contain pitfalls that didn't occur to me during the few hours I played with this. For example, I didn't try placing controls that were wider than the FlowLayoutPanel's client area.
As a side note, what will still fail is trying to anchor to the bottom, but that wasn't part of the question ;-)
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
namespace ControlTest
{
public sealed class InvisibleControl : Control
{
public InvisibleControl()
{
TabStop = false;
}
#region public interface
// Reduce the temptation ...
public new AnchorStyles Anchor
{
get { return base.Anchor; }
set { base.Anchor = AnchorStyles.None; }
}
public new DockStyle Dock
{
get { return base.Dock; }
set { base.Dock = DockStyle.None; }
}
// We don't ever want to move away from (0,0)
public new Point Location
{
get { return base.Location; }
set { base.Location = Point.Empty; }
}
// Horizontal or vertical orientation?
private Orientation _orientation = Orientation.Horizontal;
[DefaultValue(typeof(Orientation), "Horizontal")]
public Orientation Orientation
{
get { return _orientation; }
set
{
if (_orientation == value) return;
_orientation = value;
ChangeSize();
}
}
#endregion
#region overrides of default behaviour
// We don't want any margin around us
protected override Padding DefaultMargin => Padding.Empty;
// Clean up parent references
protected override void Dispose(bool disposing)
{
if (disposing)
SetParent(null);
base.Dispose(disposing);
}
// This seems to be needed for IDE support, as OnParentChanged does not seem
// to fire if the control is dropped onto a surface for the first time
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
ChangeSize();
}
// Make sure we don't inadvertantly paint anything
protected override void OnPaint(PaintEventArgs e) { }
protected override void OnPaintBackground(PaintEventArgs pevent) { }
// If the parent changes, we need to:
// A) Unsubscribe from the previous parent's Resize event, if applicable
// B) Subscribe to the new parent's Resize event
// C) Resize our control according to the new parent dimensions
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
// Perform A+B
SetParent(Parent);
// Perform C
ChangeSize();
}
// We don't really want to be resized, so deal with it
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
ChangeSize();
}
#endregion
#region private stuff
// Make this a default handler signature with optional params, so that this can
// directly subscribe to the parent resize event, but also be called without parameters
private void ChangeSize(object sender = null, EventArgs e = null)
{
Rectangle client = Parent?.ClientRectangle ?? new Rectangle(0, 0, 10, 10);
Size proposedSize = _orientation == Orientation.Horizontal
? new Size(client.Width, 0)
: new Size(0, client.Height);
if (!Size.Equals(proposedSize)) Size = proposedSize;
}
// Handles reparenting
private Control boundParent;
private void SetParent(Control parent)
{
if (boundParent != null)
boundParent.Resize -= ChangeSize;
boundParent = parent;
if (boundParent != null)
boundParent.Resize += ChangeSize;
}
#endregion
}
}
Hi I am using forms in .net and i am adding lots of linked labels dynamically during runtime,
I am adding these linklabels to panel and adding that panel to the winform. When the no of linklabels increases the form puts out an auto scrollbar(vertical)...
Now when i scroll down using that autoscroll the form is not updating its view as i scroll, the form gets refreshed only when i stop scrolling...
Also when it refresh it looks too bad.. i can see how it draws slowly....
Has anyone dealt with this before??
I tried form.refresh() in scroll event handler but that doesn't seem to help..
Any clues?
Pop this into your class (UserControl, Panel, etc) , then it will work with thumb drag.
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
protected override void WndProc (ref Message m)
{
if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
&& (((int)m.WParam & 0xFFFF) == 5))
{
// Change SB_THUMBTRACK to SB_THUMBPOSITION
m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
}
base.WndProc (ref m);
}
If you don't want to use WinAPI calls, you can do this:
// Add event handler to an existing panel
MyPanel.Scroll += new EventHandler(MyPanelScroll_Handler);
// Enables immediate scrolling of contents
private void MyPanelScroll_Handler(System.Object sender, System.Windows.Forms.ScrollEventArgs e)
{
Panel p = sender As Panel;
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll) {
p.HorizontalScroll.Value = e.NewValue;
} else if (e.ScrollOrientation == ScrollOrientation.VerticalScroll) {
p.VerticalScroll.Value = e.NewValue;
}
}
Try setting your form's DoubleBuffered property to True.
Update: actually, that probably won't do anything since your controls are on a Panel on your Form. The built-in Panel control doesn't have an exposed DoubleBuffered property, so the way to do it is to add a UserControl name DBPanel to your project, and change the code so that it inherits from Panel instead of UserControl (you can change this manually in the CS file after you add it). When you add the UserControl, the code will look like this:
public partial class DBPanel : UserControl
{
public DBPanel()
{
InitializeComponent();
}
}
Edit it so that it looks like this (change UserControl to Panel and add the "this.DoubleBuffered = true;" line to the constructor):
public partial class DBPanel : Panel
{
public DBPanel()
{
InitializeComponent();
this.DoubleBuffered = true;
}
}
When you build the project, the compiler will barf on a line that begins with "this.AutoScaleMode ... ". Delete this line and rebuild.
You can now use the DBPanel control on your form in place of a regular Panel, and this should take care of your flicker problem.
Update 2: sorry, I didn't read your question closely enough. You're right, the Panel doesn't redraw itself until you let go of the scrollbar's thumb. I think to achieve this effect you'll just have to create your own UserControl.
Basically you'd just have a UserControl with a VScrollBar docked on the right, and a Panel with AutoScroll = false docked on the left taking up the remainder of the space. The Scroll and ValueChanged events of the VScrollBar fire as you move the thumb up and down, so after adding a bunch of LinkLabels to the inner Panel you can use these events to change the Top position of the Panel, and thus achieve the dynamic scrolling effect you're looking for.
It's kind of irritating that the Panel doesn't work this way by default, or even have a setting that enables it.
The simplest way is to refresh the panel during the scroll event.
private void panel1_Scroll(object sender, ScrollEventArgs e)
{
panel1.Refresh();
}