Problem z rozwijaniem TreeView za pomocą kodu

0

Potrzebuję dokopać się za pomocą kodu do pewnych miejsc w TreeView. Kodzik:

private void button2_Click(object sender, EventArgs e)
        {

            foreach (TreeNode tn in tvMain.Nodes)
            {
                if (tn.Text == "Test1")
                {
                    tn.Expand();
                }

            }

                foreach (TreeNode tn2 in tvMain.Nodes[26].Nodes)
                {

                    if (tn2.Text == "Test2")
                    {
                        richTextBox1.Text += tn2.Text + "\n";
                        tn2.Expand();
                    }
                }   

        } 

Pierwsza pętla działa, znajduje i rozwija węzeł "Test1". Z drugą jest problem, bo w powyższej postaci po prostu nie rozwija dziecka Test1. Próbowałem np while'em czekać aż tvMain.Nodes[26].Nodes.IsExpaned będzie true, ale mimo, że faktycznie jest, to następny węzeł nie jest rozwijany. W celach testowych wrzuciłem sobie MessageBox'a między pętlami, i po kliknięciu "OK" rozwijany jest "Test2". Dodam, że kolejne węzły po rozwinięciu są tworzone w czasie rzeczywistym za pomocą API. Jeśli nie da się tego naprawić w tym miejscu to wrzucę funkcję AfterExpand. Dzięki z góry.

0

Trochę brzydki ten Twój kod. Rozumiem, że
tvMain.Nodes[26].Nodes
jest tym samym co znaleziony wcześniej element z tekstem "Test1"? Jeżeli tak to zapamiętaj ten element po znalezieniu i użyj go do iterowania po podrzędnych.

Co to znaczy "w czasie rzeczywistym za pomocą API" ? Daj więcej kodu to będziemy mogli pomóc. Pokaż jak generujesz elementy i w jakim miejscu to wywołujesz.

0

No faktycznie, może być trochę niejasno. tvMain.Nodes[26].Nodes to zbiór podwęzłów węzła "Test1", po którym druga pętla właśnie iteruje. Jeśli chodzi o to zapamiętywanie - akurat w tym przypadku od początku wiem, że "Test1" ma indeks 26, faktycznie niezbyt profesjonalnie, ale potem to poprawie. Napisałem celowo o czasie rzeczywistym, ponieważ węzły nie są tworzone wcześniej, ale na bieżąco w czasie "expandowania" przy pomocy API do pewnego serwisu (ten kod to fragment przerabianej na własne potrzeby aplikacji). Dlatego też myślę, że przez to druga pętla nie działa jeśli uruchomimy ją zaraz po pierwszej. Przykład z MessageBox między pętlami - po kliknięciu OK, otworzył się węzeł z drugiej pętli.

Funkcja AfterExpand:

private void tvMain_AfterExpand(object sender, System.Windows.Forms.TreeViewEventArgs e)
        {
            if (e.Node != null)
            {
                e.Node.Nodes.Clear();
                //e.Node.Nodes.Add(new TreeNode("Loading..."));

                int eventParentId = 0;

                if (e.Node != null)
                {
                    CSharpAPI6.betfair.api.global.BFEvent eventItem = (CSharpAPI6.betfair.api.global.BFEvent)e.Node.Tag;
                    eventParentId = eventItem.eventId;
                }

                GetEventAsyncRequest(eventParentId, e.Node).ToString();
                
            }
            //Expanded = true;
        } 

Przyznam bez bicia, że nie ogarniam dobrze kodu całej aplikacji. Sporo plików w projekcie jak na moje małe doświadczenie.

0

Spróbuj wstawić

Application.DoEvents;

pomiędzy pętle (w miejscu gdzie miałeś MessageBox-a). Powinno pomóc.

0

Niestety, wstawienie

Application.DoEvents();

między pętlami nie pomogło. Specjalnie przetestowałem to na drzewie, w którym wszystkie nody znane są przed przeszukiwaniem - zadziałało. Zastanawia mnie też dlaczego takie coś nie pomaga (choć z tym jest podobnie jak z MessageBox, przed nim Count = 0, po nim wskazuje właściwą liczbę nod):

while (tvMain.Nodes[id].Nodes.Count > 20); //id - indeks wyższego węzła
break;
 
0

Problem będzie zapewne z funkcją GetEventAsyncRequest możesz wrzucić jej źródło? Coś wykonuje się w niej asynchronicznie (jej nazwa na to wskazuje) dlatego nie możesz się "zgrać" bo już po ustawieniu Expanded=true może się coś zmieniać w drzewie..

0
private IAsyncResult GetEventAsyncRequest(int eventParentId, TreeNode node)
        {
            // Get session from session manager
            string session = SessionTokenManager.GetSessionToken();

            // If no session, log in and get another one
            if (session == null)
            {
                session = MakeSession();
            }

            CSharpAPI6.betfair.api.global.GetEventsReq request = null;

            request = new CSharpAPI6.betfair.api.global.GetEventsReq();
            request.header = new CSharpAPI6.betfair.api.global.APIRequestHeader();
            request.header.sessionToken = session;
            request.eventParentId = eventParentId;

            var state = new CustomAsyncStateContainer(0, node, new object[] { eventParentId, node }, DateTime.Now);

            return simpleAPIWrapper.BFGlobalService.BegingetEvents(request, GetEventAsyncCallback, state);
        } 

Jeszcze tworzenie nod:

private void DisplayGetEventTypes(CSharpAPI6.betfair.api.global.GetEventTypesResp resp, CustomAsyncStateContainer state)
        {
            // We need to display information from the provided API response
            tvMain.Nodes.Clear();

            if (resp.eventTypeItems != null)
            {
                CSharpAPI6.betfair.api.global.EventType[] eventItems = resp.eventTypeItems;

                int nodeCount = eventItems.Length;
                TreeNode[] nodes = new TreeNode[nodeCount];
                for (int i = 0; i < nodeCount; i++)
                {
                    CSharpAPI6.betfair.api.global.BFEvent eventItem = new CSharpAPI6.betfair.api.global.BFEvent();
                    eventItem.eventName = eventItems[i].name;
                    eventItem.eventId = eventItems[i].id;

                    nodes[i] = new TreeNode(eventItems[i].name);
                    nodes[i].Tag = eventItem;
                    nodes[i].Nodes.Add(new TreeNode("")); // Create a fake child node
                }
                tvMain.Nodes.AddRange(nodes);
            } 
0

Wrzuć jeszcze deklarację IAsyncResult

0
public interface IAsyncResult
    {
        // Summary:
        //     Gets a user-defined object that qualifies or contains information about an
        //     asynchronous operation.
        //
        // Returns:
        //     A user-defined object that qualifies or contains information about an asynchronous
        //     operation.
        object AsyncState { get; }
        //
        // Summary:
        //     Gets a System.Threading.WaitHandle that is used to wait for an asynchronous
        //     operation to complete.
        //
        // Returns:
        //     A System.Threading.WaitHandle that is used to wait for an asynchronous operation
        //     to complete.
        WaitHandle AsyncWaitHandle { get; }
        //
        // Summary:
        //     Gets a value that indicates whether the asynchronous operation completed
        //     synchronously.
        //
        // Returns:
        //     true if the asynchronous operation completed synchronously; otherwise, false.
        bool CompletedSynchronously { get; }
        //
        // Summary:
        //     Gets a value that indicates whether the asynchronous operation has completed.
        //
        // Returns:
        //     true if the operation is complete; otherwise, false.
        bool IsCompleted { get; }
    } 
0

No to masz odpowiedź, musisz sprawdzić czy pole IsCompleted w obiekcie zwróconym przez GetEventAsyncRequest zawiera true. To powinieneś sprawdzać w pętli żeby upewnić się czy dane już się załadowały.

0

Hmm, nie bardzo wiem jak i w którym miejscu to zrobić. Spróbowałem wstawić while w funkcji buttona (która właśnie sprawdza i otwiera drzewa) między pętlami:

while (async.IsCompleted)
                break; 

Tutaj jednak nie znalazło tego pola i dlatego zdefiniowałem zmienną async, podobnie jak w funckji MakeSession, ale nie pomogło:

public string MakeSession()
        {
            string session = null;
            var async = LoginAsyncRequest(simpleAPIWrapper.username, simpleAPIWrapper.password, simpleAPIWrapper.productId, simpleAPIWrapper.softwareId, null);

            int count = 0;
            while (!async.IsCompleted || !SessionTokenManager.HasSessions)
            {
                Thread.Sleep(500);

                if ((count++) == 20)
                {
                    throw new Exception("Time out occured in \"public string MakeSession()\"");
                }
            }
            session = SessionTokenManager.GetSessionToken();

            if (session == null)
            {
                throw new Exception("String session is NULL in \"public string MakeSession()\"");
            }

            return session;
        }

To pewnie trzeba sprawdzić w AfterExpand, ale nie wiem jak się odnieść do tego pola.

0

Proponuję napisać klasę implementującą ten interfejs i zadeklarowanie IsCompleted jako public. A później zapisujesz rezultat do egzemplarza tej klasy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1