WPF ListBox -> Frage zu "seltsames Verhalten"


  • Administrator

    Hallo zusammen,

    Ich denke zwar, dass ich das Problem gelöst habe, möchte aber trotzdem die Sache noch in aller Klarheit für mich klären. Folgender Code hatte ich in C#. WPF wurde verwendet:

    // m_lsbReceive <- ist eine WPF ListBox
    
    foreach(string message in messages)
    {
      m_lsbReceive.Items.Add(message);
    }
    

    Das Verhalten war mehr als seltsam. Vor allem aber beim Selektieren gab es plötzlich doppelte Selektionen, obwohl nur auf ein einziges Item geklickt wurde. Und man konnte dann diese doppelt selektierten Items nicht mehr deselektieren.
    Irgendwann kam mir dann in den Sinn, dass man unter XAML eigentlich ein ListBoxItem mit entsprechendem Content einfügen würde. Daher habe ich den Code abgeändert zu:

    // m_lsbReceive <- ist eine WPF ListBox
    
    foreach(string message in messages)
    {
      ListBoxItem item = new ListBoxItem();
      item.Content = message;
    
      m_lsbReceive.Items.Add(item);
    }
    

    Und alles funktioniert, wie gewollt. Ich denke mal, dass ich hier schon die richtige Lösung gesehen habe, oder?
    Aber was ist dann eigentlich bei den Strings passiert? Wieso hat das überhaupt funktioniert, bzw. so seltsam? Das man Strings dazufügen kann, ist wohl klar, da die Funktion Objects erwartet. Aber wieso kam es zur Laufzeit nicht zur einer Exception oder sowas?

    Noch eine kleine Zusatzfrage zur ListBox . Ich habe den Code dann probiert zu erweitern:

    // m_lsbReceive <- ist eine WPF ListBox
    ListBoxItem item = null;
    
    foreach(string message in messages)
    {
      item = new ListBoxItem();
      item.Content = message;
    
      m_lsbReceive.Items.Add(item);
    }
    
    if(null != item)
    {
      // Dies hat nichts gebracht:
      item.BringIntoView();
    
      // Nur dies hat funktioniert:
      m_lsbReceive.ScrollIntoView(item);
    }
    

    Wieso funktioniert hier BringIntoView nicht? Wäre doch irgendwie das logischere von den beiden.

    Grüssli



  • Hallo

    Warum verwendest du nicht DataBinding?

    chrische


  • Administrator

    chrische5 schrieb:

    Warum verwendest du nicht DataBinding?

    1. Weil ich mich darin noch kaum eingearbeitet habe. Bin immer noch am lernen.
    2. Ich wüsste aktuell auch gar nicht, wie ich das hier damit umsetzen sollte. Ich bekomme Strings über einen Socket rein, diese müssen geparst werden, verändert usw. usf. und das Resultat wird dann in die ListBox eingetragen. Die ListBox ist aktuell also nur eine Resultatanzeige. Die Werte werden aber nicht weiterverarbeitet und werden auch sonst nirgends gespeichert. Ich wüsste nicht, wie ich hier etwas anbinden könnte, da ja nix vorhanden ist. 😉

    Grüssli



  • Hallo

    Du kannst messages an die ListBox binden.

    chrische


  • Administrator

    chrische5 schrieb:

    Du kannst messages an die ListBox binden.

    Messages wird jedes mal neu erstellt. Dort drin sind nur die neu empfangenen Nachrichten, welche entsprechen weitergeleitet wurden, damit sie in der ListBox eingefügt werden. Danach wird die Liste nicht mehr benötigt. Es gibt keine Liste mit allen empfangenen Nachrichten, ausser halt die ListBox selber.

    Grüssli



  • Hallo

    Warum erstellst du sie denn immer neu? Du kannst doch einfach den Inhalt löschen und füllen und dann die Liste binden.

    chrische


  • Administrator

    chrische5 schrieb:

    Warum erstellst du sie denn immer neu? Du kannst doch einfach den Inhalt löschen und füllen und dann die Liste binden.

    Weil Thread A die Liste erstellt und diese dann an Thread B weitergibt. Wenn ich die gleiche Liste verwenden würde, müsste ich die Verwendung der Liste synchronisieren. So kann ich nur den Dispatcher nehmen und ein Invoke machen.

    (so am Rande, auch wenn es gerade sehr interessant ist, darum geht es ja eigentlich nicht :))

    Grüssli



  • was die doppelte markierung an geht

    das laeuft so ab:
    der user klickt auf ein element
    das framework bekommt das mouse down event und will die markierung setzen
    dafuer sagt der der liste "packe objekt x in deine selecteditems collection"
    da es nur einfache strings sind - wird nur der inhalt verglichen
    also sucht die liste alle elemente mit dem inhalt ab
    dh das alle strings die gleich sind markiert werden

    durch dein erstellen eines listboxitems erstellst du unikate die die liste genau finden und markieren kann

    bezueglich des anderen wuerd ich es so machen:

    (mir ist grad langweilig also denk ich mir n bissl pseudocode aus - alles hier im forum getippt - muss nichts funktionieren)

    public class MessageItem : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private string _message;
        private DateTime _timeStamp;
        public MessageItem(string message)
        {
            Message = message;
            _timeStamp = DateTime.Now;
        }
    
        public string Message
        {
            get { return _message; }
            set
            {
                _message = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Message"));
            }
        }
    
        public string TimeStamp
        {
            get { return _timeStamp.ToString(CultureInfo.CurrentUICulture); }
        }
    }
    
    partial class YourWindowClass : Window
    {
        private ObservableCollection<MessageItem> _messages = new ObservableCollection<MessageItem>();
    
        public YourWindowClass()
        {
            InitializeComponent();
            MessageHistoryListView.DataContext = _messages;
        }
    
        public void OnRetrieveMessages(IEnumerable<string> messages)
        {
            foreach(string message in messages)
                messages.Add(new MessageItem(message));
        } 
    }
    
    <YourWindowClass ...>
    <!-- -->
    <ListView ItemSource="{Binding}" x:Name="MessageHistoryListView">
        <ListView.View>
            <GridView>
                <GridViewItem Header="{DynamicResource IDS_TIME}" DisplayMemberBinding="{Binding TimeStamp}" />
                <GridViewItem Header="{DynamicResource IDS_MESSAGE}" DisplayMemberBinding="{Binding Message}" />
            </GridView>
        </ListView.View>
    </ListView>
    <!-- -->
    </YourWindowClass>
    

  • Administrator

    @Mr Evil,
    Danke für die Erklärung.

    Mr Evil schrieb:

    bezueglich des anderen wuerd ich es so machen: ...

    Ich möchte hier nur nochmals klarstellen, dass dies von mir nicht als ein Problem angesehen wird. Das war eine Frage von chrische5, welche nichts mit meinen Fragen zu tun hatte 🙂

    Allerdings finde ich deine Lösung auch nicht besser. Wieso so umheimlich kompliziert, wenn es auch einfach geht? Was ich bisher von DataBinding gesehen habe, kann das extrem praktisch sein, wenn man gleiche Daten an verschiedenen Stellen darstellen, bzw. verbinden, möchte. Aber das heisst doch nicht, dass man nun alles über DataBinding machen sollte.
    Ich kann mich nur wiederholen, die Nachrichten werden bei mir nirgends gespeichert. Die werden empfangen, geparst, umgewandelt und dann direkt an die ListBox angefügt. Mehr passiert mit diesen Nachrichten nicht. Die werden nirgends sonst gespeichert und auch in der Zukunft nicht. Die müssen nur angezeigt werden, dass ist alles und das nur in dieser ListBox. Wieso sollte ich da plötzlich noch irgendwelche zusätzlichen Listen, Klassen, Delegates, Events usw. usf. erstellen, wenn ich den ganzen Kram gar nicht benötigte, nur damit dann DataBinding funktioniert? Das macht doch keinen Sinn...

    Naja, jetzt fehlt nur noch eine Erklärung zu BringIntoView , bzw. wieso das nicht funktioniert 🙂

    Grüssli



  • die liste hat den vorteil das du mit der gleich alles machen kannst
    eintraege entfernen, hinzufuegen, aendern, exportieren, ...
    wenn du spaeter planst die ausgaben zu exportieren brauchst du nur die liste speichern
    du kannst dann auch einfacher die liste anders sortieren ohne die eigentliche liste zu veraendern

    du bist dann direkt auf alles vorbereitet und kannst einfacher ggf die liste an anderer stelle weiterverarbeiten

    wenn du es aber einfacher haben willst:

    partial class YourWindowClass : Window
    {
        private ObservableCollection<string> _messages = new ObservableCollection<string>();
    
        public YourWindowClass()
        {
            InitializeComponent();
            DataContext = _messages;
        }
    
        public void OnRetrieveMessages(IEnumerable<string> messages)
        {
            foreach(string message in messages)
                messages.Add(message);
        }
    }
    
    <YourWindowClass ...>
        <!-- -->
        <ListBox ItemSource="{Binding}" />
        <!-- -->
    </YourWindowClass>
    

    dann gehts auch - (nur damit dann DataBinding funktioniert)
    //wenns dann mit dem selektieren nicht gehen sollte, bzw wieder mehrere selektiert werden:

    <YourWindowClass ...>
        <!-- -->
        <ListBox ItemSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <!-- -->
    </YourWindowClass>
    

    dann muesste es gehen


  • Administrator

    Mr Evil schrieb:

    die liste hat den vorteil das du mit der gleich alles machen kannst

    Ich will aber nichts mehr machen.

    Mr Evil schrieb:

    eintraege entfernen, hinzufuegen, aendern, exportieren, ...

    Ich will nur Einträge hinzufügen, der Rest wird nicht benötigt.

    Mr Evil schrieb:

    wenn du spaeter planst die ausgaben zu exportieren brauchst du nur die liste speichern

    Wir es nie geben.

    Mr Evil schrieb:

    du kannst dann auch einfacher die liste anders sortieren ohne die eigentliche liste zu veraendern

    Auch nicht nötig, die Reihenfolge muss immer gleich bleiben.

    Mr Evil schrieb:

    du bist dann direkt auf alles vorbereitet und kannst einfacher ggf die liste an anderer stelle weiterverarbeiten

    Es wird nichts anderes geben. In Zukunft wird höchstens der Teil entfernt. Das ist nur ein Randprodukt während der Entwicklung. Auch wenn die Empfehlung gut gemeint ist, es gibt auch Grenzen bei DataBinding, nicht? Man muss nicht immer alles mit dem Vorschlaghammer bearbeiten 🙂

    Grüssli


Anmelden zum Antworten