Datenaustausch zwischen RightView/LeftView - wie macht man es vernünftig?


  • Mod

    Roger Wilco schrieb:

    Wenn ich Dich richtig verstanden habe würde man zum Beispiel so vorgehen:
    1. Änderung im CLeftView (CTreeView)durch Benutzer
    2. CLeftView ruft GetDocument()->UpdateAllViews(this, ...) auf
    3. Bei allen Views wird die OnUpdate(...)-Methode aufgerufen und die Parameter durchgereicht

    Richtig?

    Korrekt!

    Roger Wilco schrieb:

    Warum kursieren dann immer so komische Lösungen im Netz, wo nach Herzenslust Objekt-Pointer und public Member quer durch alle Objekte getauscht und gespeichert werden?

    Einer der Punkte unten reproduziert oft den nächsten
    1. Weil User falsch fragen: "Wie komme ich in View1 an einen Zeiger von View2?" und nicht "Wie kommunizieren Views miteinander?"
    2. Weil nur wenige eigentlich wissen wie es richtig geht...
    3. Weil miese Antworten im Netz oft schneller reproduziert werden als "richtige"
    4. Weil viele Frager nur an einer schnellen Antwort interssiert sind, egal wie blöde oder mies die ist....
    5. Wer ist schon wirklich an sauberen OOP-Design interssiert?
    6. Die MFC hat kein sauberes OOP Design! 😉
    7. Weil die wenigen User, die Ahnung haben, keine Lust mehr haben dem vielen Mist im Netz dauernd zu widersprechen...

    Die Liste mag beliebig fortgesetzt werden... 🤡



  • Dann möchte ich Dir für Deine Hilfe recht herzlich danken! 👍



  • Ich muss das Thema nochmal aufgreifen.

    Mein ListView zeigt eine Auflistung von Datenpunkten an. Wenn ich nun einen neuen Wert für einen Datenpunkt erhalte, wollte ich UpdateAllViews() aufrufen, um die Liste zu aktualisieren. Um nicht alle Datenpunkte jedes mal zu aktualisieren, wenn sich ein Wert ändert, wollte ich die Übergabeparameter von UpdateAllViews nutzen, um nur den Wert eines einzelnen Datenpunktes zu aktualisieren. Dabei signalisiert UPDATE_SINGLE_DP_VALUE , dass ein einzelner Datenpunkt aktualisiert werden muss und tagName enthält den Key für eine std::tr1::unordered_map<CString, DataPoint> , die alle Datenpunkte enthält.

    CString tagName = ...;
    UpdateAllViews(this, UPDATE_SINGLE_DP_VALUE, tagName);
    

    Tja, leider funktionierte das nicht, was daran lag, dass dieser Aufruf in einem anderen Thread ablief, der die Datenkommunikation handelt. 😞

    Wie mache ich das nun am besten, bzw. wie wird es i.d.R. gemacht? 😕

    Bin jetzt auf PostMessage() umgestiegen. Da ein CDocument keine Nachrichten empfangen kann (da es kein Fenster besitzt), wird diese in der MainFrm verarbeitet:

    AfxGetMainWnd()->PostMessage(WM_NEW_DATA, 0, UPDATE_SINGLE_DP_VALUE)
    
    //MainFrm.cpp:
    
    BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWndEx)
        //...
        ON_MESSAGE(Messages::WM_NEW_DATA, &CMainFrame::OnNewData)
    END_MESSAGE_MAP()
    
    //...
    
    LRESULT CMainFrame::OnNewData(WPARAM wParam, LPARAM lParam)
    {
        MDIGetActive()->GetActiveView()->GetDocument()->UpdateAllViews(NULL, lParam);
        return NULL;
    }
    

    So kann ich leider nicht den CString tagName des betroffenen Datenpunktes übergeben.

    Ist das der richtige Ansatz? Wie übertrage ich am besten die Information, welcher Datenpunkt aktualisiert werden soll? 😕


  • Mod

    Du kannst aber eine WM_COMMAND Nachricht posten und einen verdeckten Handler einbauen der im Dokument dann entsprechend reagiert.

    WM_COMMAND Nachrichten werden auch durch das Dokument geroutet!



  • Hallo Martin,

    du meinst WM_COMMAND direkt an das Dokument wäre in diesem Fall besser?

    Doch wie löse ich das Problem, dass ich dann den Listeneintrag identifiziere, welcher aktualisiert werden soll?

    Vielleicht eine extra map mit einem int als Key und dann diesen Key übergeben?


  • Mod

    OK. WM_COMMAND hättest Du einfach an das Mainframe posten können. Das wäre in das aktive Dokumenbt geroutet worden.

    Aber mit WM_COMMAND kann man keinen Punkt angeben... Fehlgedanke meinerseits.

    Du müsstest die Kommunikation zwischen Datenerzeuger und DOkument auf andere Füße stellen.
    Ich weiß nicht wie schnell die Verarbeitung sein muss, aber ich könnte mir folgendes vorstelllen:
    - Der Thread erzeugt die Daten und Änderungen und packt die in eine Liste.
    - Die Liste muss mit einer Critical Section abgearbeitet werden.
    - In CDocument::OnIdle platzierst Du eine Funktion, die nachsieht ob etwas in der Liste drin ist. Wenn ja wird das Zeug an die Views weitergegebem.
    - Im Mainframe setzt Du einen Dummy-Timer mit der Auflösung in der evt. mal nachgesehen werden soll ob Daten abgearbeitet werden sollen. (1/10 sec. o.ä.), denn OnIdle wird nur ausgeführt wenn nach einer Windows Nachricht kene weiteren Nachrichten anliegen.
    - Alternativ sendest Du ein WM_COMMAND an ein Fenster (Dokuemnt geht nicht) und dieses Nachricht wird an das Dokument (Für Views/Frame der Standard) geroutet und diese Nachricht führt dazu, dass in die Liste gesehen wird ob Infos anliegen zm bearbeiten. (Achtung MDI/SDI Routng unterscheidet sich!)

    Isoliere Datenhaltunf/Erzeugung. Das Dokument muss selbst wissen wann es was tun soll. Andere Threads können ihm schlecht was sagen. Höchstens einen Anstoß geben

    BTW: Von meheren Threads war in Deiner ersten Anfrage nix drin... 😉



  • Vielen Dank! Das hört sich gut an...

    Martin Richter schrieb:

    Isoliere Datenhaltunf/Erzeugung. Das Dokument muss selbst wissen wann es was tun soll.

    Kannst Du das noch etwas näher erläutern? Zur Zeit wird der Thread zum Datenaustausch vom Dokument selber angestoßen und der Thread benutzt nur Methoden und Member des Dokumentes.

    Martin Richter schrieb:

    BTW: Von meheren Threads war in Deiner ersten Anfrage nix drin... 😉

    Das ist richtig - da ging es auch um Datenaustausch zwischen LeftView und RightView (ohne Threading). Was ich da gelernt habe wollte ich nun in einer Multithreading-Umgebung umsetzten - und das klappte halt nicht.



  • Funktioniert! 👍

    //aus dem Thread:
    void DoDataExchange()
    {
       // Daten holen
       // Critical Section betreten
       // CMyDoc::m_newData mit Zeigern auf neue Daten befüllen
    
       AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_NEW_DATA);
    
       // Critical Section verlassen
    }
    
    //MyDoc.cpp:
    
    BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
        //...
        ON_COMMAND(ID_NEW_DATA, &CMyDoc::OnNewData)
    END_MESSAGE_MAP()
    
    //...
    
    void CMyDoc::OnNewData()
    {
       // Critical Section betreten
       UpdateAllViews(NULL, UPDATE_DP_VALUES); // --> Views greifen auf m_newData zu
       // Liste löschen
       // Critical Section verlassen
    }
    

    Danke nochmal! 🙂



  • Ich greife das Thema LeftView/RightView nochmal auf:

    In meinem Fall (MDI) möchte ich eine Kopplung des LeftView an das RightView haben. Wenn im LeftView (TreeCtrl) ein neue Auswahl stattfindet soll nur das dazugehörige LeftView des selben Fensters entsprechend darauf reagieren.

    Damit sollen verschiedene Ansichten des gleichen Dokumentes möglich sein, bei dem jeweils andere Zweig-Inhalte des LeftView angezeigt werden. Zur Zeit reagieren alle RightViews aller Fenster darauf, wenn ich im LeftView eine andere Auswahl tätige - was ja der Sinn von CDocument::UpdateAllViews() ist.

    Wäre es in diesem Fall angebracht, wenn man die beiden Views über Zeiger sich gegenseitig bekannt macht? Und die beiden Views über Methodenaufrufe direkt miteinander kommunizieren?


  • Mod

    Find ich nicht prickelnd. Aber warum nicht.

    Da beide Fenster im selben MDI-Child-Frame (vermute ich mal) liegen kann jeder View sowieso seinen Partner schnell ermitteln. Man durchlaufe alle Views und suche den View der im selben Frame (GetParentFrame) liegt.

    Vom Design her würde ich hier nicht die Klasse selbst bekannt machen sondern ein Interface verwenden.
    Theoretisch würde dies auch den direkten Aufruf von OnUpdate erlauben 🙂


Anmelden zum Antworten