Laden...

Schlieren bei LinearGradientBrush minimieren?

Erstellt von baer999 vor 15 Jahren Letzter Beitrag vor 15 Jahren 1.582 Views
B
baer999 Themenstarter:in
375 Beiträge seit 2007
vor 15 Jahren
Schlieren bei LinearGradientBrush minimieren?

Hallo,

ich habe ein Programm und aus designtechnischen Gründen die Form / bzw. ein komplett abdeckendes Panel mit einem LinearGradientBrush versehen. Das selbe bei allen ChildForms (Einstellungen, Info-Box, ...). Wenn ich diese ChildForms hin und her bewege sieht man die üblichen Schlieren auf der Main Form, als ob der PC hängen würde.

Es ist mir klar, dass es rel. aufwendig ist, jedesmal den Brush zu zeichnen usw. aber ich wollte nur mal nachfragen, ob es eine Tuning Möglichkeit gibt?

Das Panel verfügt bereits über diesen Aufruf im Konstruktor:

            this.SetStyle (ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
P
48 Beiträge seit 2008
vor 15 Jahren

Hallo baer999,

die einfachste möglichkeit ist, einfach NICHT jedes mal neuzuzeichnen. Und die erreichst du beispielsweise mit Bitmaps.
Momentan hängst du dich in den OnPaintEvent rein oder?
Verwerfe das, und versuche folgende Schritte:

  • einmalig (zb im Load-Event) ein Background-Bitmap mit deinem LinearGradientBrush erstellen
  • dazu new Bitmap (in der Größe des Panels), dann über Graphics.FormImage() das Graphics-Objekt erstellen, dann über das Graphics-Objekt den Farbverlauf in das Bitmaps zeichnen, dann Graphics disposen und das Bitmap dem Panel als backgroundimage übergeben

jetzt wird der farbverlauf nicht mehr andauernd neugezeichnet. Der Nachteil ist halt, dass dieser auch nicht mehr neugezeichnet wird, wenn sich die Form (oder Panel) in der Größe ändert. Ist dies nötig, musst du dich in den SizeChangedEvent reinhängen und die selbe Funktion aufrufen.

Grüße,

Psy

B
baer999 Themenstarter:in
375 Beiträge seit 2007
vor 15 Jahren

Die Lösung ist leider noch anfälliger bezüglich Schlierenbildung, trotzdem danke 🙂
Gibt es noch andere Ideen ?

P
48 Beiträge seit 2008
vor 15 Jahren

Echt?

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace WindowsApplication1
{
	public class Form1 : System.Windows.Forms.Form
	{
      private System.Windows.Forms.PictureBox pictureBox1;
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			InitializeComponent();

         DrawBackGround();
		}

		/// <summary>
		/// Die verwendeten Ressourcen bereinigen.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Vom Windows Form-Designer generierter Code
		/// <summary>
		/// Erforderliche Methode für die Designerunterstützung. 
		/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
		/// </summary>
		private void InitializeComponent()
		{
         this.pictureBox1 = new System.Windows.Forms.PictureBox();
         this.SuspendLayout();
         // 
         // pictureBox1
         // 
         this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
         this.pictureBox1.Location = new System.Drawing.Point(0, 0);
         this.pictureBox1.Name = "pictureBox1";
         this.pictureBox1.Size = new System.Drawing.Size(728, 525);
         this.pictureBox1.TabIndex = 0;
         this.pictureBox1.TabStop = false;
         this.pictureBox1.SizeChanged += new System.EventHandler(this.pictureBox1_SizeChanged);
         this.pictureBox1.Layout += new System.Windows.Forms.LayoutEventHandler(this.pictureBox1_Layout);
         // 
         // Form1
         // 
         this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
         this.ClientSize = new System.Drawing.Size(728, 525);
         this.Controls.Add(this.pictureBox1);
         this.Name = "Form1";
         this.Text = "Form1";
         this.ResumeLayout(false);

      }
		#endregion

		/// <summary>
		/// Der Haupteinstiegspunkt für die Anwendung.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

      private void pictureBox1_Layout(object sender, System.Windows.Forms.LayoutEventArgs e)
      {
      DrawBackGround();
      }

      private void pictureBox1_SizeChanged(object sender, System.EventArgs e)
      {
      DrawBackGround();
      }

      private void DrawBackGround()
      {
         Image aImage = new Bitmap(pictureBox1.Width, pictureBox1.Height);
         Graphics aGraphics = Graphics.FromImage(aImage);
         Rectangle aRect = new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height);

         LinearGradientBrush aBrush = new LinearGradientBrush(aRect, Color.Red, Color.Yellow, 0.0f);
         aGraphics.FillRectangle(aBrush, aRect);

         pictureBox1.Image = aImage;

         aGraphics.Dispose();
         aBrush.Dispose();
      }
	}
}

keine schlieren ... nix.

Grüße,

psy

B
baer999 Themenstarter:in
375 Beiträge seit 2007
vor 15 Jahren

Nein das ist ja nicht das Problem, habe mich wohl unverständlcih ausgedrückt 🙂
Wenn ich ein Dialogfenster (Einstellungen, Info, ...) über der Main Form Bewege dann sieht man deutlich das Neuzeichnen, darum ging es mir. Laut meinen ungenauen visuellen Tests ist Lin.GradientBrush hierbei sogar etwas schneller. Vielleicht liegt es aber auch einfach an der CPU Leistung, da diese beim Zeichnen ja maßgeblich in Anspruch genommen wird (AMD 2100+)

915 Beiträge seit 2006
vor 15 Jahren

Hrm, es gibt auch andere Lösungsansätze dafür.

Erstmal der allgemeine Workaround: Über die Windowsnachricht WM_ERASEBKGND kann man den Hintergrund von (fast) allen Controls beeinflussen. Hierzu ist es wichtig das man sich allerdings auf der richtigenen Ebene befindet zum zeichnen da es sonst zu den genannten schliereneffekten kommt. Der Vorteil über die WindowsNachricht das ganze zu triggern ist, das man nur eine Zentrale stelle für den Aufruf der Zeichenroutine hat und nicht mehrere.


        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case (int)Win32.WindowsMessages.WM_ERASEBKGND:
                    {
                        this.WmPaintBkgnd(ref m);
                        break;
                    }
                default:
                    {
                        base.WndProc(ref m);
                        break;
                    }
        }

        private void WmPaintBkgnd(ref Message m)
        {
            base.WndProc(ref m);

            this.PaintBkgndArea(m.HWnd, m.WParam);
            m.Result = Win32.TRUE;
        }

       private void PaintBkgndArea(IntPtr hWnd, IntPtr hRgn)
        {
            if (this.m_bUseSkinBackColor)
            {
                Win32.RECT rWnd = new Win32.RECT();
                if (Win32.GetClientRect(hWnd, ref rWnd) == 0) // the small but fine difference betweem client area and non client area :-)
                    return;

                Rectangle rClip = new Rectangle(0, 0, rWnd.Width, rWnd.Height);

                if (hRgn == Win32.FALSE) return;

                using (Graphics g = Graphics.FromHdc(hRgn))
                {
                    this.OnClientBkgndAreaPaint(new PaintEventArgs(g, rClip));
                }
            }
        }


        public void ClientBkgnInvalidate()
        {
            IntPtr hdc = Win32.GetDCEx(this.Handle, IntPtr.Zero, (uint)(Win32.DeviceContextValues.ClipChildren
                | Win32.DeviceContextValues.Cache
                | Win32.DeviceContextValues.ClipSiblings));
            this.PaintBkgndArea(this.Handle, hdc);
        }

Natürlich wäre es nun zu komplex jedes Control für das man die Hintergrundfarbe ändern möchte die WndProc zu überschreiben. Hier kann man relativ einfach eine Komponente erstellen die zusätzlich die Schnittstelle IExtenderProvider implementiert (siehe hierzu MSDN). Anhand der Liste der verfügbaren Controls innerhalb der Komponente kann man nun Subclassing betreiben, das geshcieht am einfachsten über NativeWindow.

Z.B. wie folgt:


    public class MySubClass : NativeWindow
    {
        public SkinUtil(IntPtr _hWnd)
        {
            base.AssignHandle(_hWnd);
        }

        protected override void WndProc(ref Message m)
        { 
          ///... siehe code oben
        }
    }

Kannst dir auch mal die UtilitiesLib von mir ansehen. Bzw. die Klassen UtilitiesLib.Skins.Components.SkinForms (Komponente) und die SubClass UtilitiesLib.Skins.SkinUtil. Mache dort nichts anderes.

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

P
48 Beiträge seit 2008
vor 15 Jahren

Hi bear99,

Wenn ich ein Dialogfenster (Einstellungen, Info, ...) über der Main Form Bewege dann sieht man deutlich das Neuzeichnen, darum ging es mir

probiere doch einfach mal mein Beispiel aus ... es wird nix neugezeichnet, auch wenn ein Dialog drüber geschoben wird. Warum das so ist, habe ich ja schon erwähnt ...

Grüße,

psy