moinsen leutes,
ich muss in einer richtextbox in einer zeile text einfügen,
und ab einigen bestimmten stellen genau in der textzeile darüber
mit einer anderen schriftart einige weitere zeichen hinzufügen.
das ganze muss schon pixelgenau sein, zumindest in x richtung.
fragt sich nur, wie ich das machen soll.
könnte mir da mal einer helfen?
ich danke
naja
moinsen
Hi juhuichbins,
du kannst mit Graphics.MeasureString die Ausmaße von Text mit Angabe der Schriftart bestimmen. Mit Graphics.FromHwnd bekommst du das Graphics-Objekt der RichTextBox. Am besten einen using-Block verwenden, damit das Graphics-Objekt in jedem Fall wieder freigegeben wird.
using(Graphics g = Graphics.FromHwnd(rtb.Handle) {
int textWidth = g.MeasureString(/* .. */);
}
Gruss
Pulpapex
danke, funzt schon ganz gut, nur noch 1 problem: wo bekomm ich den paint event von der richtextbox her? jedesmal, wenn ich den inhalt verändere,
verschwindet mein gezeichnetes...
Ich hab gedacht, du wolltest den Text in der RichTextBox formatieren, so dass es passt. Also mit MeasureString nur die Textbreite im Pixel messen, aber nicht im Control zeichnen.
Wenn du selbst zeichnen möchtest, musst du RichTextBox.OnPaint überschreiben. Die Ansicht muss bei einem Control ständig aktualisiert werden. Wenn nicht, verschwindet das Gezeichnete wie du selbst bemerkt hast.
Gruss
Pulpapex
joa, aber irgendwie hat meine richtextbox kein paint event... habs bisher nirgens finden können. zwar steht in dem englischen msdn docu etwas davon, aber ich habs bei mir net gefunden ...
Hallo juhuichbins,
der Paint Event und die OnPaint-Methode sind zwei Seiten der gleichen Medallie, aber nicht dasselbe. Eine RTB hat beides, da beides von Control geerbt wird. Außerdem ist OnPaint protected und nicht public. Vielleicht hast du es aus einem dieser Gründe nicht gefunden, aber vorhanden ist die Methode.
herbivore
beim kompilieren bekomme ich keine fehlermeldung...
aber die methode richTextBox1_Paint wird auch net aufgerufen...
was läuft da also falsch?
this.richTextBox1.Paint += new System.Windows.Forms.PaintEventHandler(this.richTextBox1_Paint);
Hallo juhuichbins,
wenn du mit nicht nie meinst, dann sehe ich nur zwei Möglichkeiten: Die von dir angegebene Zeile wird aus irgendeinem Grund nicht ausgeführt oder du hast zusätzlich OnPaint überschrieben und vergessen base.OnPaint aufzurufen.
herbivore
hm, komisch
innerhalb der paint methode habe ich ne messagebox reingetan,
welche auch net aufgerufen wird.
hab mal am ende als auch am anfang vom coder der methode mal base.OnPaint(e) reingetan, funzt auch net.
habe mal den event "textchanged" auch mit paar zeichnungen bestückt.
wenn ich da ganz schnell hintereinander was eintippe,
dann sehe ich kurz, wie da was gezeichnet wird.
geht aber sofort wieder weg...
ich weiß absolut nicht mehr weiter
hallo? kann mir jemand jetzt helfen?
Hallo juhuichbins,
wenn du eine eigene Control-Klasse schreibst, die direkt oder indirekt von Control erbt, dann brauchst du keinen EventHandler für den Paint-Event eintragen und solltest das auch nicht tun. Stattdessen überschreibst du die OnPaint-Methode (override). In dieser Methode solltest du base.OnPaint aufrufen, dies jedoch nur am Anfang.
Wenn du das Control erzeugst und einem Form hinzufügst, kann ich mir kaum vorstellen, dass OnPaint nicht aufgerufen wird. Wenn das Control dann nicht gezeichnet wird, würde ich eher darauf Tippen, dass mit dem Zeichnen selbst was nicht stimmt.
Wenn du Dein Programm geändert hast und es immer noch nicht geht, poste mal den Code deiner OnPaint-Methode.
herbivore
ok, langsam funzt es
hab SetStyle(ControlStyles.UserPaint, true); mal eingefügt.
jetzt wird innerhalb der richtextbox gemalt.
allerdings sehe ich keinen text 🙁
wie kann ich nun dieses problem lösen?
Ich hab da mal eine Frage:
Willst Du ernsthaft alles selber Zeichnen ?
RTF unterstützt ja x verschiedene Fonts und Hinter / Vordergrundfarben....
Wenn Du nun den Draw selber machst musst Du alles selber Zeichnen... also den ganzen RTF auslesen und richtig zeichnen...
Falls Du dies schaffen solltest, dann hast Du immer noch das Problem, dass die Zeichen die Du hinzufügst nicht im Zielstring enthalten sind.... somit hast Du zwar die Visualisierung... jedoch nicht die Daten.
Ich habe mich auch mal mit RTF beschäftigt und kann Dir nur sagen dass es nicht ganz einfach ist.
Wäre es nicht einfacher die zusätzlichen Zeichen in den RTF einzufügen ? (natürlich mit allen nötigen Formatierungen...)
Vor langer Zeit habe ich für mich selber mal einen IRC-Client zusammengeschraubt und die Textausgabe mit RTF gelöst.
Hierführ habe ich mehrere Windows mit meiner eigenen RTF-Box bestückt und die Converter in einer Art Singleton Klasse gehalten.
Die RTFBox unterstützt hier zwar nur das Anfügen von Text mit verschiedener Formatierung aber mehr habe ich halt nicht gebraucht.
Es ist aber sicher einfacher mit diesem Beispiel zu beginnen als das Rad neu zu erfinden.
Nachfolgend die benötigten Klassen.... Spiel mal damit rum und passe es entsprechend an Deine Bedürfnisse an.
using System;
using System.Windows.Forms;
namespace Irc
{
/// <summary>
/// Zusammendfassende Beschreibung für IrcRichtTextBox.
/// </summary>
public class IrcRichtTextBox : RichTextBox
{
public IrcRichtTextBox()
{
//
// TODO: Fügen Sie hier die Konstruktorlogik hinzu
//
}
public void AppendMircText(string mirctext)
{
this.Select(this.Text.Length,0);
this.SelectedRtf=mirctext;
}
}
public delegate void AppendMircTextHandler(string mirctext);
}
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Text;
using System.Collections;
namespace Irc
{
public class MircColors
{
private static MircColors m_pThis;
private SortedList m_slColors;
private string m_strColorString;
public static MircColors GetInstance()
{
if (m_pThis == null)
{
m_pThis=new MircColors();
m_pThis.m_slColors=new SortedList();
m_pThis.AddColor(0,new MircColor( Color.White));
m_pThis.AddColor(1,new MircColor( Color.Black));
m_pThis.AddColor(2,new MircColor( Color.DarkBlue));
m_pThis.AddColor(3,new MircColor( Color.Green));
m_pThis.AddColor(4,new MircColor( Color.Red));
m_pThis.AddColor(5,new MircColor( Color.Brown));
m_pThis.AddColor(6,new MircColor( Color.DarkMagenta));
m_pThis.AddColor(7,new MircColor( Color.Orange));
m_pThis.AddColor(8,new MircColor( Color.Yellow));
m_pThis.AddColor(9,new MircColor( Color.LightGreen));
m_pThis.AddColor(10,new MircColor(Color.DarkGreen));
m_pThis.AddColor(11,new MircColor(Color.LightBlue));
m_pThis.AddColor(12,new MircColor(Color.Blue));
m_pThis.AddColor(13,new MircColor(Color.Magenta));
m_pThis.AddColor(14,new MircColor(Color.Gray));
m_pThis.AddColor(15,new MircColor(Color.LightGray));
}
return m_pThis;
}
private void AddColor(int key, MircColor color)
{
if (!this.m_slColors.ContainsKey(key))
{
this.m_slColors.Add(key,color);
}
}
public string RtfColorTableString
{
get
{
if (this.m_strColorString==null)
{
StringBuilder sb=new StringBuilder();
sb.Append("{\\colortbl ;");
foreach (MircColor mrc in this.m_slColors.Values)
{
sb.Append(mrc.ToRtfColorTableString());
sb.Append(";");
}
sb.Append("}");
this.m_strColorString=sb.ToString();
}
return this.m_strColorString;
}
}
public void PrintColored(IrcRichtTextBox destination, string text, string foreColor, string backColor, int size, string fontName)
{
lock (this)
{
StringBuilder sb=new StringBuilder();
//Font
sb.Append("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil\\fcharset0 fontName;}}\r\n".Replace("fontName", fontName));
//ColorTable
sb.Append(this.RtfColorTableString);
sb.Append("\r\n");
//Color
if (foreColor!=null && foreColor!="")
{
sb.AppendFormat("\\cf{0}\\f0\\fs{1}",int.Parse(foreColor)+1,size);
}
else
{
sb.AppendFormat("\\cf{0}\\f0\\fs{1}",0,size);
}
sb.Append("\r\n");
//TurnOn HighLighting
if (backColor!=null && backColor!="")
{
sb.AppendFormat("\\highlight{0}",int.Parse(backColor)+1);
}
//Begin LineFeed
if (text.EndsWith("\n")|| text.EndsWith("\r"))
{
sb.Append("\\pard");
}
string[] textParts;
textParts=text.Split(new char[]{''});
if (textParts.Length>1)
{
text=textParts[0];
for (int i=0;i < textParts.Length-2;i+=2)
{
//text=text+"\\b "+textParts[i+1];
text=string.Format("{0}\\b {1}",text,textParts[i+1]);
if (textParts.Length>i+1)
{
//text=text+"\\b0 "+textParts[i+2];
text=string.Format("{0}\\b0 {1}",text,textParts[i+2]);
}
}
//text=text+"\\b0";
text=string.Format("{0}\\b0",text);
}
System.Diagnostics.Debug.WriteLine(text);
//Text
sb.Append(" ");
sb.Append(text);
//EndLine
if (text.EndsWith("\n")|| text.EndsWith("\r"))
{
sb.Append("\\par");
}
//TurnOff HighLighting and Color
if (backColor!=null && backColor!="")
{
sb.AppendFormat("\\cf0\\highlight0");
}
//Close Brackets
sb.Append("}");
System.Diagnostics.Debug.WriteLine(sb.ToString());
lock(destination)
{
AppendMircTextHandler del=new AppendMircTextHandler(destination.AppendMircText);
if (destination.InvokeRequired)
{
destination.Invoke(del,new object[]{sb.ToString()});
}
else
{
destination.AppendMircText(sb.ToString());
}
}
}
}
/// <summary>
/// Zusammendfassende Beschreibung für MircColors.
/// </summary>
private class MircColor
{
private Color color;
public MircColor(Color color)
{
this.color=color;
}
public string ToRtfColorTableString()
{
System.Text.StringBuilder sb=new System.Text.StringBuilder();
sb.AppendFormat("\\red{0}\\green{1}\\blue{2}",this.color.R,this.color.G,this.color.B);
return sb.ToString();
}
}
}
}
Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...
nein, du verstehst mich nicht...
ich versuch das mal zu visualisieren:
Abm Ebm
What will you do when you get lonely
Abm G A B
With nobody waiting by your side
C#m F# B E
You've been running and hiding much too long,
C#m F# B
You know it's just your foolish pride.
du siehst hier den text, und in jeder zeile darüber die akkorde dazu.
jeder akkord hat seine passende stelle.
ausgelesen und abgespeichert sieht das in der datei aber so aus:
[Abm]What will you do when you get [Ebm]lonely
[Abm]With nobody [G]waiting [A]by your [B]side
[C#m]You've been [F#]running and [B]hiding much too [E]long,
[C#m]You know it's [F#]just your foolish [B]pride.
so, diese datei muss ich in die richtextbox kriegen.
aber halt so, dass die sachen in den eckigen klammern
in einer anderen schriftart genau in der zeile darüber ab der position vom ersten buchstaben nach der eckigen klammer.
wenn ich in der richtextbox die schriftart courier new für alles nehme,
dann geht das, einigermaßen.
allerdings möchte ich gerne die akkorde in einer anderen schriftart nehmen.
das beudetet dann wiederrum, dass die buchstaben dann eine andere größe haben,
und so nicht mehr richtig positioniert werden können,
nämlich anzahl der zeichen abzählen und dann einfügen und so.
außerdem möchte ich das so machen,
dass ich mit der maus über die akkorde gehen kann,
und wenn ich einen akkord dann anklicke,
dass ich diese positionieren kann,
was dann auch beim abspeichern in der datei entsprechend aussehen soll.
WIE kann ich das nun hinbekommen?
Hallo juhuichbins!
Ich gebe dem Programmierhans mal Recht. Das was Du versuchst, ist sehr umständlich.
Kann man nicht einfach Zeile für Zeile durch das Dokument gehen, zunächst Deine Akkorde Stück für Stück aus der Zeile schneiden und gleichzeitig den bisher verwendeten Zeilenabstand vom linken Rand berechnen?
Anschließend dann die Akkorde in die Zeile schreiben um zuletzt die Originalzeile hinzuzufügen?
Mal an einem Beispiel erklärt:
Zeile1: [Abm]What will you do when you get [Ebm]lonely
Zeile2: [Abm]With nobody [G]waiting [A]by your [B]side
Zeile3: [C#m]You've been [F#]running and [B]hiding much too [E]long,
Zeile4: [C#m]You know it's [F#]just your foolish [B]pride.
Das sollte wesentlich einfacher sein, als irgendwelche Pixelberechnungen und Paint etc...
Ciao
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
Hallo zusammen,
grundsätzlich halte ich es auch für besser, ein neues Dokument zu erstellen, das von der RTB komplett selbst angezeigt wird.
Dabei müsste sogar eine pixelgenaue Positionierung möglich sein, denn in RTF kann man ja auch Tab-Positionen definieren (auch für je zwei Zeile immer wieder neue). Also quasi der Algorithmus von norman_timo, nur pixelgenau durch Verwendung von Tabs statt Leerzeichen.
Das Anklicken eines Akkords sollte auch nicht das Problem sein. Denn dabei wird dann einfach der Cursor auf die Stelle gesetzt, was man durch den entsprechenden Event (SelectionChanged?) mitbekommen sollte und entsprechend reagieren kann.
Das einzige Problem was ich sehe ist, dass der Benutzer die Akkordzeilen munter editieren kann (insbesondere die Tabs löschen). Weiß nicht, ob man in RFT einzelne Zeilen sperren kann. Es sollte aber eigentlich funktionieren, solche Änderungen im TextChanged-Event abzufangen.
Ich denke das Erstellen eines RFT-Textes auf Quellcodeebene ist nicht ganz trivial (schätze ich auf jeden Fall schwieriger ein, als HTML zu generieren), aber sicher um einiges leichter als mit der RTB zu kämpfen, wer, was, wie und wann zeichnet.
herbivore
Also was ich noch für eine Lösung sehe:
Mach Dir eine Assoziazionsklasse welche:
Den Text zu einem Akkord sowie einen Akkord enthält
Eine Collection welche die Assoziazionsklassen enthält
Dann eine eigene Textbox welche die Collection enthält (eine Textbox ist einfacher als RTF).
Im OnPaint kannst Du dann alles painten
Da die Texte immer länger als die Akkorde sind kannst Du dann mit MeeasureString die Länge des Textes berechnen. Der Akkord (welcher zum Text gehört) kann dann an derselben X-Pos gepinselt werden wie der Text (daher übereinander).... der nächste Text folgt dann mit einem definierten Abstand nach dem letzten Text
Bei einem Click auf den Text die Position berechnen und aufgrund der Position die entsprechende Asso-Klasse aus der Collection holen... Akkord editieren... und Invalidate der Textbox aufrufen (löst einen Repaint aus)
Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...
die idee von herbivore gefällt mir bisher am besten...
werd mich mal in rtf reinarbeiten müssen.
kennt da jemand eine gute (deutsche? 🙂) online referenz?
@norman_timo
das ganze muss pixelgenau sein, geht auch nicht anders.
wenn die zeile mit den akkorden hat eine andere schriftart
und somit sind die abstände zwischen den buchstaben und auch den leerzeilen anders...
und wenn ich noch die option hinzufüge,
dass der user die einzelnen schriftarten selber aussuchen kann,
dann kann ich das ausmessen anhand leerzeichen vergessen.
Nur mal so ne Frage am Rande.... warum willst du das zwingend in einer RichTextBox darstellen?
Wenn du sowieso schon den Text der Accorde malst, dann kannst du den Rest-Text auch gleich malen. Und dann brauchst du auch keine RichTextBox mehr.
Dann kannst du genausogut auf nem Panel oder direkt auf der Form malen. Ist eigentlich genau das selbe, nur das die (in diesem Falle wohl mehr störende als helfende) RTF-Box nicht mehr dabei ist.
---edit---
Sehe grade, das du auch editieren willst.... das geht eigentlich auch genauso, nur das du (wenn das Panel den Focus hat) die Tastatur abfragst, und einfach an der Stelle einfügst, an der jetzt editiert wird.
Wo das ist kannst du leicht herausbekommen, da du ja selbst weisst du do hingemalt hast. 😉 Und nen blinkenden Cursor zu malen, sollte da auch nicht mehr das Problem sein.
Bevor du Dir die Finger brichst um ein RTF dazu zu bringen was zu machen was es vielleicht kann, aber nicht dafür gemacht ist, kannst du es besser komplett selbst machen.....
--
Man kann Scheisse nicht polieren!
ok, bis jetzt funzt es bestens.
habe mir in den msdn docu mal die rtf 1.6 docu reingezogen.
das schwierigste war bisher rauszubekommen,
wieviele twips (tabulatorabstand wird in rtf in twips angegeben) ein point hat.
man nehme einfach 1440 (anzahl der twips pro inch) und teile durch Graphics.DpiX.
so komme ich auf 15. dann nur noch mittels der richtextbox methode GetPositionFromCharIndex den point von dem startbuchstaben beim akkord holen, mal 15 rechnen, und schon habe ich den wert für einen tabulator abstand.
so bastel ich mir dann hard hand coded eine eigene rtf datei... 🙂
auf jeden fall danke an jeden für hilfe bis hier her, doch weiter gehts 😉
nächste hürde wäre es so hinzubekommen,
dass solange der benutzer einen akkord angeklickt hält,
dieser umrahmt wird und gleichzeitig ein strich zur nächsten zeile darunter
zum startindex für diesen akkord gezogen wird.
und wenn der benutzer seine maus bewegt,
dann wird auch der akkord mitbewegt.
und sobald er loslässt, wird an der entsprechenden stelle der "song-quellcode" eben editiert und aus diesem heraus nochmals die rtf datei erstellt.
fragt sich nur wie... jemand eine idee für einen ansatz?
Hallo juhuichbins,
ich weiß nicht, ob das alle Probleme löst, aber das klingt nach Drag&Drop. Alle Controls bieten dafür eine Unterstützung.
herbivore
joa, soetwas habe ich mir auch schon gedacht...
fragt sich nur, wie das mit dem rahmen drum herum und so gehen soll.
da müsste ich dann doch zeichnen... oder?
zeichnen müsstest du schon aber ein einfacher ramen ist ja nur ein rechteck und ich glaube dein problem kanst du glaube ich mit mous down ,mous up und mous move lösen bei einem mous down zeichnest du ein rechteck um den Akkrod wenn dann die Maus bewegt wird wird das rechteck immer unter der Maus entlang gezogen wenn dann die Maus wieder losgelassen wir fügst du den Akord an der entsprechenen stelle einfügen
Wir Arbeiten eigendlich nicht wir nehmen nur das geld
Hallo zusammen,
ich glaube MouseDown/Move/Up sind nicht das passende für Drag&Drop. Da muss es sowas wie BeginDrag und so geben. Habe die Doku gerade nicht parat.
herbivore