Beschreibung:
Es ist garnicht so einfach, die Darstellung von einem Control zu verändern, dass vom Betriebssystem gezeinet (z.B. Prgressbars) wird, ohne alles selbst zeichnen zu müssen, und dabei auchnoch das Flackern zu verhindern.
Der folgende Code weist das Betriebssystem an, das Control nicht direkt, sondern zuerst auf eine Bitmap zu zeichnen, die man dann nach belieben bearbeiten kann, bevor man sie anzeigt:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace DiverseTests
{
class OwnProgressBar:ProgressBar
{
//----erlaubt es auf belibigen windowseigenen Controls zu zeichnen(außer auf Textboxen)----------
//Interop Krams:
[DllImport("user32.dll")]
static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);
[StructLayout(LayoutKind.Sequential)]
struct PAINTSTRUCT
{
public IntPtr hdc;
public bool fErase;
public RECT rcPaint;
public bool fRestore;
public bool fIncUpdate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] rgbReserved;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public Rectangle ToRectangle()
{ return Rectangle.FromLTRB(Left, Top, Right, Bottom); }
public static implicit operator Rectangle(RECT rect)
{
return rect.ToRectangle();
}
}
[DllImport("user32.dll")]
static extern bool EndPaint(IntPtr hWnd, [In] ref PAINTSTRUCT lpPaint);
const int WM_PAINT = 15;
const int WM_ERASEBKGND = 20;
Bitmap buffer;
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case WM_ERASEBKGND:
case WM_PAINT:
//Buffer vorbereiten:
if (buffer == null)
{
buffer = new Bitmap(Width, Height);
}
if (buffer.Width != Width || buffer.Height != Height)
{
buffer.Dispose();//!!!
buffer = new Bitmap(Width, Height);
}
//Hdc besorgen:
IntPtr hdc = m.WParam;
PAINTSTRUCT ps=new PAINTSTRUCT();
bool callEndPaint = false ;
Rectangle drawingRegion;
if (hdc == IntPtr.Zero)
{
hdc = BeginPaint(Handle, out ps);
callEndPaint = true;
drawingRegion=ps.rcPaint;
}
else
{
drawingRegion=ClientRectangle;
}
if (hdc == IntPtr.Zero)
{ return; }
//auf Buffer zeichnen:
using (Graphics ownGx = Graphics.FromImage(buffer))
{
IntPtr ownHdc=ownGx.GetHdc();
Message newM = new Message();
newM.Msg=m.Msg;
newM.HWnd = Handle;
newM.WParam=ownHdc;
newM.LParam=m.LParam;
DefWndProc(ref newM);
ownGx.ReleaseHdc(ownHdc);
//man kann hier den Buffer beliebig manipulieren.
if (m.Msg == WM_PAINT) OnPaint(new PaintEventArgs(ownGx, drawingRegion));
else OnPaintBackground(new PaintEventArgs(ownGx, drawingRegion));
}
//Buffer zeichnen:
if(m.Msg==WM_PAINT)
{
using (Graphics gx = Graphics.FromHdc(hdc))
{
gx.DrawImage(buffer, drawingRegion);
}
}
//Aufräumen:
if (callEndPaint)
{ EndPaint(Handle, ref ps); }
m.Result = IntPtr.Zero;
return;
default:
base.WndProc(ref m);
return;
}
}
protected override void OnPaint(PaintEventArgs e)
{
SizeF TextSize=e.Graphics.MeasureString(Text, Font);
e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), (Width - TextSize.Width) / 2, (Height - TextSize.Height) / 2);
}
[EditorBrowsable(EditorBrowsableState.Always)]
[Browsable(true)]
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
[EditorBrowsable(EditorBrowsableState.Always)]
[Browsable(true)]
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
}
}
}
}
Schlagwörter: <Bitte Schlagwörter, unter denen das Snippet über die Suche gefunden werden soll, hier eintragen>
weils so schön ist hier noch ein screenshot:
Hallo floste!
Sehr schönes Snippet.
Hab' allerdings auch eine Frage: Warum überschreibst du Text und Font?
Nobody is perfect. I'm sad, i'm not nobody 🙁
Hallo tom-essen,
um die Anzeige der Properties im Designer, die in der Oberklasse ProgressBar ausgeschaltet wurde, wieder einzuschalten:
[EditorBrowsable(EditorBrowsableState.Always)] [Browsable(true)]
herbivore
Es wäre vielleicht ganz hilfreich, wenn du die Integers im WndProc mit Konstanten bennenen könntest. Ich weis zwar, was 15 und 20 für eine Windows Nachricht ist, aber andere vielleicht nicht 😃
BTW: 15 = WM_PAINT, 20 = WM_ERASEBKGND
"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)
Hallo 🙂
Um einfach nur eine prozentuale Anzeige zu erreichen, welche den Fortschritt anzeigt ist es geschickter, wenn das die ProgressBar selber zeichnet.
Für diesen Fall habe ich eine kleine Umänderung vom Snippet gemacht, damit das die ProgressBar von "Haus aus" kann.
Die grösste Änderung ist die, dass ich den Code nach meinem Geschmack umgeändert habe.
Um die Prozente anzuzeigen muss man nur noch
progressStateBar.StateView = ProgressStateBar.ProgressState.Percent;
machen. Geht auch im Designer
Damit das Layout nicht gesprengt wird, hänge ich die Klasse an.
Und auch noch ein Bild
mfg
SeeQuark