| 

.NET C# Java Javascript Exception

4
Hallo zusammen,

ich habe vor, Rahmen von bestimmten Textboxen einzufärben. Dazu habe ich folgende, praktische Sub im Web gefunden:

Private Sub BorderColor(ByRef _Control As Control, ByVal _Color As Color)
Dim g As Graphics = Me.CreateGraphics
Dim pen As New Pen(_Color, 2.0)
g.DrawRectangle(pen, New Rectangle(_Control.Location, _Control.Size))
pen.Dispose()
g = Nothing
End Sub

Ergänzt mit

Private Sub Me_Paint(ByVal sender As Object, ByVal e As _
System.Windows.Forms.PaintEventArgs) Handles Me.Paint
BorderColor(TextBox1, Color.Red)
BorderColor(TextBox6, Color.Red)
End Sub

läuft das für die Textbox1 ja ganz wunderbar. Textbox6 dagegen liegt in einer Tabpage, und da die Location eine relative Angabe ist, wollte ich schlau sein und habe in der Sub BorderColor die Zeile, in der gezeichnet wird, durch folgendes ersetzt:

g.DrawRectangle(pen, New Rectangle(_Control.Location + _Control.Parent.Location, _Control.Size))

Das Blöde daran ist, dass mit der Ergänzung die Textbox1 immer noch richtig gezeichnet wird, aber Textbox6 (in der Tabpage) immer noch nicht. Wie bekomme ich das hin?
News:
20.07.2012
muffi 1,3k 1 9
Was zeichnet denn nicht richtig in der ersten Version? Die Location eines Controls bezieht sich immer auf den Parent-Container. Das Koordinatensystem muss zu deinem drawing context passen (Graphics g bei dir).
puls200 20.07.2012
Die erste Version zeichnet insofern nicht richtig, dass die Textbox6 (die in einer Tabpage liegt), nicht gezeichnet wird. Nur Textbox1 wird gezeichnet.
muffi 20.07.2012
3 Antworten
2
Hier Beispielcode, der bei mir funktioniert:
(Allerdings in C#, da ich mit der VB.NET-Syntax vollständig unvertraut bin, allerdings sollte das relativ einfach übertragbar sein)

Das wäre dein Ansatz:
private static void PaintControlBorder(Graphics g, Control control, Color color)
{
using (Pen pen = new Pen(color, 2))
g.DrawRectangle(pen, new Rectangle(control.Location, control.Size));
}

private static void PaintBorders(Control container, Graphics g)
{
// Alle TextBoxen umranden
foreach (var tb in container.Controls.OfType<TextBox>())
PaintControlBorder(g, tb, Color.Red);
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
// Das Graphics-Objekt des Events verwenden
PaintBorders((sender as Control), e.Graphics);
}

private void tabPage1_Paint(object sender, PaintEventArgs e)
{
// Das Graphics-Objekt des Events verwenden
PaintBorders((sender as Control), e.Graphics);
}


IMHO ist es aber besser, ein UserControl zu verwenden:
Variante 1, Hooken des Paint-Events des Handlers:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TextBoxSampleRect
{
// Diese Variante ist eine normale TextBox
// klinkt sicher aber in die Paint-Routine ihres Parents ein
public partial class BorderedTextBox_Var1 : TextBox
{
Color _borderColor = Color.Red;

/// <summary>
/// Farbe des Rahmens
/// </summary>
[Browsable(true)]
[Category("layout")]
public Color BorderColor
{
get
{
return _borderColor;
}
set
{
if (_borderColor != value)
{
_borderColor = value;
if (Parent != null)
Parent.Refresh();
}
}
}

Control _oldParent = null;

protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);

if (_oldParent != null)
{
_oldParent.Paint -= PaintHook;
}

if (Parent != null)
Parent.Paint += PaintHook;

_oldParent = Parent;
}

private void PaintHook(object sender, PaintEventArgs e)
{
PaintControlBorder(e.Graphics, this, BorderColor);
}

private static void PaintControlBorder(Graphics g, Control control, Color color)
{
using (Pen pen = new Pen(color, 2))
g.DrawRectangle(pen, new Rectangle(control.Location, control.Size));
}
}
}


Variante 2
Die sauberste Methode, da sie nicht auf die OnPaint-Routine des Parents angewiesen ist, allerdings gehen erst mal alle TextBox-Felder und Events verloren und du musst sie manuell "durchschleifen".
Erstell hierzu ein neues userCOntrol und füge eine TextBox hinzu und folgenden Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TextBoxSampleRect
{
// Diese Variante funktioniert auch, falls sich der Parent komisch im OnPaint verhält etc.
// allerdings musst du alle Eigenschaften und/oder Ereignisse der TextBox, die du brauchst, "durchschleifen"
// da sie sonst nach außen nicht sichtbar sind

public partial class BorderedTextBox_Var2 : UserControl
{
public BorderedTextBox_Var1()
{
InitializeComponent();

textBox1.Left = 1;
textBox1.Top = 1;
this.Height = textBox1.Height + 2;
}

[Browsable(true)]
public new String Text
{
get
{
return textBox1.Text;
}
set
{
textBox1.Text = value;
}
}

protected override void OnResize(EventArgs e)
{
base.OnResize(e);

textBox1.Left = 1;
textBox1.Top = 1;
textBox1.Width = this.ClientSize.Width - 2;
this.Height = textBox1.Height + 2;
}
}
}
20.07.2012
LunaticShade 498 4
So, das hast Du jetzt davon! Weil Variante 1 nach dem Konvertieren auf VB genau das macht, was ich mir vorgestellt habe, gibt's den grünen Haken. Vielen Dank!!!
muffi 23.07.2012
2
Ein eigenständiges Control (siehe Variante2 von LunaticShade oder eine Ableitung von TextBox) ist eine Möglichkeit, die man in Betracht ziehen kann.

WinForms bietet mit dem IExtenderProvider Interface etwas ähnliches wie AttachedProperties in WPF. Man kann Controls mit Properties erweitern. In der MSDN findet sich tatsächlich ein Beispiel, das zwar ein HelpLabel über IExtenderProvider zur Verfügung stellt, aber auch einen Rahmen um das zu erweiternde Control zeichnet.
Siehe How to: Implement a HelpLabel Extender Provider.

Meine Vermutung bzgl. Deiner Implementierung ist, dass Du die Koordinatensysteme nicht ineinander überführst.
Wenn Du OnPaint Deines Formulars überschreibst (das würde ich definitiv an Stelle des Behandelns des Paint-Events empfehlen), ist der Nullpunkt Deines Graphics-Objekts in der linken oberen Ecke des Formulars.
Die Textbox in Deiner TabPage besitzt aber eine Location bezüglich Ihres Parent-Containers, in diesem Fall der TabPage.
Um nun trotzdem im OnPaint des Formulars einen Rahmen um die TextBox in der TabPage zu zeichnen, rechnest Du zuerst die Koordinaten der TextBox in Bildschirmkoordinaten um, um diese anschließend ins Koordinatensystem des Formulars umzurechnen. Danach sollte der Rahmen an der richtigen Stelle sitzen.

Hier meine Variante Deiner BorderColor-Methode:
Private Sub BorderColor(ByRef _Control As Control, ByVal _Color As Color)
Dim g As Graphics = _Control.Parent.CreateGraphics
Dim pen As New Pen(_Color, 2.0)
Dim locationInForm as Point

locationInForm = Me.PointToClient(_Control.PointToScreen(_Control.Location))

g.DrawRectangle(pen, New Rectangle(locationInForm, _Control.Size))

pen.Dispose()
g.Dispose() 'nicht vergessen, das selbst erzeugte Graphics zu disposen
'g = Nothing 'überflüssig
End Sub

HTH
20.07.2012
ffordermaier 8,4k 2 9
1
Bei deiner Variante wird der Rahmen zwar an die richtige Stelle, aber eben auf die Form gezeichnet. Und darüber wird dann die TabPage gezeichnet, und der Rahmen ist nicht sichtbar. Wenn, dann musst CreateGraphics für den Parent der TextBox, die du umrandne willst, aufrufen.
LunaticShade 22.07.2012
Da hast Du natürlich vollkommen Recht. Habs in der Antwort berücksichtigt. Danke.
ffordermaier 22.07.2012
0
So wie ich das sehe, zeichnest du direkt in/auf den Parent.
Du müsstest also CreateGraphics für den Parent der jeweiligen TextBox aufrufen (bei TextBox6 die TabPage) um dessen Zeichenoberfläche zu erhalten.
Allerdings ist das etwas unglücklich, da man innerhalb der OnPaint-Methode nicht in Controls zeichnen sollte, in deren OnPaint man sich nicht befindet (s. auch hier).
Wenn du Pech hast wird OnPaint für den Parent nach dem der TextBox ausgelöst, und alle deine Änderugnen werden wieder "übermalt".
Besser wäre es, wenn du das Zeichnen der Umrandungen in das OnPaint-Ereignis der entsprechenden Parents der TextBoxen auslagerst (und nicht CreateGraphics sondern dann entsprechend das übergebene e.Graphics verwendest). Oder mach dir ein UserControl mit TextBox, welches etwas größer als die TextBox ist und als Hintergrund deine Rahmenfarbe hat.
20.07.2012
LunaticShade 498 4
Mit ... Handles TabPage1.Paint habe ich es auch probiert. "Glücklicheweise" liegt die TextBox6 ziemlich weit oben in der Tabpage. Und so sehe ich, dass der rote Rahmen oberhalb vom Tabcontrol wild ins Formular gepinselt wird. Deshalb hatte ich ja die Version mit +_Control.Parent.Location probiert, die zu keinem (sichtbaren) Ergebnis führt. Ich bin im Moment total planlos, wie ich das machen kann...
muffi 20.07.2012

Stelle deine .net-Frage jetzt!