C# Fehler in TextBox erkennen?
Hallo. Ich schreibe gerade einen 10-Finger schreibtrainer mit zwei TextBoxen. In TextBox 1 (multiline) wird ein text erscheinen welche nicht änderbar ist, da readonly. In TextBox 2 (auch multiline) soll man den text abschreiben. Wie kann man erkennen, ob jemand ein buchstaben falsch eingegeben hat, sprich einen wort falsch schrieb. Kann man das im TextBox 1 rot markieren? Danke.
1 Antwort
In einer einfachen TextBox kann man keine einzelnen Zeichen andersfarbig markieren.
Da es ziemlich mühsam ist, andere TextBoxen darüber zu legen, nimmt man am besten eine RichTextBox -- hier kann man einzelne Zeichen, Abschnitte etc. formatieren.
Übrigens ist die Windows-Anwendung Notepad eine TextBox mit einem Rahmen drumherum und WordPad eine RichTextBox mit einem Rahmen drumherum.
-----
Möglicher Code:
// berechne, bis wohin eingefärbt werden soll - solange wir nicht sicher sind, dass textBox2 höchstens so viele Zeichen enthält wie richTextBox1, sollten wir das so machen, sonst kriegen wir womöglich eine IndexOutOfRangeException.
var lgMin = Math.Min(textBox2.TextLength, richTextBox1.TextLength);
// gehe alle Zeichen bis zur gemeinsamen Länge durch und markiere entsprechend der (Nicht-)Übereinstimmung
for (int i = 0; i < lgMin; i++) {
richTextBox1.Select(i, 1);
if (textBox2.Text[i] == richTextBox1.Text[i]) {
richTextBox1.SelectionColor = Color.Black;
} else {
richTextBox1.SelectionColor = Color.Red;
}
}
// Rest des Originaltextes schwarz einfärben
if (richTextBox1.TextLength - lgMin >= 0) {
richTextBox1.Select(lgMin, richTextBox1.TextLength - lgMin);
richTextBox1.SelectionColor = Color.Black;
}
Ich verwende hier richTextBox1.Select(Start, Länge) -- das ist nur ein einziger Befehl statt 2 wie bei richTextbox1.SelectionStart = ... und richTextbox1.SelectionLength = ...
Das Setzen der Markierung, welches Zeichen als nächstes einzugeben ist, muss natürlich hiernach folgen. (Oder man setzt die Hintergrundfarbe auf die entsprechende Systemfarbe und ggf. die Vordergrundfarbe auch.)
Ob es auch bei Zeilenumbrüchen funktioniert, habe ich nicht probiert. Außerdem sollte man vermutlich zeilenweise vorgehen.
Allerdings wäre es vermutlich sinnvoller, richTextBox1.SelectionBackColor (Hintergrundfarbe statt Textfarbe) zu verwenden, dann fallen Fehler auch bei Leerzeichen auf.
Vielleicht ist es auch besser, die Markierungen in dem Text zu setzen, den der Benutzer eingegeben hat. Also für das Eingabefeld eine richTextBox zu nehmen statt für das Vorlagefeld. (Oder für beide.)
Nach dem Setzen der Einfärbungen muss natürlich wieder das nächste einzutippende Zeichen ausgewählt werden.
Natürlich kann das so sein. Braucht einfach 2 RichTextBoxen.
Ist das so richtig?
private void textBox2_TextChanged(object sender, EventArgs e)
{
var anzahlAnschläge = 0;
for (int i = 0; i < textBox2.Text.Length; i++)
{
anzahlAnschläge++;
if (Char.IsUpper(textBox2.Text[i])) { anzahlAnschläge++; }
}
label2.Text = String.Format("Anschläge: {0}", anzahlAnschläge);
// berechne, bis wohin eingefärbt werden soll - solange wir nicht sicher sind, dass textBox2 höchstens so viele Zeichen enthält wie richTextBox1, sollten wir das so machen, sonst kriegen wir womöglich eine IndexOutOfRangeException.
var lgMin = Math.Min(textBox2.TextLength, richTextBox1.TextLength);
// gehe alle Zeichen bis zur gemeinsamen Länge durch und markiere entsprechend der (Nicht-)Übereinstimmung
for (int i = 0; i < lgMin; i++)
{
richTextBox1.Select(i, 1);
if (textBox2.Text[i] == richTextBox1.Text[i])
{
richTextBox1.SelectionColor = Color.Black;
}
else
{
richTextBox1.SelectionColor = Color.Red;
}
}
// Rest des Originaltextes schwarz einfärben
if (richTextBox1.TextLength - lgMin >= 0)
{
richTextBox1.Select(lgMin, richTextBox1.TextLength - lgMin);
richTextBox1.SelectionColor = Color.Black;
}
richTextBox1.Refresh();
}
Dann funktioniert aber die markierung nicht (mein vorheriger beitrag).
Hier fehlt noch das Markieren des nächsten Buchstabens.
Vor -- oder anstelle von --
richTextBox1.Refresh();
muss noch ein
richTextBox1.Select(textBox2.TextLength, 1);
bzw. besser (falls man in textBox2 zu viel eingibt, wird hiermit die Ausnahme vermieden)
if (textBox2.TextLength < richTextBox1.TextLength) {
richTextBox1.Select(textBox2.TextLength, 1);
} else {
richTextBox1.Select(richTextBox1.TextLength, 0);
}
Natürlich kann man die Fehler zählen. Dazu reicht es, vor die Schleife, wo die Übereinstimmungen geprüft werden, eine neue Variablendefinition zu setzen
var fehleranzahl = 0;
und den Block, der
richTextBox1.SelectionColor = Color.Red;
enthält, um diese Zeile zu ergänzen:
fehleranzahl++;
Code:
charCount = textBox2.Text.Length;
label2.Text = "Anschläge: " + charCount.ToString();
var lgMin = Math.Min(textBox2.TextLength, richTextBox1.TextLength);
for (int i = 0; i < lgMin; i++)
{
richTextBox1.Select(i, 1);
if (textBox2.Text[i] == richTextBox1.Text[i])
{
//richTextBox1.SelectionColor = Color.Black;
richTextBox1.SelectionStart = textBox2.Text.Length;
if (textBox2.TextLength < richTextBox1.TextLength)
{
richTextBox1.Select(textBox2.TextLength, 1);
}
else
{
richTextBox1.Select(richTextBox1.TextLength, 0);
}
}
else
{
richTextBox1.SelectionColor = Color.Red;
}
}
// Rest des Originaltextes schwarz einfärben
if (richTextBox1.TextLength - lgMin >= 0)
{
richTextBox1.Select(lgMin, richTextBox1.TextLength - lgMin);
richTextBox1.SelectionColor = Color.Black;
}
richTextBox1.Select(textBox2.TextLength, 1);
richTextBox1.Refresh();
Ja, sonst wird der ganze Text ausgewählt. Habe es auf false gestellt und es funktioniert plötzlich. Kann man auch das kopieren des Textes blockieren?
Vor dem if-Block wird das Zeichen an der Stelle i selektiert und im "then"-Block unter if wird SelectionStart wieder auf textBox2.Text.Length gesetzt. Dadurch wird das erste Select doch sinnlos.
Enabled-Eigenschaft auf false setzen.
Allerdings kann man den Text dann immer noch über das Windows-API auslesen (z. B. über AutoHotkey oder AutoIt).
musste es aber auf true setzen, da man sonst nicht scrollen kann
Ruckeln sollte es eigentlich nicht -- aber wenn doch, dann färbe das zu markierende Zeichen mit den Markierungsfarben ein:
// einzutippendes Zeichen markieren
if (richTextBox1.TextLength - lgMin >= 0) {
richTextBox1.Select(lgMin, 1);
richTextBox1.SelectionColor = SystemColors.HighlightText;
richTextBox1.SelectionBackColor = SystemColors.Highlight;
}
An den Anfang muss dann die Markierung zurückgenommen werden, sonst verschwindet sie nicht:
richTextBox1.SelectAll();
richTextBox1.SelectionColor = SystemColors.WindowText;
richTextBox1.SelectionBackColor = SystemColors.Window;
Man kann den Text vom oberen richTextBox kopieren und unten einfügen. Wie kann man das blockieren?
Die Fehler sind nun nicht mehr rot.
Code:
private void textBox2_TextChanged(object sender, EventArgs e)
{
charCount = textBox2.Text.Length;
label2.Text = "Anschläge: " + charCount.ToString();
// berechne, bis wohin eingefärbt werden soll - solange wir nicht sicher sind, dass textBox2 höchstens so viele Zeichen enthält wie richTextBox1, sollten wir das so machen, sonst kriegen wir womöglich eine IndexOutOfRangeException.
var lgMin = Math.Min(textBox2.TextLength, richTextBox1.TextLength);
// gehe alle Zeichen bis zur gemeinsamen Länge durch und markiere entsprechend der (Nicht-)Übereinstimmung
for (int i = 0; i < lgMin; i++)
{
richTextBox1.Select(i, 1);
if (textBox2.Text[i] == richTextBox1.Text[i])
{
//richTextBox1.SelectionColor = Color.Black;
richTextBox1.SelectionStart = textBox2.Text.Length;
if (textBox2.TextLength < richTextBox1.TextLength)
{
richTextBox1.Select(textBox2.TextLength, 1);
}
else
{
richTextBox1.Select(richTextBox1.TextLength, 0);
}
}
else
{
richTextBox1.SelectionColor = Color.Red;
}
}
// Rest des Originaltextes schwarz einfärben
richTextBox1.SelectAll();
richTextBox1.SelectionColor = SystemColors.WindowText;
richTextBox1.SelectionBackColor = SystemColors.Window;
if (richTextBox1.TextLength - lgMin >= 0)
{
richTextBox1.Select(lgMin, 1);
richTextBox1.SelectionColor = SystemColors.HighlightText;
richTextBox1.SelectionBackColor = SystemColors.Highlight;
}
richTextBox1.Refresh();
}
Wenn richTextBox1.Enabled == false ist, sollte man mit dem Text gar nichts mehr machen können.
Wenn die Einfärbung stört, kann man die Box in einen Container packen, der Enabled == false hat.
Die Fehler werden nicht mehr rot angezeigt. Code ist oben.
Hast du das Konzept der Schleife verstanden?
(Ich meine generell das Konzept der Wiederholung bestimmter Programmanweisungen.)
Dann überleg dir, in welcher Zeile welcher Bereich des Textes markiert ist und folglich von den Formatierungen betroffen ist. Und welche Textbereiche mehrfach formatiert werden -- bei jedem Schleifendurchlauf einmal.
Passt dieser Code nicht?
private void textBox2_TextChanged(object sender, EventArgs e)
{
charCount = textBox2.Text.Length;
label2.Text = "Anschläge: " + charCount.ToString();
// berechne, bis wohin eingefärbt werden soll - solange wir nicht sicher sind, dass textBox2 höchstens so viele Zeichen enthält wie richTextBox1, sollten wir das so machen, sonst kriegen wir womöglich eine IndexOutOfRangeException.
var lgMin = Math.Min(textBox2.TextLength, richTextBox1.TextLength);
// gehe alle Zeichen bis zur gemeinsamen Länge durch und markiere entsprechend der (Nicht-)Übereinstimmung
for (int i = 0; i < lgMin; i++)
{
richTextBox1.Select(i, 1);
if (textBox2.Text[i] == richTextBox1.Text[i])
{
//richTextBox1.SelectionColor = Color.Black;
richTextBox1.SelectionStart = textBox2.Text.Length;
if (textBox2.TextLength < richTextBox1.TextLength)
{
richTextBox1.Select(textBox2.TextLength, 1);
}
else
{
richTextBox1.Select(richTextBox1.TextLength, 0);
}
}
else
{
richTextBox1.SelectionColor = Color.Red;
}
}
// Rest des Originaltextes schwarz einfärben
if(richTextBox1.TextLength - lgMin >= 0) {
richTextBox1.SelectAll();
richTextBox1.SelectionColor = SystemColors.WindowText;
richTextBox1.SelectionBackColor = SystemColors.Window;
richTextBox1.Select(lgMin, 1);
richTextBox1.SelectionColor = SystemColors.HighlightText;
richTextBox1.SelectionBackColor = SystemColors.Highlight;
}
richTextBox1.Select(textBox2.TextLength, 1);
richTextBox1.Refresh();
}
//richTextBox1.SelectionColor = Color.Black;
Warum hast du diese Zeile auskommentiert? (Ok, es geht schneller ohne diese Zeile. Kann man machen, wenn man vor der Schleife allen Text auf Schwarz setzt.)
richTextBox1.SelectionStart = textBox2.Text.Length;
if (textBox2.TextLength < richTextBox1.TextLength)
{
richTextBox1.Select(textBox2.TextLength, 1);
}
else
{
richTextBox1.Select(richTextBox1.TextLength, 0);
}
Was soll dieser Block in der Schleife?
richTextBox1.Select(textBox2.TextLength, 1);
schadet nicht, ist aber ohne Wirkung, wenn richTextBox1.HideSelection == true
richTextBox1.Refresh();
ist normalerweise überflüssig (bevor ich auf HideSelection gekommen bin, hatte ich hierauf getippt, weil man manchmal ein Refresh braucht, obwohl sich alle Controls neu zeichnen sollten, wenn ein Ereignishandler abgeschlossen ist)
Jetziger Code:
private void textBox2_TextChanged(object sender, EventArgs e)
{
charCount = textBox2.Text.Length;
label2.Text = "Anschläge: " + charCount.ToString();
// berechne, bis wohin eingefärbt werden soll - solange wir nicht sicher sind, dass textBox2 höchstens so viele Zeichen enthält wie richTextBox1, sollten wir das so machen, sonst kriegen wir womöglich eine IndexOutOfRangeException.
var lgMin = Math.Min(textBox2.TextLength, richTextBox1.TextLength);
// gehe alle Zeichen bis zur gemeinsamen Länge durch und markiere entsprechend der (Nicht-)Übereinstimmung
for (int i = 0; i < lgMin; i++)
{
richTextBox1.Select(i, 1);
if (textBox2.Text[i] == richTextBox1.Text[i])
{
richTextBox1.SelectionStart = textBox2.Text.Length;
if (textBox2.TextLength < richTextBox1.TextLength)
{
richTextBox1.Select(textBox2.TextLength, 1);
}
else
{
richTextBox1.Select(richTextBox1.TextLength, 0);
}
}
else
{
richTextBox1.SelectionColor = Color.Red;
}
}
// Rest des Originaltextes schwarz einfärben
if (richTextBox1.TextLength - lgMin >= 0)
{
richTextBox1.Select(lgMin, richTextBox1.TextLength - lgMin);
richTextBox1.SelectionColor = Color.Black;
}
richTextBox1.Refresh();
}
Nun wird alles ausgewählt
Nanu?
Und wieso ist
richTextBox1.SelectionStart = textBox2.Text.Length;
if (textBox2.TextLength < richTextBox1.TextLength)
{
richTextBox1.Select(textBox2.TextLength, 1);
}
else
{
richTextBox1.Select(richTextBox1.TextLength, 0);
}
immer noch in der Schleife?
Der Block aus meinem vorigen Kommentar gehört nach die Schleife.
Falls richTextBox1.HideSelection==true, sollte auf diesen Block noch das Einfärben mit den Auswahlfarben ("Highlight...") folgen.
Das war ja nur eins von mehreren Dingen, die nicht richtig funktionieren
oh ok. was ist noch falsch? Oder kann man es so machen, dass wenn man 10 Minuten geschrieben hat, die Fehler in einem neuen Fenster kommen, und man es von dort aus druckt?
Auch das geht natürlich.
Aber um weiter zu analysieren, bräuchte ich den Quelltext.
(Ich hätte auch ein Beispielprojekt, aber unvollständig und mit vielen nicht unbedingt anfängertypischen Bestandteilen)
Die fehler können nicht gezählt werden. Mal kommt 1 raus, mal 0
Ich kann die Fehler nicht zählen. Es kommt immer 1/0 Raus:
private void beendenToolStripMenuItem1_Click(object sender, EventArgs e)
{
this.Size = new System.Drawing.Size(898, 627);
label1.Visible = true;
label3.Visible = true;
label4.Visible = true;
textBox1.Visible = true;
textBox3.Visible = true;
textBox4.Visible = true;
button1.Visible = true;
richTextBox1.HideSelection = true;
textBox2.HideSelection = true;
textBox2.ReadOnly = true;
// berechne, bis wohin eingefärbt werden soll - solange wir nicht sicher sind, dass textBox2 höchstens so viele Zeichen enthält wie richTextBox1, sollten wir das so machen, sonst kriegen wir womöglich eine IndexOutOfRangeException.
var lgMin = Math.Min(textBox2.TextLength, richTextBox1.TextLength);
// gehe alle Zeichen bis zur gemeinsamen Länge durch und markiere entsprechend der (Nicht-)Übereinstimmung
for (int i = 0; i < lgMin; i++)
{
richTextBox1.Select(i, 1);
if (textBox2.Text[i] == richTextBox1.Text[i])
{
richTextBox1.SelectionColor = Color.Black;
}
else
{
richTextBox1.SelectionColor = Color.Red;
}
}
// Rest des Originaltextes schwarz einfärben
if (richTextBox1.TextLength - lgMin >= 0)
{
richTextBox1.Select(lgMin, richTextBox1.TextLength - lgMin);
richTextBox1.SelectionColor = Color.Black;
}
}
Falscher quelltext sorry, und hat funktioniert. habe aus versehen var fehleranzahl = 0 in for {} reingetan. Hat nun geklappt. Wie kann man den einen 10Min Timer erstellen, welche nach 10 Minuten das schreiben verbietet?
Nein -- in einer TextBox hat der gesamte Text dasselbe Format.
Kann man zumindest die Fehler zählen. Sprich welcher Buchstabe falsch eingegeben wurde?
Sprich wie hier auf dem Bild: https://ibb.co/i42yoT
Mit einer RichTextBox kannst du die einzelnen Buchstaben so einfärben.
Es wird dadurch einfach nur der ganze Text markiert. Auch wenn nichts falsch ist. Was ich wollte ist, dass sich ein Wort verfärbt wenn es falsch geschrieben wird