User Drawn Controls: the MSN chat window - c#

I'm wondering about the famous MSN chat clients conversation windows! I'm sure there must be a lot of different aspects but I'd like to focus on those little sliding panes. For instance where the pictures of the people in the conversation are displayed. When you click the collapse button the pictures disappear and the panel gracefully slides in, and when you click it again to expand, it slides out and the pictures are smoothly faded in.
How would one go about customly drawing a control in WinForms that had similar behaviour?

This should give you an idea how animate your width.
int _collapsedWidth;
int _fullWidth;
float _speed;
float _acurateWidth;
System.Diagnostics.Stopwatch _stopwatch = new Stopwatch ();
int _animationDirection;
AnimatedControl (){
Application.Idle += ApplicationIdle;
}
void Expand (){
_animationDirection = 1;
_stopwatch.Start();
}
void ApplicationIdle (object sender, EventArgs e){
if (_animation.Direction == 0)
return;
float delta = _stopwatch.Elapsed.TotalMilliseconds * _speed;
_acurateWidth += delta;
if (_acurateWidth < _collapsedWidth)
{
_animationDirection = 0;
_acurateWidth = _collapsedWidth;
_stopwatch.Stop();
}
else if (_acurateWidth > _fullWidth)
{
_animationDirection = 0;
_acurateWidth = _fullWidth;
_stopwatch.Stop();
}
_stopwatch.Reset();
this.Width = (int)System.Math.Round(_acurateWidth , MidpointRounding.AwayFromZero);
this.Invalidate (); // May not need this
}
and for the pictures, somthing similar but using translucent images, you might want to make a new control with a transparent background color for them as well depending how you want to paint things.
You can then put this control into one of the LayoutPanel controls to move your other controls around in the form to match the width.

Related

Dynamically display an array of PictureBoxes - Performance issue

I'd like to display coverarts for each album of an MP3 library, a bit like Itunes does (at a later stage, i'd like to click one any of these coverarts to display the list of songs).
I have a form with a panel panel1 and here is the loop i'm using :
int i = 0;
int perCol = 4;
int disBetWeen = 15;
int width = 250;
int height = 250;
foreach(var alb in mp2)
{
myPicBox.Add(new PictureBox());
myPicBox[i].SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
myPicBox[i].Location = new System.Drawing.Point(disBetWeen + (disBetWeen * (i % perCol) +(width * (i % perCol))),
disBetWeen + (disBetWeen * (i / perCol))+ (height * (i / perCol)));
myPicBox[i].Name = "pictureBox" + i;
myPicBox[i].Size = new System.Drawing.Size(width, height);
myPicBox[i].ImageLocation = #"C:/Users/Utilisateur/Music/label.jpg";
panel1.Controls.Add(myPicBox[i]);
i++;
}
I'm using the same picture per picturebox for convenience, but i'll use the coverart embedded in each mp3 file eventually.
It's working fine with an abstract of the library (around 50), but i have several thousands of albums. I tried and as expected, it takes a long time to load and i cannot really scroll afterward.
Is there any way to load only what's displayed ? and then how to assess what is displayed with the scrollbars.
Thanks
Winforms really isn't suited to this sort of thing... Using standard controls, you'd probably need to either provision all the image boxes up front and load images in as they become visible, or manage some overflow placeholder for the appropriate length so the scrollbars work.
Assuming Winforms is your only option, I'd suggest you look into creating a custom control with a scroll bar and manually driving the OnPaint event.
That would allow you to keep a cache of images in memory to draw the current view [and a few either side], while giving you total control over when they're loaded/unloaded [well, as "total" as you can get in a managed language - you may still need tune garbage collection]
To get into some details....
Create a new control
namespace SO61574511 {
// Let's inherit from Panel so we can take advantage of scrolling for free
public class ImageScroller : Panel {
// Some numbers to allow us to calculate layout
private const int BitmapWidth = 100;
private const int BitmapSpacing = 10;
// imageCache will keep the images in memory. Ideally we should unload images we're not using, but that's a problem for the reader
private Bitmap[] imageCache;
public ImageScroller() {
//How many images to put in the cache? If you don't know up-front, use a list instead of an array
imageCache = new Bitmap[100];
//Take advantage of Winforms scrolling
this.AutoScroll = true;
this.AutoScrollMinSize = new Size((BitmapWidth + BitmapSpacing) * imageCache.Length, this.Height);
}
protected override void OnPaint(PaintEventArgs e) {
// Let Winforms paint its bits (like the scroll bar)
base.OnPaint(e);
// Translate whatever _we_ paint by the position of the scrollbar
e.Graphics.TranslateTransform(this.AutoScrollPosition.X,
this.AutoScrollPosition.Y);
// Use this to decide which images are out of sight and can be unloaded
var current_scroll_position = this.HorizontalScroll.Value;
// Loop through the images you want to show (probably not all of them, just those close to the view area)
for (int i = 0; i < imageCache.Length; i++) {
e.Graphics.DrawImage(GetImage(i), new PointF(i * (BitmapSpacing + BitmapWidth), 0));
}
}
//You won't need a random, just for my demo colours below
private Random rnd = new Random();
private Bitmap GetImage(int id) {
// This method is responsible for getting an image.
// If it's already in the cache, use it, otherwise load it
if (imageCache[id] == null) {
//Do something here to load an image into the cache
imageCache[id] = new Bitmap(100, 100);
// For demo purposes, I'll flood fill a random colour
using (var gfx = Graphics.FromImage(imageCache[id])) {
gfx.Clear(Color.FromArgb(255, rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)));
}
}
return imageCache[id];
}
}
}
And Load it into your form, docking to fill the screen....
public Form1() {
InitializeComponent();
this.Controls.Add(new ImageScroller {
Dock = DockStyle.Fill
});
}
You can see it in action here: https://www.youtube.com/watch?v=ftr3v6pLnqA (excuse the mouse trails, I captured area outside the window)

Resizing drawlines on a paint event

I've seen few questions about this problem, I tried every solution but none of them worked for my case.
My code is working; this image shows what happens when I click on Draw button.
I need to zoom on that drawing.Is it possible to code something like autocad feature "zoom/extent"?
Pen myPen = new Pen(Color.Black);
int centerpointx, centerpointy;
private void pictureBoxDraw_Paint(object sender, PaintEventArgs e)
{
centerpointx = pictureBoxDraw.Size.Width/2;
centerpointy = pictureBoxDraw.Size.Height/2;
myPen.Width = 2;
if (binary > 0)
{
var sizecrestgeo = 40;
var distancearraycrestgeo = new float[sizecrestgeo];
var elevationarraycrestgeo = new float[sizecrestgeo];
for (int i = 0; i < sizecrestgeo; i++)
{
distancearraycrestgeo[i] = float.Parse(dataGridViewCrestGeo.Rows[i].Cells[0].Value.ToString());
elevationarraycrestgeo[i] = float.Parse(dataGridViewCrestGeo.Rows[i].Cells[1].Value.ToString())*-1;
}
for (int i=0; i < sizecrestgeo-1; i++)
{
e.Graphics.DrawLine(myPen, distancearraycrestgeo[i]+centerpointx, elevationarraycrestgeo[i]+centerpointy, distancearraycrestgeo[i + 1]+centerpointx, elevationarraycrestgeo[i + 1]+centerpointy);
}
}
else
{
}
}
private void buttonDraw_Click_1(object sender, EventArgs e)
{
if (Hd > 0.0001)
{
binary = 1;
pictureBoxDraw.Invalidate();
}
else
{
MessageBox.Show("No data to draw, perform analysis first.");
}
}
private void buttoncleardraw_Click(object sender, EventArgs e)
{
binary = 0;
pictureBoxDraw.Invalidate();
}
}
This is not so hard, provided you know all the puzzle pieces.
Let's start with the obvious one:
You can scale the Graphics object to create zoomed graphics with ScaleTransform.
As I mentioned, this will include the widths of pens, font sizes and also any images you draw (though not the hatches of a HatchBrush).
You also asked about keeping the drawing 'centered'. This is a non-obvious concept: Just what is the center of your drawing surface??
When zooming (just like rotating) you always need to know the center point of the zoom (or the rotation.) By default this is the origin (0,0). I chose the center of the Panel. You may want to pick some other point..
Once you do you can move the origin of the graphics viewport to this point with TranslateTransform.
Once you have achieved all this you almost certainly will want to allow scrolling.
To do so you have two options:
You can keep AutoScroll = false and nest the canvas control inside another control, usually a Panel, which has AutoScroll = true; next make the canvas control big enough to always hold your drawing and you're done.
Or you can turn on AutoScroll for the canvas control and also set a large enough AutoScrollMinSize. If you then add the current scrolling position to the translation you are also done. Let's see this solution in action:
This is the code in the Paint event:
Size sz = panel3.ClientSize;
Point center = new Point(sz.Width / 2, sz.Height / 2);
Graphics g = e.Graphics;
// center point for testing only!
g.DrawEllipse(Pens.Orange, center.X - 3, center.Y - 3, 6, 6);
// you determine the value of the zooming!
float zoom = (trackBar1.Value+1) / 3f;
// move the scrolled center to the origon
g.TranslateTransform(center.X + panel3.AutoScrollPosition.X,
center.Y + panel3.AutoScrollPosition.Y);
// scale the graphics
g.ScaleTransform(zoom, zoom);
// draw some stuff..
using(Pen pen = new Pen(Color.Yellow, 0.1f))
for (int i = -100; i < 100; i+= 10)
g.DrawEllipse(Pens.Yellow, i-22,i-22,44,44);
A few notes:
I draw an orange circle in the center to show this point is invariant.
My coordinates go from the negative to the positive so you can see that this works nicely.
I draw with a tiny pen width; so the width of the drawing only changes once the resulting pen goes over 1 pixel. Anything draw will always be draw with 1 pxiel width, though.
I first translate and then scale so I don't have to calculate scaled poitions.
The only line in the TrackBar's Scroll event is to trigger the Paint event: panel3.Invalidate();
The only settings needed for the Panel are
panel3.AutoScroll = true;
panel3.AutoScrollMinSize = new Size(500, 500); // use the size you want to allow!
However to avoid flicker it is highly recommended to use a DoubleBuffered control, maybe a Panel subclass like this:
class DrawPanel : Panel
{
public DrawPanel() { DoubleBuffered = true; }
}
Update: Instead of a Panel, which is a Container control and not really meant to draw onto you can use a Picturebox or a Label (with Autosize=false); both have the DoubleBuffered property turned on out of the box and support drawing better than Panels do.
Graphics.ScaleTransform() is how you can zoom. Try using something like this inside your paint event handler:
e.Graphics.ScaleTransform(2.0F, 2.0F);

Maximizing borderless form with multiple monitors c#

I'm currently making a borderless form with a Doubleclick event to maximize form. But I realized that the form wouldn't maximize on the two other screens, only my main middle.
So my code is currently:
private void Form1_DoubleClick(object sender, EventArgs e)
{
if ((this.Height == Screen.PrimaryScreen.WorkingArea.Height) && (this.Width == Screen.PrimaryScreen.WorkingArea.Width))
{
this.Width = 534;
this.Height = 600;
CenterToScreen();
}
else
{
this.Height = Screen.PrimaryScreen.WorkingArea.Height;
this.Width = Screen.PrimaryScreen.WorkingArea.Width;
this.Location = Screen.PrimaryScreen.WorkingArea.Location;
}
}
It might look weird, but I use it to not cover the taskbar.
I need a code like this to dock it to the side, and use it to calculate where the form should be. Looking like this: half right screen dock
when I click one of those 9 buttons, it will dock the screen in different places of the screen. In corner, half of the screen or in the middle.
I tried using a code where the form would detect which screen it was on, and using that again to maximize the form on that screen, but I got a bunch of red lines, and it didn't work in the end.
I have 3 monitors.
Please help.
You hardcoded it to primary screen, which is the screen with the task bar. To allow other screens get the screen the form is currently on an adjust to that.
private void Form1_DoubleClick(object sender, EventArgs e)
{
if ((this.Height == Screen.FromControl(this).WorkingArea.Height) && (this.Width == Screen.FromControl(this).WorkingArea.Width))
{
this.Width = 534;
this.Height = 600;
CenterToScreen();
}
else
{
this.Height = Screen.FromControl(this).WorkingArea.Height;
this.Width = Screen.FromControl(this).WorkingArea.Width;
this.Location = Screen.FromControl(this).WorkingArea.Location;
}
}

Programmatically Scrolling is not working C# WinForm

i have one panel and i put a flow layout panel in the main panel and flow layout panel has many images. my UI looks like this
now i got a code which scroll the container in the panel. i mean the flow layout will scroll in main panel when i will place my mouse at the left or right most area on the main panel. their code is working i checked but when i place their code in my project then that is not working, i means scrolling is not working when i place my mouse at the left or right most area on the main panel.
here i am attaching main code which causes to scroll the flow layout panel inside in main panel
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
int _myval = 5;
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Y < metroPanel1.Top || e.Y > metroPanel1.Top + metroPanel1.Height) return;
if (e.X <= metroPanel1.Left && e.X >= metroPanel1.Left - 40)
{
if (metroPanel1.HorizontalScroll.Value <= _myval)
{
metroPanel1.HorizontalScroll.Value = _myval;
}
else
{
metroPanel1.HorizontalScroll.Value -= _myval;
}
}
if (e.X <= (metroPanel1.Left + metroPanel1.Width + 40) && e.X >= (metroPanel1.Left + metroPanel1.Width))
{
metroPanel1.HorizontalScroll.Value += _myval;
}
}
i just do not understand this value 40 used here if (e.X <= metroPanel1.Left && e.X >= metroPanel1.Left - 40)
i guess i need to use different value rather than 40 but i used 10 & 20 but did not work.
here is my full project link from where anyone can download and see what is wrong in my routine which prevent the scrolling. here is the link https://onedrive.live.com/embed?cid=C4A6F16F34D7540A&resid=C4A6F16F34D7540A%21134&authkey=AM5Fq2gcFLtcw_A
so it is my request that please some one see my code and guide me what i need to change in code and why. thanks
When you want to scroll dont use
metroPanel1.HorizontalScroll.Value -= _myval;
or
metroPanel1.HorizontalScroll.Value += _myval;
but instead
_myval -= 5;
or _myval += 5;
metroPanel1.HorizontalScroll.Value = _myval;
metroPanel1.Refresh();
valter
Yes.
Yes, programmatically scrolling a FlowLayoutPanel is not working, if the Scrollbars are not shown, due to its AutoScroll being off.
Neither setting HorizontalScroll.Value (in any way) nor using
[DllImport("user32.dll")]
static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
will do anything.
Not even doing this has any effect:
flowLayoutPanel1ScrollControlIntoView(someControlThatsOutOfSight);
So, as far as I can tell: Without active Scrollbar(s) no scrolling a FlowLayoutPanel!
The containing Panel can however indeed be scrolled the way valter shows. However my first tests showed even when using a double-buffered Panel subclass a terrible flicker..
Update:
I can't get the Panel to scroll reliably. I have checked up on the link and found that the behaviour is really much simpler than anything I had written: No up down scrolling, no edge detection, no speed detection, no direction detection. In fact there are two scroll zones and when the mouse is in one, it scrolls.. It also relies on the 'constant firing of the mousemove event bug', which only works for me when I don't want it..!
So, here is a solution that implements this behaviour and is, of course much shorter, than the original code.
I include a screenshot to show you the layout: You need two Panels and the FlowLayoutPanel.
The outer Panel is a little wider so that its right and left part work as the scroll zones.
The inner Panel contains the FLP; the FLP has AutoSize=true.
There is a timer scrollTimer with a fast Intervall of 5-10ms.
Here is the code:
int speed = 10;
int delta = 0;
private void panOuter_MouseMove(object sender, MouseEventArgs e)
{
delta = e.X < panOuter.Width / 2 ? speed : -speed;
scrollTimer.Start();
}
private void panOuter_MouseLeave(object sender, EventArgs e)
{
scrollTimer.Stop();
}
private void scrollTimer_Tick(object sender, EventArgs e)
{
int newLeft = FLP.Left + delta;
int alpha = panInner.ClientRectangle.Width - FLP.ClientRectangle.Width;
if (newLeft > 0) { newLeft = 0; scrollTimer.Stop(); }
else if ( newLeft < alpha)
{ newLeft = alpha; scrollTimer.Stop(); }
FLP.Left = newLeft;
}
That's practically all you need. Only in case you Dock or Anchor the outer Panel you should script the Resize event to keep the inner Panel centered!

Scrolling in a flowlayoutpanel while dragging?

I have a Windows Forms application which is using a FlowLayoutPanel control to display a Picture boxes that are built dynamically. I have enabled the drag drop effect as they may want to reorder them, this works fine with only a few picture boxes (right now the screen shows about 6) but if there are more you try to drag an item below the control it will not scroll, so you cannot put an image that is currently on the screen (say image 4) to an image that is below what is visible (say image 13).
I have seen several posts where the ScrollControllIntoViewMethod should be used, I have tried in a few spots unsuccessfully.
Thanks!
Here is what I ended up doing.
Create the event on the DragLeave event
Getting the position of the control
Calculating the height of the control to get the lower boundary.
check the mouse position and if above the bounds, change the vertical scroll (or horizontal scroll) by a value in a Constant..
private void thumbFlow_DragLeave(object sender, EventArgs e)
{
int BegY_ThumbFlow = this.thumbFlow.FindForm().PointToClient(this.thumbFlow.Parent.PointToScreen(this.thumbFlow.Location)).Y;
int thumbFlowBound_Y = this.thumbFlow.Height + BegY_ThumbFlow;
int mouseY = this.thumbFlow.FindForm().PointToClient(MousePosition).Y;
while (mouseY >= thumbFlowBound_Y)
{
thumbFlow.VerticalScroll.Value = thumbFlow.VerticalScroll.Value + DRAG_DROP_SCROLL_AMT;
mouseY = thumbFlow.FindForm().PointToClient(MousePosition).Y;
thumbFlow.Refresh();
}
while (mouseY <= BegY_ThumbFlow)
{
thumbFlow.VerticalScroll.Value = thumbFlow.VerticalScroll.Value - DRAG_DROP_SCROLL_AMT;
mouseY = thumbFlow.FindForm().PointToClient(MousePosition).Y;
thumbFlow.Refresh();
}
}
Hope this helps others.

Categories