Zum Inhalt springen

PL1994

Mitglieder
  • Gesamte Inhalte

    77
  • Benutzer seit

  • Letzter Besuch

Beiträge von PL1994

  1. Ich habe das nur auf das Wesentliche reduziert. Natürlich muss in der Klasse "Change" noch gespeichert werden, welches Objekt geändert wurde. Da lag nur nicht mein Problem.

    Das würde ich dann mit Hilfe einer Enumeration machen, deren Member beschreiben, welche Aktion vorgenommen wurde. Eine Variable muss noch den Primärschlüssel speichern. Dann reichen einzelne Integer und Strings völlig und ich brauche nicht Objekte, die den ganzen Datensatz widerspiegeln, in "Change" speichern, obwohl nur ein einzelnes Feld eines Datensatzes geändert werden soll ...

    Löschen und Einfügen brauche ich nicht, sondern nur die Möglichkeit, eine Aktion rückgängig zu machen. Dafür reichen die Daten aus.

    Ich wollte nur vermeiden, dass in der Klasse "Value" zwangsläufig immer eine Variable des Wertes "NULL" steht.

  2. Na gut, dann eben anders. Ich werde es wohl jetzt wie folgt machen :

    #include <ctime>
    #include <iostream>
    #include <map>
    #include <string>
    
    using namespace std;
    
    class Value
    {
    private:
    	enum Type
    	{
    		INTEGER, STRING
    	} type;
    
    	const int intValue;
    	const string stringValue;
    
    public:
    	Value(int value) : type(INTEGER), intValue(value) {}
    	Value(string value) : type(STRING), intValue(NULL), stringValue(value) {}
    
    	int getIntValue() { return intValue; }
    	string getStringValue() { return stringValue; }
    };
    
    class Change
    {
    private:
    	Value newValue, oldValue;
    
    public:
    	Change(Value newValue, Value oldValue) : newValue(newValue), oldValue(oldValue) {}
    
    	Value getNewValue() { return newValue; }
    	Value getOldValue() { return oldValue; }
    };
    
    map<time_t, Change> changelog;
    
    int main()
    {
    	changelog.insert(pair<time_t, Change>(time(NULL), Change(Value(1), Value(2))));
    	changelog.insert(pair<time_t, Change>(time(NULL)+1, Change(Value("string1"), Value("string2"))));
    
    	for (map<time_t, Change>::iterator i = changelog.begin(); i != changelog.end(); i++)
    		cout << i->first << ": " << i->second.getOldValue().getIntValue() << " -> " << i->second.getNewValue().getIntValue() << " | " << i->second.getOldValue().getStringValue() << " -> " << i->second.getNewValue().getStringValue() << endl;
    
    	system("pause");
    }

    Das geht prinzipiell zwar, ist aber halt ungünstig, weil bei "Value" immer eine völlig sinnlose Variable gespeichert ist und man später erst mal herausfinden muss, was überhaupt gespeichert werden sollte.

    Das scheint mir nicht gerade optimal zu sein. Für Verbesserungsvorschläge bin ich also absolut offen :)

    EDIT: Nicht fragen, warum da die Variable "type" drin ist. Wollte was anderes versuchen, was aber Quatsch war. Nur vergessen, das rauszulöschen ...

  3. Bevor ich mich mit Smartpointern auseinandersetze, erkläre ich dann wohl erst einmal, was ich mit den Daten vorhabe:

    Es geht darum, dass der Benutzer zum Beispiel Änderungen an einer TreeView vornehmen kann oder bestimmte Datensätze bearbeitet etc. Diese Daten werden in einer Datenbank gespeichert. Damit jetzt nicht jedes mal, bei jeder noch so kleinen Änderung gleich die Datenbank bearbeitet werden muss, wollte ich ebendiese Änderungen in einem Changelog speichern, um dann, wenn gewünscht, alle auf einmal in die Datenbank zu übertragen.

    Außerdem sollte man eine Aktion auch noch rückgängig machen können. Diese könnte ich aber bei einer bloßen Änderung an der Datenbank nicht mehr nachvollziehen. Das wären die zwei Hauptfunktionen. Das muss aber eben mit verschiedenen Datentypen gehen ...

  4. Habe noch etwas anderes ausprobiert. Das geht im Prinzip. Ich kann mir nur fast nicht vorstellen, dass das keine Nachteile mit sich bringen soll:

    #include <ctime>
    #include <iostream>
    #include <map>
    
    using namespace std;
    
    typedef long Value;
    
    class Change
    {
    public:
    	Value v1, v2;
    
    	Change(Value v1, Value v2) : v1(v1), v2(v2) {}
    };
    
    map<time_t, Change> changelog;
    
    int main()
    {
    	changelog.insert(pair < time_t, Change>(0, Change((Value)"Val1", (Value)"Val2")));
    
    	cout << (char*)changelog.at(0).v1 << "\n" << (char*)changelog.at(0).v2 << endl;
    	system("pause");
    }

    Was haltet ihr davon?

  5. Danke dir für die Antwort. Wenn ich das jetzt richtig verstanden habe, meinst du das so:

    #include <ctime>
    #include <iostream>
    #include <map>
    
    using namespace std;
    
    class AbstractChange
    {
    
    };
    
    class IntChange : public AbstractChange
    {
    public:
    	int newVal;
    	int oldVal;
    
    	IntChange(int newVal, int oldVal) : newVal(newVal), oldVal(oldVal) {}
    };
    
    class CharChange : public AbstractChange
    {
    public:
    	char newVal;
    	char oldVal;
    
    	CharChange(char newVal, char oldVal) : newVal(newVal), oldVal(oldVal) {}
    };
    
    map<time_t, AbstractChange> changelog;
    
    int main()
    {
    	changelog.insert(pair < time_t, AbstractChange>(time(NULL), IntChange(0, 0)));
    }

    Dann bräuchte ich aber für alle Datentypen eine eigene Klasse, die jeweils eine Änderung von einer Variable dieses Typs behandelt. Das wäre dann noch aufwendiger, als in einer Klasse "Change" von allen Datentypen Variablen für jeweils neu und alt bereitzustellen. Habe ich das falsch verstanden?

  6. Hallo,

    ich versuche mich gerade an einem Changelog, das, sobald eine Variable eines beliebigen Typs geändert wird, ebendiese Änderung mit einem Timestamp als Key in einer "map" abspeichern soll. Um nicht an einen Datentyp gebunden zu sein, habe ich eine Klasse "Change" als template geschrieben, die jeweils neuen und alten Wert der Variable beinhalten soll (die Variable, auf die sich die Änderung bezieht, wird noch nicht referenziert, was für mein Problem aber auch egal ist). Das ganze nutzt mir natürlich herzlich wenig, wenn ich mich bei der Definition der "map" dann doch auf einen Datentyp festlegen muss.

    Ich hatte mir das so vorgestellt:

    #include <ctime>
    #include <iostream>
    #include <map>
    
    using namespace std;
    
    template<class T> class Change
    {
    	T n;
    	T o;
    
    public:
    	Change(T n, T o) : n(n), o(o) {}
    
    	T getN() { return n; }
    	T getO() { return o; }
    };
    
    map<time_t, Change< /* ??? */ >> changelog;
    
    int main()
    {
    	changelog.insert(pair < time_t, Change<int>>(time(NULL), Change<int>(0, 0)));
    }

    Die mit "???" markierte Stelle ist mein Problem. Kann ich da irgendetwas einsetzen, um den Datentyp, der hinterher tatsächlich verwendet werden soll, noch offen zu lassen oder geht das sowieso nicht?

    Die einzige Alternative, die mir einfällt, ist "Change" (dann nicht als template) so umzuschreiben, dass die Klasse für alle möglichen Datentypen Variablen für jeweils neu und alt bereitstellt und entsprechende Konstruktoren. Das dürfte aber ziemlich aufwendig werden. Gibt es eine andere (einfachere) Lösung?

    Gruß

    PL1994

  7. Hallo,

    ich möchte ein paar Datensätze aus einer SQLITE-Datenbank in eine Liste schreiben. Das Auslesen an sich funktioniert, die Liste wird auch initialisiert. Allerdings werden die vorherigen Einträge immer wieder mit dem nächsten Datensatz überschrieben. Die Liste hat am Schluss also die richtige Länge, aber alle Einträge entsprechen dem letzten, der ausgelesen wurde.

    Hier der Quellcode:

    list<Country> countriesList;
    
    	while (sqlite3_step(statements[GET_COUNTRIES]) == SQLITE_ROW) {
    		countriesList.push_back(Country((int)sqlite3_column_int(statements[GET_COUNTRIES], 0), (char *)sqlite3_column_text(statements[GET_COUNTRIES], 1), (char *)sqlite3_column_text(statements[GET_COUNTRIES], 2), (char *)sqlite3_column_text(statements[GET_COUNTRIES], 3)));
    
    
    		for (list<Country>::iterator i = countriesList.begin(); i != countriesList.end(); ++i)
    		{
    			Country c = *i;
    			OutputDebugStringA(c.getName());
    			OutputDebugStringA("\n");
    		}
    	}
    
    	sqlite3_reset(statements[GET_COUNTRIES]);

    Warum klappt das nicht?

    Gruß

    PL1994

  8. Hallo,

    ich möchte ein Dialogfenster für eine Benutzereingabe erstellen. So etwas ist doch grundsätzlich als Dialog Box zu klassifizieren, oder nicht? Typischer Window Style für solche ist laut dieser Seite WS_DLGFRAME. Der nächste Satz lautet aber, ein solches Fenster könne nicht auch eine Titelleiste haben. Unter einer Dialog Box stelle ich mir so etwas vor, also gerade ein Fenster mit Titelleiste. Dieser Style ist aber eigentlich WS_POPUP, das seinerseits aber nicht WS_CHILD des Hauptfensters sein kann (Eine Dialog Box ist doch Child vom Hauptfenster!?).

    Also: Warum soll der Standard Style für Dialog Boxes keine Titelleiste haben können? Hat es Vorteile, wenn ich mir die Leiste selber da hineinbastele, nur um den adäquaten Style zu verwenden? Wenn der Style für meine Zwecke gar nicht gedacht sein sollte, wofür ist er denn normalerweise vorgesehen?

    Gruß

    PL1994

  9. Hallo,

    ich möchte ein Programm gerne nach der Model-View-Controller-Architektur strukturieren. Der Controller erbt dabei von "CWinApp", die View von "CFrameWnd". Das Model ist für mein Problem im Augenblick unwichtig.

    Mein Problem ist, dass ich es nicht hinbekomme, dass der Controller auf Events, die von der View ausgelöst werden, reagiert. Wenn ich das richtig verstanden habe, sollte man innerhalb einer Message Map des "CFrameWnd" definieren, welche Funktion etwa bei "ON_BN_CLICK" aufgerufen werden soll. Das funktioniert auch glänzend, wenn die entsprechende Funktion Member des "CFrameWnd" ist. Damit wäre sie aber Teil der View, was ja nun gerade nicht der Fall sein soll. Vielmehr sollte die Funktion im Controller definiert sein. Auf eine solche kann ich aber in der Message Map des "CFrameWnd" natürlich nicht verweisen. Ist ja auch im Prinzip richtig, die View soll den Controller nicht kennen.

    Wie bekomme ich es denn hin, dass mehr oder weniger "vom Controller aus" die Funktionen zu den entsprechenden Events zugeordnet werden?

    Um das exemplarisch zu verdeutlichen, hier das Notwendigste in ein paar Zeilen Code:

    #include <afxwin.h>
    
    #define ID_OK_BUTTON 1
    
    class View : public CFrameWnd
    {
    private:
    	CButton okButton;
    
    protected:
    	DECLARE_MESSAGE_MAP()
    
    public:
    	View()
    	{
    		Create(NULL, _T("View"));
    		okButton.Create(_T("OK"), WS_CHILD | WS_VISIBLE, CRect(0, 0, 200, 100), this, ID_OK_BUTTON);
    	};
    };
    
    class Controller : public CWinApp
    {
    private:
    	View *view;
    
    
    	afx_msg void onClick();
    
    public:
    	BOOL InitInstance();
    };
    
    
    BOOL Controller::InitInstance()
    {
    	view = new View();
    
    	m_pMainWnd = view;
    	m_pMainWnd->ShowWindow(SW_SHOW);
    
    	return TRUE;
    }
    
    afx_msg void Controller::onClick()
    {
    	TRACE("CLICKED");
    }
    
    BEGIN_MESSAGE_MAP(View, CFrameWnd)
    	ON_BN_CLICKED(ID_OK_BUTTON, /* ??? */)
    END_MESSAGE_MAP()
    
    Controller c;

    Ist das überhaupt im Ansatz richtig? Funktioniert das mit Message Maps oder muss man das irgendwie anders machen?

     

    Gruß

    PL1994

  10. Hallo,

    um verschiedene Child Windows in ein mein Hauptfenster möglichst passgenau einzufügen, bräuchte ich die Höhe der Titelleiste. Um diese auszulesen, werden auch verschiedene Wege vorgeschlagen, die aber an der dafür wohl eigentlich vorgesehenen Funktion "GetSystemMetrics" vorbeigehen. Ich würde das schon gerne mit dieser bewerkstelligen.

    Nach dieser Seite müsste der dafür richtige Index "SM_CYCAPTION" sein: https://msdn.microsoft.com/en-us/library/aa929225.aspx

    Damit habe ich es versucht. Hier der Code:

    MainFrame.h

    
    #pragma once
    
    
    #include <afxcmn.h>
    
    #include <afxwin.h>
    
    
    class MainFrame : public CFrameWnd
    
    {
    
    private:
    
    	const int MAINFRAME_HEIGHT = 768, MAINFRAME_WIDTH = 1024, TREE_HEIGHT = MAINFRAME_HEIGHT - GetSystemMetrics(SM_CYCAPTION), TREE_WIDTH = MAINFRAME_WIDTH / 4;
    
    
    	CTreeCtrl tree;
    
    
    public:
    
    	MainFrame();
    
    	MainFrame(CSize displaySize);
    
    };
    
    
    MainFrame.cpp
    
    #include "MainFrame.h"
    
    
    MainFrame::MainFrame()
    
    {
    
    	Create(NULL, _T("MainFrame"));
    
    }
    
    
    MainFrame::MainFrame(CSize displaySize)
    
    {
    
    	Create(NULL, _T("MainFrame"), WS_OVERLAPPEDWINDOW, CRect(displaySize.cx / 2 - MAINFRAME_WIDTH / 2, displaySize.cy / 2 - MAINFRAME_HEIGHT / 2, displaySize.cx / 2 + MAINFRAME_WIDTH / 2, displaySize.cy / 2 + MAINFRAME_HEIGHT / 2));
    
    
    	tree.Create(WS_VISIBLE | WS_BORDER | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_DISABLEDRAGDROP | TVS_NOTOOLTIPS | TVS_EDITLABELS, CRect(0, 0, TREE_WIDTH, TREE_HEIGHT), this, 1);
    
    }
    
    

    Durch "Herumprobieren" habe ich inzwischen festgestellt, dass die Titelleistenhöhe etwa 43px beträgt. "GetSystemMetrics(SM_CYCAPTION)" gibt 23px zurück.

    Wie gesagt, mir ist bewusst, dass es auch andere Wege gibt, um an die Höhe zu kommen, aber wenn "GetSystemMetrics" dafür eigentlich vorgesehen ist, wird das ja wohl auch damit hinzubekommen sein.

    Deshalb die Frage: Ist "GetSystemMetrics(SM_CYCAPTION)" grundsätzlich dafür gedacht oder habe ich die Funktion falsch verstanden? Was sind die 23px denn dann eigentlich?

    Gruß

    PL1994

  11. Vorab vielen Dank für eure Antworten!

    Leider war ich wohl etwas ungeduldig und habe, bevor ich den Beitrag von pr0gg3r lesen konnte, mit der Neuinstallation begonnen. Damit habe ich mir jetzt tagelangen Ärger mit Fehlern bei Windows Update beschert, der sich jetzt aber wenigstens gelohnt hat. Insoweit kann ich sagen, dass die Neuinstallation zwar aufwendig ist, sich aber durchaus lohnt. Windows 10 läuft! Die Installation ging ohne Probleme.

    Gruß

    PL1994

  12. Hallo,

    ich hatte mir ein kostenloses Upgrade auf Windows 10 reserviert, was mir auch inzwischen zur Verfügung steht. Leider habe ich bisher nur erfolglos versucht, dieses durchzuführen. Es scheitert stets bei 75%, also wenn der zweite Installationsschritt abgeschlossen ist (Treiber etc.). An dieser Stelle sollte ein Neustart erfolgen. Das passiert gleich vier Mal. Nach dem vierten Neustart bekomme ich dann (wenn ich das Media Creation Tool benutze) die Fehlermeldung: "In der Phase SECOND_BOOT ist beim Vorgang BOOT ein Fehler aufgetreten" (jedenfalls so ähnlich). Die Fehlernummer ist C1900101-40017.

    Ich habe ein paar Beiträge gefunden, die sich mit einem Fehler in der gleichen Phase, allerdings nicht bei BOOT, sondern bei MIGRATE_DATA beschäftigen. Einige dort angebotene Lösungen habe ich versucht:

    - Alle noch für Windows 8.1 ausstehenden Updates installiert

    - Das Windows 10 Upgrade komplett neu heruntergeladen, um auszuschließen, dass die Dateien fehlerbehaftet sind

    - Installation mit und ohne Media Creation Tool versucht

    - USB-Geräte abgesteckt (Soundkarte und Drucker)

    - Keine Antivirensoftware außer der systemeigenen

    Das einzige, was mir jetzt noch einfällt, wäre ein "clean install" von Windows 8 durchzuführen, um sicherzugehen, dass wirklich gar nichts auf der Festplatte ist, was irgendwie stören könnte. Das verspricht allerdings, ein langwieriger Prozess zu werden, den ich nur in Kauf nehmen will, wenn das halbwegs erfolgversprechend ist. Könnte das denn helfen?

    Hat jemand einen Vorschlag, was ich noch versuchen könnte, bevor ich alles neu aufspielen muss? Hatte jemand den gleichen Fehler?

    Im Übrigen müssten die Hardware-Anforderungen erfüllt sein, aber um sicherzugehen, hier ein paar Daten:

    CPU: Intel Core i5 (3,3 GHz)

    RAM: 8 GB

    Grafikkarte: AMD Radeon HD 6900 Series

    Das reicht doch wohl, oder?

    Danke im Voraus und Gruß

    PL1994

  13. Hallo,

    ich habe eine Treeview. Die wird so erstellt:

    
    InitCommonControls();
    
    
    hWndTreeview = CreateWindow(WC_TREEVIEW, TEXT("Treeview"), WS_VISIBLE | WS_CHILD | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, rcTreeview.left, rcTreeview.top, rcTreeview.right, rcTreeview.bottom, hWnd, (HMENU)ID_TREEVIEW, GetModuleHandle(0), NULL);
    
    
    Das klappt soweit. Wenn ich das richtig verstanden habe, sollte das Event TVN_SELCHANGED als WM_NOTIFY Nachricht an das Parentwindow (hier also hWnd) gesendet werden (https://msdn.microsoft.com/en-us/library/windows/desktop/bb773544(v=vs.85).aspx). Im WndProc des hWnd kann ich auch WM_NOTIFY Nachrichten abfangen. Nur scheinen die IDs nicht zu stimmen. Ich habe das so versucht:
    
    LRESULT CALLBACK Controller::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    
    {
    
    	switch (message)
    
    	{
    
    	// [...]
    
    	case WM_NOTIFY:
    
    	{
    
    		NMTREEVIEW* pnmtv = (NMTREEVIEW*)lParam;
    
    
    		switch (pnmtv->hdr.idFrom)
    
    		{
    
    		case ID_TREEVIEW:
    
    			char cAction[20];
    
    			sprintf_s(cAction, 20, "%d\n  %d\n\n", pnmtv->action, TVN_SELCHANGED);
    
    			OutputDebugString(cAction);
    
    
    			break;
    
    		}
    
    
    		break;
    
    	}
    
    	}
    
    
    	return DefWindowProc(hWnd, message, wParam, lParam);
    
    }
    
    

    Die Ausgabe zeigt: Die ID von TVN_SELCHANGED sollte -402 sein. In pnmtv->action steht beim entsprechenden Event aber eine 1.

    Was stimmt nicht?

    Gruß

    PL1994

  14. Rate mal.

    Enthält MFC und ich darf es benutzen?

    Im Output-Fenster, wenn du als Quelle Debug ausgewählt hast.

    Habe ich. Da steht nichts.

    Hier der Code, aber so viel kann da eigentlich nicht falsch sein:

    Main.cpp

    
    #include <Windows.h>
    
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    
    {
    
    	OutputDebugString("Debugstring");
    
    
    	return 0;
    
    }
    
    

    :confused:

    EDIT:

    Habe es gefunden. Stand zwischen diversen anderen Ausgaben. Danke!

  15. Hallo,

    ich habe die Express-Version von Visual Studio, die MFC nicht beinhaltet, also kann ich die Log-Funktionen nicht nutzen.

    Eine Alternative, die ich gefunden habe, wäre "OutputDebugString", was für meine Zwecke auch reichen würde.

    Wo sollte der Ouput denn angezeigt werden? Nicht in der Debug-Ausgabe? Muss ich noch was an den Projekteinstellungen ändern? Oder gibt es eine andere Alternative?

    Gruß

    PL1994

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...