none
Form mit Rand? (BorderStyle = None) RRS feed

  • Frage

  • Guten Morgen!

    Wenn ich den FormBorderStyle auf "None" gesetzt habe gibt es dann irgendwie eine Möglichkeit einen Rand anzuzeigen? 

    Dienstag, 22. Januar 2013 08:07

Antworten

  • Na sowas. Dann würde ich dich bitten, deine Testanwendung auf SkyDrive oder vergleichbares hochzuladen, damit ich mir sie mal ansehe und herausfinde, warum sie nicht so aussieht:

    ;-)

    P.S. Das Uploaden ist nicht mehr notwendig. Du hast - warum auch immer - die BackgroundColor-Eigenschaft deiner Controls auf Transparent gesetzt. Ändere das mal auf SystemColors.Control und gut ist. Tipp: Damit Du dir nicht in die Quere kommst mit dem Zeichnen der anderen Controls, kannst Du im Konstruktor deiner Form noch folgendes hinzufügen:

    public SolidBorderForm()
    {
        InitializeComponent();
        Padding = new Padding(WIDTH);
    }
    

    Dienstag, 22. Januar 2013 12:52
    Moderator
  • Gerne. Schön, dass es auch bei dir geklappt hat.

    Meine ersten Schritte in Sachen Programmierung stammen aus den späten Jahren des Triumph Adler Alphatronic PCs, als die PCs noch keine Festplatten hatten, Betriebsysteme vom Floppy geladen wurden und Monitore mit orangener Schrift als fortschrittlich galten. Diese Erfahrung ist nicht immer ein Vorteil. Sie hat aber mir und meinen Kunden so manches mal aus der Patsche geholfen.

    Bis zum nächsten Mal.

    • Als Antwort markiert XxDeadLiiNexX Dienstag, 22. Januar 2013 15:34
    Dienstag, 22. Januar 2013 15:04
    Moderator

Alle Antworten

  • Guten Morgen XxDeadLiiNexX,

    Ja, Du könntest z.B. den Rand selber zeichnen:

    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class SolidBorderForm : Form
        {
            public SolidBorderForm()
            {
                InitializeComponent();
                FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            }
    
            private const int WIDTH = 3;
            Color COLOR = Color.BlueViolet;
    
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                ControlPaint.DrawBorder(e.Graphics, 
                    e.ClipRectangle,                        // Rahmen-Rechteck
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: LINKS 
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: OBEN 
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: RECHTS  
                    COLOR, WIDTH, ButtonBorderStyle.Solid); // Rahmen-Farbe, -Breite und -Art: UNTEN
            }
        }
    }

    Das könnte dann z.B. so aussehen:


    Gruß
    Marcel




    Dienstag, 22. Januar 2013 08:41
    Moderator
  • Erstmal Dankeschön ! 

    Aber jetzt ist alles umrandet :-O 

    Wie kann ich ihm sagen das er nur die Form umranden soll? 

    Dienstag, 22. Januar 2013 09:00
  • Aber jetzt ist alles umrandet [...] Wie kann ich ihm sagen das er nur die Form umranden soll? 
    Wenn Du mit "alles" das Hauptfenster deiner Applikation meinst, dann hast Du den Code in die falschen Klasse eingefügt. Öffne den Code-Editor deiner rahmenlosen Form und füge den Code dort ein.
    Dienstag, 22. Januar 2013 09:12
    Moderator
  • Moin,

    Meinte eigentlich alle Steuerelement :-S Also jeder Button usw. ... 

    Hab den Code in der Form eingefügt in welcher der BorderStyle "None" ist. 

    Mfg

    Dienstag, 22. Januar 2013 09:25
  • Das klingt in der Tat sehr unterhaltsam. Poste mal bitte eine Bildschirmkopie und deinen reproduzierbaren Code.  Oder benutze den zwischenzeitlich erweiterten Code im letzten Posting zum Testen.
    Dienstag, 22. Januar 2013 09:32
    Moderator
  • Erstmal danke für die Antwort und den neuen Code !

    Das gibts nicht :-S 

    Wenn ich ne neue Windows Forms Anwendung mache ein paar Buttons hineinziehe und deinen Code reinpacke funktioniert es! Ist übrigens genau das was ich brauche :-P

    Aber bei meiner jetzigen Anwendung funktioniert es nicht, er umrandet alle Steuerelement mhm... 


    Dienstag, 22. Januar 2013 09:53
  • Dienstag, 22. Januar 2013 10:06
  • Wenn ich ne neue Windows Forms Anwendung mache ein paar Buttons hineinziehe und deinen Code reinpacke funktioniert es! Ist übrigens genau das was ich brauche

    Wunderbar.

    Aber bei meiner jetzigen Anwendung funktioniert es nicht, er umrandet alle Steuerelemente

    Cool. Deinen Code, der imstande ist, über ControlPaint.DrawBorder(e.Graphics, ...) auf die GDI+-Zeichnungsoberfläche anderer Controls zu zeichnen, möchte ich unbedingt sehen. Und ich meine deinen Code, nicht meinen.


    Dienstag, 22. Januar 2013 10:11
    Moderator
  • Hhahaha ja was ist da dann los O.o 

    Ist ja genau der selbe Code den ich bei der nackten Windows Form verwende ( siehe Bild )


    Dienstag, 22. Januar 2013 11:29
  • Na sowas. Dann würde ich dich bitten, deine Testanwendung auf SkyDrive oder vergleichbares hochzuladen, damit ich mir sie mal ansehe und herausfinde, warum sie nicht so aussieht:

    ;-)

    P.S. Das Uploaden ist nicht mehr notwendig. Du hast - warum auch immer - die BackgroundColor-Eigenschaft deiner Controls auf Transparent gesetzt. Ändere das mal auf SystemColors.Control und gut ist. Tipp: Damit Du dir nicht in die Quere kommst mit dem Zeichnen der anderen Controls, kannst Du im Konstruktor deiner Form noch folgendes hinzufügen:

    public SolidBorderForm()
    {
        InitializeComponent();
        Padding = new Padding(WIDTH);
    }
    

    Dienstag, 22. Januar 2013 12:52
    Moderator
  • Du bist ein Gott !!! xD

    Das schon das zweite mal das ich wegen der Transparenz Probleme hatte @-:  ... 

    Großes Dankeschön !!! 

    Wie lange programmierst du eigentlich schon? 

    Mfg

    Dienstag, 22. Januar 2013 14:44
  • Gerne. Schön, dass es auch bei dir geklappt hat.

    Meine ersten Schritte in Sachen Programmierung stammen aus den späten Jahren des Triumph Adler Alphatronic PCs, als die PCs noch keine Festplatten hatten, Betriebsysteme vom Floppy geladen wurden und Monitore mit orangener Schrift als fortschrittlich galten. Diese Erfahrung ist nicht immer ein Vorteil. Sie hat aber mir und meinen Kunden so manches mal aus der Patsche geholfen.

    Bis zum nächsten Mal.

    • Als Antwort markiert XxDeadLiiNexX Dienstag, 22. Januar 2013 15:34
    Dienstag, 22. Januar 2013 15:04
    Moderator
  • Das warn noch Zeiten :-P 

    Wäre da gerne schon auf der Welt gewesen gg*

    Ok Grüße ! 

    Dienstag, 22. Januar 2013 15:34
  • Na sowas. Dann würde ich dich bitten, deine Testanwendung auf SkyDrive oder vergleichbares hochzuladen, damit ich mir sie mal ansehe und herausfinde, warum sie nicht so aussieht:

    ;-)

    P.S. Das Uploaden ist nicht mehr notwendig. Du hast - warum auch immer - die BackgroundColor-Eigenschaft deiner Controls auf Transparent gesetzt. Ändere das mal auf SystemColors.Control und gut ist. Tipp: Damit Du dir nicht in die Quere kommst mit dem Zeichnen der anderen Controls, kannst Du im Konstruktor deiner Form noch folgendes hinzufügen:

    public SolidBorderForm()
    {
        InitializeComponent();
        Padding = new Padding(WIDTH);
    }

    Moin Marcel Roma!

    ich würde nochmal deine Hilfe benötigen, und zwar hab ich mit dieser Zeichen Methode folgendes Problem:

    Wenn das Programm startet und ich es anschließend nehmen und beim Monitor Rand hinaus schiebe und anschließend wieder rein ziehe sind alles Striche vom sozusagen Monitor Rand in der Form. 

    Kann man dagegen was machen?

    Mit freundlichen Grüßen!

    P.S das ist ein anders Konto alos nicht wundern gg*

    Samstag, 23. November 2013 11:20
  • Moin moin.

    mit welcher Zeichenmethode hat Du denn Probleme? Könntest Du eine Bildschirmkopie posten? Ist das Problem auch mit anderen Grafikkarten, auf anderen Systemen replizierbar?

    Gruß
    Marcel

    Samstag, 23. November 2013 12:05
    Moderator
  • Moin moin.

    mit welcher Zeichenmethode hat Du denn Probleme? Könntest Du eine Bildschirmkopie posten? Ist das Problem auch mit anderen Grafikkarten, auf anderen Systemen replizierbar?

    Gruß
    Marcel

    Moin!

    Freut mich erstmal das du antwortet, ja das Problem ist replizierbar, denke das es am Code liegt und man die Form sozusagen schützen muss. Verwenden tu ich denn Code denn du oben/damals gepostet hast.

    Ich schick poste jetzt einfach 2 Screenshots dazu dann weisst du bestimmt was ich mein :-)

     

    Beim ersten Bild befindet sich das Programm ganz normal am Desktop, dann zieh ich es mit der Maus über denn Bildschirm Rand hinaus, als würde ich es verstecken wollen gg*

    Beim zweiten Bild zieh ich das Programm was hinter dem Bildschirm Rand steckt wieder rein, jetzt sind die ganzen Striche vom Bildschirm Rand auf der Form.

    Mfg! :D

    Montag, 25. November 2013 07:42
  • Hallo,

    So wie's aussieht, hat deine Anwendung nicht genügend Zeit/Ressourcen zur Verfügung, die erhaltenen WM_PAINT-Nachrichten im OnPaint zu verarbeiten. Die Verarbeitung geschieht dadurch intermitent; deshalb die gestrichelten Linien auf dem Bildschirm.

    Versuchst Du die Form zu animieren (z.B. aus dem Off-Screenbereich über eine Reihe von Animationsschritten wieder in den sichtbaren Bereich zu bringen)? Dann musst Du auch dafür sorgen, dass dies asynchron passiert und die Verarbeitung der Windows-Nachrichten noch möglich ist. So hat deine Anwendung genug "Luft" um richtig zu "sitzen" und zu "wackeln".

    Überprüfe also ob Du DoubleBuffering verwendest, ob enge Schleifen beim Bewegen des Fensters alle Ressourcen binden, ob ein Timer dabei häufig feuert etc.

    Idealerweise verschiebst Du dann alle blockierenden Aktionen auf einen Nebenthread damit die UI zeitnah auf Benutzerinteraktion reagieren kann.

    Gruß
    Marcel

    Montag, 25. November 2013 08:43
    Moderator
  • Hallo,

    anstatt e.ClipRectangle sollte man den kompletten Rahmen zeichnen, denn ClipRectangle optimiert ggf. Teile des Randes weg (weil nicht sichtbar) - was zu einem falschen Rahmen führen kann:

                var bounds = new Rectangle(Point.Empty, this.Size);
                ControlPaint.DrawBorder(e.Graphics,
                    bounds,                        // Rahmen-Rechteck
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: LINKS 
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: OBEN 
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: RECHTS  
                    COLOR, WIDTH, ButtonBorderStyle.Solid); // Rahmen-Farbe, -Breite und -Art: UNTEN
    
    Gruß Elmar
    • Als Antwort vorgeschlagen XxDeadLiinexXx Montag, 25. November 2013 12:21
    Montag, 25. November 2013 09:36
    Beantworter
  • Hi Elmar,

    es funktioniert auch mit ClipRectangle, wenn das Zeichnen im OnPaint rechtzeitig ausgeführt wird und DoubleBuffering eingeschaltet ist. Hab's gerade mal ausprobiert.

    Gruß
    Marcel

    Montag, 25. November 2013 09:58
    Moderator
  • Hallo Marcel,

    ich hatte es ohne DoubleBuffer getestet, um keine Zusatzbedingungen einzuführen, da klapperts nicht ;))

    Und das nebenbei geloggte ClipRectangle lieferte optimierte (und somit falsche Werte).

    Gruß Elmar

    Montag, 25. November 2013 10:14
    Beantworter
  • Hi Elmar,

    ClipRectangle ist nicht das eigentliche Problem hier. Der geloggte Wert mag zwar nicht der sein, den Du erwartest, und er mag nicht zur der aktuellen Location passen. Aber er war sicherlich gültig zum Zeitpunkt als ClipRectangle für den zu zeichnenden Teilbereich gesetzt wurde.

    Die Location-Änderung geschieht jedoch so schnell, dass die WM_PAINT Nachrichten zu einem Zeitpunkt eintreffen bzw. verarbeitet werden könne, wo die Werte nicht mehr aktuell sind.

    Indem man in OnPaint mit der aktuellen Größe des Clients arbeitet, umgeht man das Problem zum größten Teil, da hast Du Recht. Aber es ist nicht der einzige Weg mit dem eigentlichen Problem umzugehen.

    Durch Setzen von this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true) kann man dafür sorgen, dass das Zeichnen "am Stück" und in OnPaint() geschieht, was für ein flüssigeres Zeichnen sorgt. Dies wird umso wichtiger, je größer die Strecke ist über die man die Form bewegt.

    Durch Auslagerung des Codes zum Ändern der Location auf ein Nebenthread, wird der aktuelle Thread von Blockaden befreit und kann die WM_Nachrichten schneller bearbeiten.

    Durch Setzen von GCSettings.LatencyMode = GCLatencyMode.LowLatency kann man während der Animation dafür sorgen, dass der GC nicht den Thread unterbricht.

    Hier ein kurzes Konzept, damit Du besser verstehst, was ich meine:
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Runtime;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public class FormSlidingAnimation : IDisposable
        {
            private const int DEFAULT_FRAME_RATE = 35;
            private const int DEFAULT_INCREMENT = 35;
    
            private readonly SynchronizationContext _syncContext;
            private readonly Form _formToSlideIntoView;
            private AutoResetEvent _autoResetEvent;
            private Point _currentXLocation;
            private System.Threading.Timer _timer;
    
            public FormSlidingAnimation(Form formToSlideIntoView)
            {
                FramesPerSecond = DEFAULT_FRAME_RATE;
                LocationIncrement = DEFAULT_INCREMENT;
    
                // Für eine flüssigere Darstellung sollte man im
                // Konstruktor von formToSlideIntoView folgende Zeile
                // einfügen:
                // this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true)
    
                _formToSlideIntoView = formToSlideIntoView;
                _currentXLocation = _formToSlideIntoView.Location;
                _syncContext = SynchronizationContext.Current;
            }
    
            public void Start()
            {
                GCSettings.LatencyMode = GCLatencyMode.LowLatency;
    
                _autoResetEvent = new AutoResetEvent(false);
                Thread timerThread = new Thread(OnRunTimerThread);
                timerThread.Start();
            }
    
            private void OnRunTimerThread()
            {
                var period = 1000 / FramesPerSecond;
                _timer = new System.Threading.Timer(OnTimerCallback, null, 0, period);
            }
    
            private void OnTimerCallback(object state)
            {
                if (_currentXLocation.X < (Screen.PrimaryScreen.WorkingArea.Width / 2) - (_formToSlideIntoView.Width / 2))
                {
                    _currentXLocation = new Point(_currentXLocation.X, (Screen.PrimaryScreen.WorkingArea.Height / 2) - (_formToSlideIntoView.Height / 2));
                    _syncContext.Post(_ =>
                    {
                        _formToSlideIntoView.Location = _currentXLocation;
                        _formToSlideIntoView.Invalidate();
                    }, _currentXLocation);
                    _currentXLocation.X += LocationIncrement;
                }
                else
                {
                    _timer.Change(Timeout.Infinite, Timeout.Infinite);
                    _autoResetEvent.Set();
                    _timer.Dispose();
                    GCSettings.LatencyMode = GCLatencyMode.Interactive;
                }
            }
    
            public int FramesPerSecond { get; set; }
            public int LocationIncrement { get; set; }
    
            public void Dispose()
            {
                if (_timer != null) _timer.Dispose();
                if (_formToSlideIntoView != null) _formToSlideIntoView.Dispose();
                if (_autoResetEvent != null) {
                    _autoResetEvent.Dispose();
                    _autoResetEvent = null;
                }
            }
        }
    }


    Verwendung der Klasse aus der zu verschiebenden Form-Klasse (deren UI sich ausserhalb des Screens befindet):

    FormSlidingAnimation animation = new FormSlidingAnimation(this);
    animation.Start();
    

    Noch bessere Performance erzielt man, indem man nicht die Form, sondern ein Abbild der Form über den Bildschirm bewegt und erst zuguterletzt die Form hinter dem Bild einblendet.

    Ich konnte keine großen Unterschiede feststellen, zwischen der Verwendung von ClipRectangle und new Rectangle(Point.Empty, this.Size). Aber vielleicht kannst Du einiges mehr dazu sagen/schreiben.

    Gruß
    Marcel

    Montag, 25. November 2013 11:19
    Moderator
  • Hallo Marcel,

    man kann natürlich optimieren usw. - aber auch minimalistisch geht es:

    using System.Drawing;
    using System.Windows.Forms;
    
    namespace ElmarBoye.Samples.Forms
    {
        public partial class SolidBorderForm : Form
        {
            private const int WIDTH = 3;
            Color COLOR = Color.BlueViolet;
    
            public SolidBorderForm()
            {
                InitializeComponent();
    
                base.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
                //base.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    
                base.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
                base.Size = new Size(800, 600);
                base.Padding = new Padding(WIDTH);
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                var bounds = new Rectangle(Point.Empty, this.Size);
                //Console.WriteLine("{0} => {1}", bounds, e.ClipRectangle);
                
                ControlPaint.DrawBorder(e.Graphics,
                    bounds, 
                    COLOR, WIDTH, ButtonBorderStyle.Solid,
                    COLOR, WIDTH, ButtonBorderStyle.Solid,
                    COLOR, WIDTH, ButtonBorderStyle.Solid,
                    COLOR, WIDTH, ButtonBorderStyle.Solid);
            }
    
            private bool isDragging;
            private Point dragLocation;
            protected override void OnMouseDown(MouseEventArgs e)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    this.isDragging = true;
                    this.dragLocation = new Point(e.X, e.Y);
                }
            }
    
            protected override void OnMouseUp(MouseEventArgs e)
            {
                this.isDragging = false;
            }
    
            protected override void OnMouseMove(MouseEventArgs e)
            {
                if (this.isDragging)
                {
                    Point location = PointToScreen(e.Location);
                    this.Location = new Point(location.X - this.dragLocation.X, location.Y - this.dragLocation.Y);
                }
            }
        }
    }
    

    (Das Verschieben ist zum Testen nur halbseiden implementiert ;)

    Gruß Elmar

    Montag, 25. November 2013 11:57
    Beantworter
  • Hallo,

    anstatt e.ClipRectangle sollte man den kompletten Rahmen zeichnen, denn ClipRectangle optimiert ggf. Teile des Randes weg (weil nicht sichtbar) - was zu einem falschen Rahmen führen kann:

                var bounds = new Rectangle(Point.Empty, this.Size);
                ControlPaint.DrawBorder(e.Graphics,
                    bounds,                        // Rahmen-Rechteck
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: LINKS 
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: OBEN 
                    COLOR, WIDTH, ButtonBorderStyle.Solid,  // Rahmen-Farbe, -Breite und -Art: RECHTS  
                    COLOR, WIDTH, ButtonBorderStyle.Solid); // Rahmen-Farbe, -Breite und -Art: UNTEN
    
    Gruß Elmar

    Super das wars, dankschön!

    Großes Danke an alle für die Mühe und den Aufwand :-) 

    Montag, 25. November 2013 12:22
  • Hi XxDeadLiinexXx,

    gerne, vielleicht hilftst Du auch mal in diesem Forum mit. Das wäre toll.

    Mir ist aufgefallen, dass Du einen neuen Kontonamen verwendest (Januar: XxDeadLiiNexX, aktuell: XxDeadLiinexXx) weswegen Du wahrscheinlich in diesem Thread keine Antworten markieren kannst.

    Wenn Du möchtest, kann ich das für dich erledigen, gib einfach Bescheid. Kann ich dir mit dem alten Konto behilflich sein?

    Gruß
    Marcel

    Montag, 25. November 2013 14:01
    Moderator