Editcontrol selbst erstellen



  • Hallo alle miteinander 🙂

    Erstmal der Fairness wegen: Ich habe den gleichen Beitrag bereits im Forum von www.winapi.net gepostet, dort allerdings noch keine Antwort bekommen und man kann sagen, dass mir das Thema irgendwie unter den Nägeln brennt. 😉

    Ich habe folgendes Problem: Ich bastle an einem kleinen GUI-Toolkit nur für mich zu Lernzwecken und habe bisher Labels und Buttons. Ich weiß, das ist noch nicht viel. 😉
    Allerdings möchte ich alles selbst zeichnen und nicht die vorhandenen Windowsklassen benutzen. Bisher lief auch alles ohne größere Probleme. Jetzt habe ich allerdings ein großes Problem beim Edit-Control. So, wie ich das sehe muss ich mich beim Edit-Control selbst darum kümmern wo gerade das Caret ist, und welcher Textbereich markiert ist, etc.
    Ich habe mir gedacht, dass ich die Caret-Postion in einem int oder so speichern könnte, gleiches für Anfang/Ende der Textmarkierung. Das Problem, welches dabei allerdings existiert ist, wie ich herausbekomme, wo ich beispielsweise das Caret zeichnen muss (in Pixel) bzw. wenn ich ins Edit-Fenster klicke, wie ich das Zeichen bekomme, wo die Textmarkierung nun beginnt.
    Also, wenn ich im Fenster an die Stelle x:100, y:10 klicke möchte ich wissen, welches Zeichen sich von meinem String an dieser Stelle befindet. Was mir spontan einfällt, was ich aber noch nicht probiert habe ist, dass ich jedes Zeichen von Anfang bis (maximal) zum Ende durchlaufe und prüfe wie breit das Zeichen ist, wenn jedes Zeichen 10 Pixel breit ist, dann weiß ich danach ja, dass sich an Pixel 100 das 10. Zeichen befindet, aber besonders bei längerem Inhalt stelle ich mir das extrem unperformant vor. 😞

    Ich hoffe, ich konnte mich einigermaßen klar ausdrücken, wo mein Problem liegt, ansonsten fragt nach. 🙂

    Vielen Dank schonmal.



  • Das musst du so machen, das ist ja nicht unperformant. Anders geht sowas nicht. Edit Control zu programmieren stelle ich mir auch etwas nervig vor um ehrlich zu sein, besonders wenn es multiline sein soll. Die Funktion um die Breite von Textstrings mit einer bestimmten Font rauszukriegen heisst GetTextExtend32 oder GetFontExtends32 oder irgendwie so. musst du mal auf MSDN schauen. wenn du es nicht selber machen willst sondern nur ein gutes edit control suchst kann ich dir Scintilla empfehlen, damit kann man eigentlich alles machen und es ist sehr einfach ins programm einzubinden, weil es auch edit/richedit funktionalität unterstützt.



  • Vielen Dank für die Hinweise. Scintilla kommt für mich nicht in Frage, da es mir gerade darum geht, außer der GDI nichts weiteres zu verwenden. Wie gesagt, ist in erster Linie zu Lernzwecken und nicht, damit ich es unbedingt mal fertig bekomme, obwohl es schon schön wäre. 🙂

    Ich denke Du meinst die Funktion "GetTextExtentPoint32". Ich habe dafür aber eine Funktion entdeckt, die wohl besser für mich passt: GetCharacterPlacement.
    Da wird mir in einem Array für jeden Character die zugehörige Caret-Position geliefert. Mal sehen, wie ich damit zurechtkomme. Bis ich merke, ob es performant genug für sehr lange Texte ist dauert es sowieso noch ein Weilchen. :p





  • Hey, super, vielen Dank, das scheint genau das zu sein, was ich gesucht habe. 😃



  • OK, so weit bin ich bis jetzt noch nicht gekommen, hatte aber auch wenig Zeit. 😞

    Ich habe mir gedacht, ich füge die Scroll- und Multiline-Funktionalität erstmal in mein Basis-Widget ein. Multiline war soweit kein Problem, allerdings habe ich bei den Scrollbars ein kleines Problem. Ich verwende die Builtin-Scrollbars und beim resizen flackern die immer. 😞
    Und zwar tauchen die immer genau neben der vertikalen bzw. über der horizontalen Scrollbar einmal kurz auf und werden dann überzeichnet.
    Ich arbeite mit einem Memory-DC, wo alles gezeichnet wird und blitte bei WM_PAINT einfach alles in der UpdateRegion in den Client-DC. Aber irgendwie scheint es mir, als ob ich etwas in der falschen Reihenfolge mache oder so.

    Hier mal die relevanten Auszüge (ich hoffe ihr könnt mir helfen ;)):

    Widget::create(): (wird in jedem Konstruktor entsprechend aufgerufen)

    void Widget::create(int _x, int _y, int _width, int _height) {
    	this->Handle = 0;              // initialize handle with "0"
    	this->DC     = 0;
    	this->Width  = _width;
    	this->Height = _height;
    
    	this->Text = "Widget";
    
    	this->dpNormal  = new DrawProperties();
    	this->dpNormal->Background.Color = common::Color(0xFFFFFF);
    	this->dpClicked = new DrawProperties();
    
    	// register class to windows
    	WNDCLASS wc;
    	wc.style         = 0; //CS_HREDRAW | CS_VREDRAW;  // redraw horz. & vert.
    	wc.lpfnWndProc   = Widget::DefWindowProc;  // set the def. windowproc
    	wc.cbClsExtra    = 0;
    	wc.cbWndExtra    = 0;
    	wc.hInstance     = 0;
    	wc.hIcon         = ::LoadIcon(0, IDI_APPLICATION);
    	wc.hCursor       = ::LoadCursor(0, IDC_ARROW);    // def-cursor: arrow
    	wc.hbrBackground = reinterpret_cast<HBRUSH>(::CreateSolidBrush(0xFFFFFF));
    	wc.lpszMenuName  = 0;
    	wc.lpszClassName = typeid(*this).name();       // classname to register
    
    	::RegisterClass(&wc);  // register the prev. defined windowclass
    
       // create the window based on the registered class with given parameters
       this->Handle = ::CreateWindowEx(0,
                                       typeid(*this).name(),
                                       "",
                                       WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                                       _x, _y, this->Width, this->Height,
                                       0, // Parent
                                       0, 0, this);
    
    	this->DC = new DeviceContext();
    	this->DC->resize(this->Width, this->Height);
    	this->paint();
    }
    

    Auszug aus der WindowProc:

    case WM_SIZE: {
    	// resize DC
    	this->Width = _lParam & 0xFFFF;
    	this->Height = _lParam >> 16;
    	if (this->DC) {
    		this->DC->resize( this->Width, this->Height);
    		this->paint();
    	}
    }
    break;
    case WM_PAINT: {
    	if (this->DC) {
    		PAINTSTRUCT ps;
    		::BeginPaint(this->Handle, &ps);
    		// copy DC-content to window's dc
    		BitBlt(ps.hdc, 
    			   ps.rcPaint.left, ps.rcPaint.top, 
    			   ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, 
    			   this->DC->getDC(), 
    			   ps.rcPaint.left, ps.rcPaint.top, 
    			   SRCCOPY);
    		::EndPaint(this->Handle, &ps);
    		return 0;
    	}
    }
    break;
    

    Konstruktor der Klasse DeviceContext:

    DeviceContext::DeviceContext() 
    : Width(1)
    , Height(1)
    , FontFamily("Tahoma")
    , FontSize(12)
    , FontWeight(400)
    {
    	HDC dc = ::GetDC(0);
    	this->DC = ::CreateCompatibleDC(dc);
    	this->Bitmap = ::CreateCompatibleBitmap(dc, this->Width, this->Height);
    	::SelectObject(this->DC, this->Bitmap);
    
    	// Hintergrund für alle Aktionen auf 'transparent' stellen
    	::SetBkMode(this->DC, TRANSPARENT);
    
    	// std-GUI-Font für DC auswählen
    	::SelectObject(this->DC, this->createFont("Tahoma", 10, 400));
    
    }
    

    DeviceContext::resize():

    void DeviceContext::resize(int _Width, int _Height) {
    	// Größe auf mind. 1 korrigieren, damit Bitmap nicht monochrom wird
    	this->Width  = _Width  < 1 ? 1 : _Width;
    	this->Height = _Height < 1 ? 1 : _Height;
    
    	// Bitmap in neuer Größe erstellen
    	this->Bitmap = ::CreateCompatibleBitmap(this->DC, this->Width, this->Height);
    
    	// neues Bitmap einsetzen und Handle auf altes Bitmap holen
    	HBITMAP hBitmapOld = reinterpret_cast<HBITMAP>(::SelectObject(this->DC, this->Bitmap));
    
    	// altes Bitmap freigeben
    	::DeleteObject(hBitmapOld);
    }
    

    Falls ihr noch Infos braucht, einfach Bescheid geben. Danke. 🙂



  • Hat sich erledigt. Wie war das nochmal mit dem Wald und den Bäumen?!?

    Vielen Dank an diejenigen, die sich evtl. die Mühe gemacht haben sich das ganze mal anzusehen.

    Das Problem bestand darin, dass ich eine Methode move(x, y) und eine Methode resize(w, h) implementiert hatte, die beide MoveWindow mit "true" als letzten Parameter aufgerufen haben. Daher der Flackereffekt. Nachdem ich eine Methode moveResized(x, y, w, h) implementiert hatte lief alles reibungslos. Ebenso war es eine Möglichkeit innerhalb der Methode move(x, y) den letzten Parameter auf false zu setzen, was ja auch klar ist, da habe ich wohl gepennt. 😞

    Also, vielen Dank nochmal (für den Fall der Fälle 😉 ).


Anmelden zum Antworten