2009-03-20 16 views
5

Mam podklasę System.Windows.Forms.TreeView, która jest ręcznie "związana" z zestawem hierarchicznych danych. Chcę, aby użytkownik mógł edytować etykiety drzewa i odzwierciedlić zmiany w danych. Tak ustawić LabelEdit true i overrode OnAfterLabelEdit w wysokości:Czy mogę wstawić węzły do ​​TreeView podczas AfterLabelEdit bez rozpoczęcia ich edycji?

protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e) 
{ 
    base.OnAfterLabelEdit(e); 

    TreeNode node = e.Node; 

    if (PassesSomeValidation(e.Label)) 
    { 
     MyDataNode dataNode = node.Tag as MyDataNode; 
     dataNode.SomeBoundValue = e.Label; 

     int oldIndex = node.Index; 
     int newIndex = RepositionChangedDataNode(dataNode); 

     TreeNode parent = node.Parent; 
     parent.Nodes.RemoveAt(oldIndex); 
     parent.Nodes.Insert(newIndex, node); 
    } 
    else 
    { 
     e.CancelEdit = true; 
    } 
} 

RepositionChangedDataNode() ponownie sortuje dane i zwraca indeks, do którego węzeł zmiana przesuwany po sortowaniu. Miałem nadzieję, że mogę po prostu przesunąć edytowany węzeł, aby odzwierciedlić ten ruch.

Problem polega na tym, że węzeł do pozostaje w trybie edycji! Próbowałem wywoływać EndEdit(), klonując węzeł przed wstawieniem go, ustawiając LabelEdit na wartość false i przywracając wartość true, opakowując zmianę w BeginUpdate()/EndUpdate() i różne kombinacje tych pomysłów, ale żadna z nich nie ma żadnego efektu.

Winowajcą wydaje się być wstawienie. Nawet jeśli spróbuję wstawić całkowicie nowy węzeł , przejdzie on natychmiast w tryb edycji.

Czy istnieje sposób, aby zmienić zachowanie TreeView w ten sposób? A jeśli nie, czy istnieje dobre obejście?

Kilka pomysłów mam uznać:

  1. Ustaw niestandardowy TreeViewNodeSorter. Wolałbym jednak nie sortować moich danych dwa razy.
  2. Ustaw flagę i opóźnij krok do usunięcia, aż do pewnego punktu po AfterLabelEdit. Robi się to podczas WndProc, ale wydaje się, że to wielki klud, który jakoś się nie uda.
  3. Zastosowanie BeginInvoke() naciskać krok remove-insert z powrotem do kolejki komunikatów tak:

    BeginInvoke(new MethodInvoker(delegate(
    { 
        parent.Nodes.RemoveAt(oldIndex); 
        parent.Nodes.Insert(newIndex, node); 
    })); 
    

    To działa i wydaje odkurzacz do mnie niż 2, ale wiem, że to nie jest chyba jak BeginInvoke() miało do użycia i może to mieć konsekwencje, że moja bardzo ograniczona wiedza na temat pompy komunikatów nie jest w stanie przewidzieć.

+0

Mam ten sam problem - co zrobiłeś w końcu? – Handleman

+0

Poszedł z pomysłem # 3 na razie. Wydaje się, że do tej pory działa dobrze. –

+0

Po prostu miałem podobny problem podczas próby sortowania TreeView w zdarzeniu AfterLabelEdit. Użyłem rozwiązania podobnego do twojego i działało dobrze. To prawdopodobnie błąd w TreeView ... –

Odpowiedz

0

Jeśli używasz wiązania danych, czy aktualizacja źródła danych (SomeBoundValue) nie powinna wywołać odświeżenia węzłów? Może zmusisz menedżera walut do ponownego zaludnienia widoku drzewa ... Jeśli martwisz się wydajnością, możesz użyć jednego z algorytmów sortowania, który działa dobrze z danymi, które są już prawie posortowane (np. NOT quicksort - scalanie lub heapsortowanie przychodzi na myśl)

Albo można zrezygnować z powiązania danych całkowicie i ręcznie obsługiwać repozycjonowanie, ponieważ jesteś już w połowie drogi z RepositionChangedDataNode() ....

+0

TreeView nie obsługuje samodzielnie tworzenia kopii danych, dlatego ręcznie kopiuję zmienioną wartość do źródła kopii. Ręczne operowanie repozycjonowaniem jest dokładnie tym, co próbuję zrobić, ale powoduje to złudne nieumiejętne przerywanie edycji. –

1

spróbować utworzyć zmienną globalną, powiedzmy:

private bool _allowEdit; 

zainicjować go true,

w swojej metodzie OnAfterLabelEdit ustawić go false po wprowadzonych zmian:

... int oldIndex = node.Index; 
    int newIndex = RepositionChangedDataNode(dataNode); 

    TreeNode parent = node.Parent; 
    parent.Nodes.RemoveAt(oldIndex); 
    parent.Nodes.Insert(newIndex, node); 
    **_allowEdit = false;** 
} 
else ... 

następnie uchwycić zdarzenie typu:

protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e) 
    { 
     base.OnBeforeLabelEdit(e); 
     e.CancelEdit = !_allowEdit; 
     _allowEdit = true; 
    } 

Zauważyłem, że tuż po uruchomieniu "AfterLabelEdit", "BeforeLabelEdit" jest ponownie wystawiony. Dlatego musisz to zatrzymać.

+0

Próbowałem tego; to nie działa. Wstawiony węzeł nadal przechodzi w tryb edycji. –

0

Możesz spróbować odłączyć program obsługi OnEdit przed dodaniem nowego węzła i ponownym zahaczeniem go. Widziałem już to zachowanie i tak sobie z tym poradziłem.

+0

Ale problem nie polega na tym, że jakikolwiek przewodnik jest wywoływany, kiedy nie powinien. To nie pomoże. –

3

Jeśli ustawisz LabelEdit dla TreeView na false, nowo dodane węzły nie będą w trybie edycji.

Wystarczy zająć się sprawą, w której użytkownik chce edytować etykietę: Utwórz procedurę obsługi zdarzenia MouseClick z TreeView, w której kliknięcie węzła odbywa się według lokalizacji. Ustaw LabelEdit na true i zadzwoń pod numer BeginEdit(). Na końcu programu obsługi dla zdarzenia AfterLabelEdit (i po wywołaniu EndEdit(...) w odpowiednim punkcie) ponownie ustaw LabelEdit na false.

To działa dla mnie, podczas gdy rozwiązanie z BeginInvoke zmieniło tylko to, który węzeł był w trybie edycji na końcu.