.NET WinForms σύνθετο πλαίσιο, όμοια στοιχεία, και το γεγονός SelectedIndexChanged

ψήφοι
5

Φαίνεται ότι όταν έχεις μια εφαρμογή WinForms .NET, και ένα σύνθετο πλαίσιο (που στο στυλ «Αναδυόμενη»), και ότι Σύνθετο πλαίσιο έχει πολλά στοιχεία σε αυτό που είναι ταυτόσημα, περίεργα πράγματα συμβαίνουν. Συγκεκριμένα, ο δείκτης του επιλεγμένου στοιχείου μπορεί να αλλάξει χωρίς ψήσιμο το γεγονός SelectedIndexChanged.

Φυσικά, αυτό προκαλεί μαζική σύγχυση και περίεργα, σκοτεινά σφάλματα, το οποίο είναι αυτό που έχω τραβώντας τα μαλλιά μου πάνω από τον τελευταίο καιρό.

Εδώ είναι ένα απλό παράδειγμα που μπορείτε να χρησιμοποιήσετε για να δείτε τι μιλώ για:

  • Δημιουργήστε ένα νέο έργο .NET WinForms (χρησιμοποιώ VB.NET, αλλά μη διστάσετε να μεταφράσει - είναι αρκετά απλό).
  • Drop ένα σύνθετο πλαίσιο, ένα κουμπί, και ένα πλαίσιο κειμένου (που MultiLine = True) στη φόρμα.
  • Χρησιμοποιήστε τον παρακάτω κώδικα για να φορτώσει το σύνθετο πλαίσιο με 3 πανομοιότυπα στοιχεία και να εκτυπώσετε κάποια μηνύματα κατάστασης, όταν οι πυρκαγιές περίπτωση SelectedIndexChanged, και να δούμε ποιο είναι το τρέχον επιλεγμένο δείκτη είναι (μέσω ενός κουμπιού):
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
        TextBox1.Text = TextBox1.Text & vbNewLine & ComboBox SelectedIndexChanged event fired. & vbNewLine & _
            SelectedIndex is:  & ComboBox1.SelectedIndex
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        ComboBox1.Items.Add(John Doe)
        ComboBox1.Items.Add(John Doe)
        ComboBox1.Items.Add(John Doe)

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        TextBox1.Text = TextBox1.Text & vbNewLine & _
        Button clicked. & vbNewLine & _
        SelectedIndex is:  & ComboBox1.SelectedIndex
    End Sub

Εκτελέστε το πρόγραμμα και επιλέξτε ένα στοιχείο από το σύνθετο πλαίσιο (για παράδειγμα, η μεσαία). Στη συνέχεια, κάντε κλικ στο αναπτυσσόμενο βέλος του Σύνθετο πλαίσιο, αλλά μην επιλέξετε τίποτα. Κάντε κλικ στο κουμπί (Button1 από προεπιλογή) και να δούμε τι λέει.

Εκτός αν έχω χάσει το μυαλό μου, εδώ είναι ό, τι πρέπει να δείτε:

Σύνθετο πλαίσιο SelectedIndexChanged εκδήλωση καύση.
SelectedIndex είναι: 1
Κουμπί κλικ.
SelectedIndex είναι: 0

Με άλλα λόγια, ο επιλεγμένος δείκτης έχει αλλάξει, αλλά χωρίς το γεγονός πυροδότησης SelectedIndexChanged!

Αυτό συμβαίνει μόνο όταν τα στοιχεία στο σύνθετο πλαίσιο είναι πανομοιότυπα. Αν είναι διαφορετικά, αυτό δεν συμβαίνει. (Επίσης δεν θα συμβεί αν το στυλ «Αναδυόμενη» του Σύνθετο πλαίσιο έχει οριστεί σε «DropDownList.»)

Υποψιάζομαι ότι αυτό μπορεί να είναι ένα σφάλμα στο ίδιο το πλαίσιο .NET και δεν είναι κάτι που μπορώ να διορθώσετε, αλλά στη μακριά πιθανότητα ότι κάποιος άλλος έχει κάποιες ιδέες για το τι να κάνουμε εδώ (ή ό, τι θα μπορούσε να κάνει λάθος!), Παρακαλούμε να συνάδουν με ! Είμαι σε μια απώλεια για να εξηγήσει αυτή τη συμπεριφορά ή την εργασία γύρω από αυτό (περιμένω το SelectedIndex να παραμείνει το ίδιο, εκτός αν, έλεγα, μπορείτε να την αλλάξετε στην πραγματικότητα, επιλέγοντας το κάτι άλλο!)

Δημοσιεύθηκε 09/12/2008 στις 23:04
πηγή χρήστη
Σε άλλες γλώσσες...                            


4 απαντήσεις

ψήφοι
17

Το .NET Framework δεν κρατήσει στην πραγματικότητα κομμάτι του επιλεγμένου δείκτη της πτώσης του σύνθετου πλαισίου της λίστας? Αυτό αντιμετωπίζεται εσωτερικά από το API των Windows. Ως συνέπεια αυτού, NET είναι εξαρτημένη από το API των Windows για να ειδοποιεί όταν οι επιλεγμένες αλλαγές του δείκτη μέσω ενός μηνύματος ειδοποίησης αποστέλλεται λαβή παράθυρο του σύνθετου πλαισίου, έτσι ώστε να μπορεί με τη σειρά του τροφοδοτεί την εκδήλωση SelectedIndexChanged.

Δυστυχώς, αποδεικνύεται ότι το συγκεκριμένο μήνυμα ειδοποίησης που .NET ρολόγια για ( CBN_SELCHANGEγια να είμαστε ακριβείς) δεν καλύπτει όλα τα πιθανά σενάρια στα οποία το επιλεγμένο δείκτη θα μπορούσε να αλλάξει. Συγκεκριμένα, CBN_SELCHANGEαποστέλλεται μόνο από το API των Windows, αν ο χρήστης κάνει κλικ σε, ή να επιλέγει χρησιμοποιώντας τα πλήκτρα βέλους, ένα στοιχείο στην αναπτυσσόμενη λίστα. Ωστόσο, σε ένα σύνθετο πλαίσιο στυλ Αναδυόμενη, η πράξη ανοίγει το σύνθετο πλαίσιο προκαλεί Windows για να δούμε το κείμενο στο τμήμα επεξεργασίας του σύνθετου πλαισίου, αναζητήστε στη λίστα των στοιχείων για έναν αγώνα, και αν βρεθεί ένας αγώνας, αυτόματα επιλέξτε το στοιχείο που ταιριάζουν (ή το πρώτο στοιχείο που να ταιριάζουν, αν υπάρχουν πολλά στοιχεία που να ταιριάζουν). Αυτό μπορεί να αλλάξει το επιλεγμένο δείκτη, αλλά δεν στέλνει ένα CBN_SELCHANGEμήνυμα ειδοποίησης, έτσι .NET χάνει το γεγονός ότι άλλαξε και δεν φωτιά το γεγονός SelectedIndexChanged.

Παράθυρα κάνει όλα αυτά σε ένα σύνθετο πλαίσιο στυλ Αναδυόμενη επειδή ο χρήστης δεν χρειάζεται να πάρει κάτι από την αναπτυσσόμενη λίστα? μπορούν να πληκτρολογήσετε ό, τι θέλουν. Έτσι, κάθε φορά που ανοίγετε το σύνθετο πλαίσιο υποθέτει ότι ο χρήστης μπορεί να έχει αλλάξει το κείμενο και προσπαθεί να συγχρονιστεί ξανά με αυτό που είναι στη λίστα, αν μπορεί.

Στην περίπτωσή σας, όταν ανοίγετε το σύνθετο πλαίσιο για δεύτερη φορά, θα είναι εκ νέου συγχρονισμό και την επιλογή του πρώτου αγώνα για το κείμενο στο τμήμα επεξεργασίας, η οποία είναι «John Doe» # 0, και αλλάζοντας το επιλεγμένο δείκτη στο 0 χωρίς ΝΕΤ να το γνωρίζει.

Γι 'αυτό είναι βασικά ένα σφάλμα το .NET Framework. Δυστυχώς, δεν υπάρχει τέλεια λύση - δεν μπορείτε να πάρετε τα Windows να μην κάνει την εκ νέου συγχρονισμό, και δεν υπάρχει περίπτωση που οι πυρκαγιές αμέσως μετά την εκ νέου συγχρονισμού εμφανίζεται στην οποία μπορείτε να πάρετε το νέο επιλεγμένο δείκτη. (Η εκδήλωση Αναδυόμενη πυρκαγιές στην πραγματικότητα λίγο πριν συμβεί η εκ νέου συγχρονισμό, έτσι δεν θα δείτε το νέο δείκτη.) Σχετικά με το καλύτερο που μπορείτε να κάνετε είναι να χειριστεί το γεγονός DropDownClosed, ας υποθέσουμε ότι ο δείκτης θα μπορούσε να αλλάξει σε αυτό το σημείο, και να ενεργήσει αναλόγως .

Απαντήθηκε 10/12/2008 στις 04:29
πηγή χρήστη

ψήφοι
2

απάντηση του Έρικ ήταν πολύ εμπεριστατωμένη, αλλά ήμουν έκπληκτος να δω ότι δεν τελειώνει με «... αλλά πραγματικά, θα πρέπει να αναρωτηθείτε γιατί πληθυσμιακή ένα σύνθετο πλαίσιο με διπλότυπα στοιχεία.» Το σφάλμα του .NET Framework χωρίς αμφιβολία έχει το δικαίωμα να υπάρχει, διότι όταν χρησιμοποιείτε το στοιχείο ελέγχου όπως προβλεπόταν, για να επιτρέψει στο χρήστη να επιλέξει ένα στοιχείο από μια λίστα, δεν έχετε τρέξει σε αυτό το σφάλμα.

Πώς είναι ο χρήστης πρόκειται να γίνει διάκριση μεταξύ των πανομοιότυπες καταχωρήσεις; Γιατί να επιλέξετε ένα πάνω στο άλλο; Είναι ένα διαφορετικό νόημα στα διάφορα στοιχεία εκεί; Αν ναι, τότε έχει διπλές καταχωρήσεις είναι διφορούμενη, η οποία είναι πάντα κακό σχεδιασμό ευχρηστίας. Αν όχι, τότε θα πρέπει να μην έχουν διπλότυπα στοιχεία.

Το μόνο σενάριο που μπορώ να σκεφτώ, όπου αυτό θα μπορούσε να έχει νόημα είναι όταν έχετε μια μεγάλη λίστα που αποτελείται από διάφορες ομάδες των σχετικών στοιχείων, όπου ένα ή περισσότερα στοιχεία ταιριάζει λογικά σε περισσότερες από μία ομάδες, έτσι θέλετε να εμφανιστεί και στα δύο τμήματα.

Υποθέτω το σχέδιό σας δεν ευθύνονται για το γεγονός ότι μπορεί να υπάρχουν πολλές ταυτόσημες εγγραφές και ότι η παράλειψη αυτή θα έχει άλλες συνέπειες χρηστικότητα που είναι πιο σημαντικό από αυτό το πρόβλημα. Φυσικά, καταλαβαίνω ότι μπορεί να κάνει κάτι που δεν έχω σκεφτεί όπου κάνει απολύτως νόημα να κάνουμε ό, τι κάνεις, οπότε μπορείτε να αισθανθείτε ελεύθεροι να αγνοούν τα σχόλιά μου.

Απαντήθηκε 30/03/2009 στις 07:54
πηγή χρήστη

ψήφοι
1

Υπάρχουν περιπτώσεις όπου έχουν διπλότυπα στοιχεία στη λίστα είναι έγκυρη όχι μόνο, αλλά και επιθυμητή. Εξετάστε το combo box OpenFileDialog που βλέπετε στο Visual Studio, όταν πατήσετε το κουμπί Άνοιγμα αρχείου. Αυτό δείχνει ένα σύνθετο πλαίσιο με τα στοιχεία, όπως «Ο υπολογιστής μου», «γραφείου», «Τα έγγραφά μου», κ.λπ. Για τα ονόματα των φακέλων, μόνο το μικρό όνομα είναι στη λίστα. Η πλήρης διαδρομή δεν εμφανίζεται. Και έτσι είναι πολύ πιθανό ότι ένας φάκελος έχει το ίδιο (μικρό) όνομα ως ένας από τους απογόνους του.

Φανταστείτε λοιπόν την ακόλουθη δομή φακέλων:

C:\
C:\A
C:\A\B
C:\A\B\A

Μια απόλυτα έγκυρη δομή. Σε εφαρμογή μου ορίσετε την ιδιότητα DataSource σε BindingList των αντικειμένων. Η ValueMember του αντικειμένου είναι το πλήρες όνομα του αρχείου και το DisplayMember είναι το μικρό όνομα του αρχείου. Το σύνθετο πλαίσιο θα πρέπει να εμφανίζει:

C:\
    A
        B
            A

Τέλεια καλό σχεδιασμό UI. Η εσοχή προτείνει το φώλιασμα των φακέλων.

Αλλά όταν έθεσα SelectedValue του σύνθετο πλαίσιο για να «C: \ Α \ Β \ Α» το λανθασμένο στοιχείο παίρνει επιλέξει. Το στοιχείο που θα πρέπει να επιλεγεί είναι το τελευταίο (4ο σημείο) στη λίστα, αλλά αντ 'αυτού το 2ο θέμα (δείκτης 1) επιλέγεται. Και τη SelectedIndex = 3 δεν συμπεριφέρεται όπως θα έπρεπε. Και πάλι, έχει επιλεγεί το δεύτερο στοιχείο, όχι η τελευταία.

Αυτό που φαίνεται να συμβαίνει εδώ είναι ότι κατά τον καθορισμό SelectedValue ή SelectedIndex, η αξία μετατρέπεται με τη χρήση του ακινήτου DisplayMember, και ο έλεγχος αναζήτηση από την αρχή μέχρι το τέλος για έναν αγώνα. Θα πρέπει να ψάχνουν χρησιμοποιώντας την ιδιότητα ValueMember. δείγμα κώδικα είναι κάτω. Εκτιμήθηκε αν κάποιος μπορεί να επιβεβαιώσει αυτό είναι ένα bug, ή κάτι που έχω κάνει λάθος.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace ComboBoxTest
{
   public partial class Form1 : Form
   {
      public Form1()
      {
         InitializeComponent();
      }

      private void Form1_Load(object sender, EventArgs e)
      {
         if (DesignMode)
            return;

         BindingList<CBItem> items = new BindingList<CBItem>();
         items.Add(new CBItem("A", @"C:\A"));
         items.Add(new CBItem("B", @"C:\A\B"));
         items.Add(new CBItem("A", @"C:\A\B\A"));

         comboBox.DisplayMember = "DisplayValue";
         comboBox.ValueMember = "RealValue";
         comboBox.DataSource = items;

         comboBox.SelectedValue = @"C:\A\B\A";
      }
   }

   class CBItem
   {
      public CBItem(string displayValue, string realValue)
      {
         _displayValue = displayValue;
         _realValue = realValue;
      }

      private readonly string _displayValue, _realValue;

      public string DisplayValue { get { return _displayValue; } }
      public string RealValue { get { return _realValue; } }
   }
}
Απαντήθηκε 28/05/2009 στις 21:38
πηγή χρήστη

ψήφοι
0

Ένα παρόμοιο πρόβλημα παρουσιάζεται χωρίς να έχει πανομοιότυπα στοιχεία αν εισάγετε ελεύθερο κείμενο, το οποίο δεν ταιριάζει ακριβώς, αλλά τους πρώτους χαρακτήρες. Εάν ο χρήστης δεν ανοίξει το αναπτυσσόμενο καμία εκ νέου συγχρονισμό συμβεί και ο επιλεγμένος δείκτης είναι -1, όπως αναμενόταν. (Δεν επιλέγοντας ένα από τα στοιχεία είναι τι σκοπεύει να κάνει ο χρήστης) Τώρα ο χρήστης κλείνει το παράθυρο διαλόγου και να το ανοίξετε ξανά. Εσείς, ως προγραμματιστής, την αποκατάσταση της combobox με το κείμενο που έδωσε ο χρήστης και το κείμενο συμπληρώνεται αυτόματα με το στοιχείο μερική ταύτιση χωρίς να ρίξει την εκδήλωση. Εάν ο χρήστης κλείνει το παράθυρο διαλόγου το κείμενο έχει αλλάξει χωρίς προειδοποίηση. Αυτό το ζήτημα δεν παρουσιάζεται αν το κείμενο δεν ταιριάζει με κανένα στοιχείο.

Απαντήθηκε 28/06/2009 στις 11:15
πηγή χρήστη

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more