Laden...

Direkt auf Desktop zeichnen mit GDI+

Erstellt von yngwie vor 16 Jahren Letzter Beitrag vor 16 Jahren 4.090 Views
Y
yngwie Themenstarter:in
238 Beiträge seit 2005
vor 16 Jahren
Direkt auf Desktop zeichnen mit GDI+

Hallo Community,

wie kann man mit GDI+ direkt auf den Desktop zeichnen, bzw. das Gezeichnete wieder löschen? Gibt es dafür auch eine DoubleBuffer Untestützung?

Gruß

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo yngwie,

fenster immer im hintergrund??

herbivore

Y
yngwie Themenstarter:in
238 Beiträge seit 2005
vor 16 Jahren

Hallo herbivore,

so wie ich dass sehe ist die wesentliche Aussage von der verlinkten Diskussion, dass es ohne DierectX nicht möglich ist auf den Desktop direkt zu zeichnen... Ist es aber nicht so dass schon uralte C++ Programme es auch ohne DirectX hingekriegt haben? Vielleicht mit Hilfe von WinAPI?


    public partial class Form1 : Form
    {
        #region Fields

        Graphics m_DesktopGrahics = null;
        IntPtr m_DesktopHandle = IntPtr.Zero;
        private bool m_ErasePainting = false;

        #endregion Fields

        #region Extern Methods

        [DllImport("User32.dll")]
        internal static extern IntPtr GetDesktopWindow();

        [DllImport("user32")]
        internal static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("User32.dll")]
        internal static extern int ReleaseDC(IntPtr hWnd, IntPtr dC);

        [DllImport("User32.dll")]
        internal static extern IntPtr GetWindowDC(IntPtr hWnd);

        [DllImport("User32.dll")]
        internal static extern bool UpdateWindow(IntPtr hWnd);

        #endregion Extern Methods

        public Form1()
        {
            InitializeComponent();
        }

        #region Properties

        private IntPtr DesktopDC
        {
            get
            {
                return GetDC(IntPtr.Zero);
            }
        }

        private IntPtr DesktopHandle
        {
            get
            {
                if (IntPtr.Zero == this.m_DesktopHandle)
                {
                    this.m_DesktopHandle = GetDesktopWindow();
                }
                return this.m_DesktopHandle;
            }
        }

        private Control DesktopControl
        {
            get
            {
                Control desktop = null;

                try
                {
                    desktop = Control.FromHandle(this.DesktopHandle);
                }
                catch
                { 
                }
                return desktop;
            }
        }

        private Graphics DesktopGraphics
        {
            get
            {
                if (null == this.m_DesktopGrahics)
                {
                    IntPtr desktopDC = this.DesktopDC;

                    if (IntPtr.Zero != desktopDC)
                    {
                        this.m_DesktopGrahics = Graphics.FromHdc(desktopDC);
                    }
                }
                return this.m_DesktopGrahics;
            }
        }

        #endregion Properties

        private void PaintToScreen()
        {
            Graphics graphics = this.DesktopGraphics;

            if (null != graphics)
            {
                Rectangle rect = new Rectangle(100, 100, 100, 100);

                Color backColor = (false == this.m_ErasePainting) ? Color.Red : Color.Transparent;

                using (Brush br = new SolidBrush(backColor))
                {
                    graphics.FillRectangle(br, rect);
                    bool updateResult = UpdateWindow(this.DesktopHandle);
                }
            }

            int releaseResult = ReleaseDC(this.DesktopHandle, this.DesktopDC);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.PaintToScreen();
            this.m_ErasePainting = !this.m_ErasePainting;
        }
    }

Die Probleme dabei sind:

  • das Wegradieren ( weil UpdateWindow(this.DesktopHandle) bringt nichts )
  • das Zeichnen mit DoubeBuffering-Support, also ohne flackern.

Hat da jemand mal eine Idee wie das realisiert werden kann?

EDIT: Anscheinend ist Desktop kein Control und Control.FromHandle(this.DesktopHandle); liefert immer null zurück... Schade

2.921 Beiträge seit 2005
vor 16 Jahren

falsch, das ist möglich, habe das auch schon gemacht:

Über ein Applikationsfenster "schreiben"

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

Y
yngwie Themenstarter:in
238 Beiträge seit 2005
vor 16 Jahren

Hallo dr4g0n76,

Kann momentan sehr komfortabel auf den Desktop zeichnen und mit InvalidateRect wieder löschen. Leider flackert das Bild beim InvalidateRect-Aufruf. Ich habe so den Eindruck dass das RECT-Parameter ignoriert wird und stattdessen das gesamte Screen neugezeichnet wird. Ich mache das so:



    public struct RECT
    {
        #region Fields

        public int Top;
        public int Left;
        public int Right;
        public int Bottom;

        #endregion Fields

        #region Operators

        /// <summary>
        /// Operator to convert a RECT to Drawing.Rectangle.
        /// </summary>
        /// <param name="rect">Rectangle to convert.</param>
        /// <returns>A Drawing.Rectangle</returns>
        public static implicit operator Rectangle(RECT rect)
        {
            return Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom);
        }

        /// <summary>
        /// Operator to convert Drawing.Rectangle to a RECT.
        /// </summary>
        /// <param name="rect">Rectangle to convert.</param>
        /// <returns>RECT rectangle.</returns>
        public static implicit operator RECT(Rectangle rect)
        {
            return new RECT(rect.Left, rect.Top, rect.Right, rect.Bottom);
        }

        #endregion Operators

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="left">Horizontal position.</param>
        /// <param name="top">Vertical position.</param>
        /// <param name="right">Right most side.</param>
        /// <param name="bottom">Bottom most side.</param>
        public RECT(int left, int top, int right, int bottom)
        {
            this.Top = top;
            this.Left = left;
            this.Right = right;
            this.Bottom = bottom;
        }

        #endregion Constructor

    }
        [DllImport("User32.dll")]
        internal static extern bool InvalidateRect(IntPtr hWnd, ref RECT rect, bool erase);   

        private void PaintToScreen()
        {
            Graphics graphics = Graphics.FromHdc(GetDC(IntPtr.Zero));

            if (null != graphics)
            {
                Rectangle rect = new Rectangle(100, 100, 100, 100);
                Color backColor = (false == this.m_ErasePainting) ? Color.Red : Color.Transparent;

                if (false == this.m_ErasePainting)
                {
                    using (Brush br = new SolidBrush(backColor))
                    {
                        graphics.FillRectangle(br, rect);
                    }
                }
                else
                {
                    RECT screenRect = ((RECT)rect);
                    InvalidateRect(IntPtr.Zero, ref screenRect, true);
                }
            }

            int releaseResult = ReleaseDC(this.DesktopHandle, this.DesktopDC);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.PaintToScreen();
            this.m_ErasePainting = !this.m_ErasePainting;
        }



Dieser Code zeichent auf ein Buttonclick hin einen roten Rechteck auf den Desktop und radiert diesen beim zweiten Buttonclick wieder weg. Wenn ich z.B. die Größe des roten Rechtecks vor dem InvalidateRect-Aufruf verringere, müsste ja nur ein Teil der Rechteckes wegradiert werden. Stattdessen verschwindet das gesammte Rechteck komplett und es flackert. Hast du etwas änliches gehabt ?

2.921 Beiträge seit 2005
vor 16 Jahren

Wenn ich ein Rechteck habe das kleiner ist wie zuvor, dieses aber im ursprünglichen Rechteck liegt, ist doch klar, dass das ganze Rechteck wegradiert wird!?

                  äußeres Rechteck (Invalidated Region)
                   |
                  \|/

\======================
                     |
|...............     |
|              |     |
|              |     |
|              |     |
|              |     |
|...............     |
                     |
\======================

Klar soweit?

(Die Mini-Ascii-Art wieder leider wohl nicht auf jedem Computer korrekt dargestellt)

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

Y
yngwie Themenstarter:in
238 Beiträge seit 2005
vor 16 Jahren

Vielleicht habe ich mich nicht ganz verständlich ausgedrückt, aber die Situation ist wie folgt:

1.) Rectangle rect = new Rectangle(0,0,100,100);
2.) Zeichne rect auf Desktop
3.) rect = new Rectangle(0,0,100,50);
4.) Eine RECT-Struktur erstellen und diese mit rect initializieren.
5.) Rufe InvalidateRect (IntPtr.Zero, ref RECT, true);
6.) rect ist nach Schritt 3 nur halb so hoch, also soll InvalidateRect nur die obere Hälfte davon wegsäbeln und muß die untere Hälfte aber stehen lassen.

leider ist es nicht der Fall, zudem flackert das ganze auch noch...

Y
yngwie Themenstarter:in
238 Beiträge seit 2005
vor 16 Jahren

Weiss vielleicht jemand wie Windows es hin kriegt? Ich meine bei Icons werden ja auch nur bestimmte Regionen des Desktops neugezeichnet, und das ohne das Flackern...

29 Beiträge seit 2007
vor 16 Jahren

Hallo yngwie,

vielleicht suchst du nach dem:

GetPixel und SetPixel um Längen geschlagen. 800 mal schneller

mfg EggaPauli

Y
yngwie Themenstarter:in
238 Beiträge seit 2005
vor 16 Jahren

Hallo EggaPauli,

ich habe mir diesen interessanten Artikel kurz angeschaut. Es scheint dass darin das Thema "wie kann ich sehr schnell auf ein Bitmap zeichnen" behandelt wird, aber es geht anscheinend nicht um das Zeichnen auf den Desktop direkt🙁 Oder habe ich da was übersehen?

Gruß

29 Beiträge seit 2007
vor 16 Jahren

Morgen yngwie!

Geht das nicht, dass du statt dem Invalidaten des Rechtecks nur ein paar Pixel ersetzt? So meinte ich das eigentlich, weiß aber auch nicht ob das geht!

mfg EggaPauli

230 Beiträge seit 2007
vor 16 Jahren

Original von yngwie
Weiss vielleicht jemand wie Windows es hin kriegt? Ich meine bei Icons werden ja auch nur bestimmte Regionen des Desktops neugezeichnet, und das ohne das Flackern...

Das geht mit der API Funktion RedrawWindow (siehe MSDN).

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo EggaPauli,

Geht das nicht, dass du statt dem Invalidaten des Rechtecks nur ein paar Pixel ersetzt? So meinte ich das eigentlich, weiß aber auch nicht ob das geht!

man kann Invalidate mit Parametern aufrufen, so das nur der tatsächlich geänderte Bereich neu gezeichnet wird.

herbivore

29 Beiträge seit 2007
vor 16 Jahren

Morgen!

man kann Invalidate mit Parametern aufrufen, so das nur der tatsächlich geänderte Bereich neu gezeichnet wird

Hm cool, hab ich auch nicht gewusst.

Aber wenn du das Flackern wegbringen willst, kannst doch DoubleBuffer auch einfach nehmen oder?

mfg EggaPauli

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo EggaPauli,

ja, klar, wenn das hilft, kann man auch DoubleBuffer nehmen.

Wenn sich jedoch nur ein kleiner Ausschnitt (oft) ändert, ist natürlich auch die Performance besser, wenn man nur den Ausschnitt und nicht die ganze Zeichenfläche neu zeichnet.

herbivore