Domanda

Sto avendo un problema con un ComboBox che è destinato a un ObservableCollection e mi chiedevo se qualcuno può puntare a quello che mi manca.

Ho un ComboBox che è legato ad un semplice ObservableCollection<string>. Anche io lego il SelectedIndex in un OneWay vincolante per alcune proprietà.

Nella mia domanda ho arrivare a un punto in cui voglio chiarire la raccolta e il ripopolamento con diversi dati e l'impostazione del SelectedIndex ad un nuovo valore. per qualche motivo vincolante il SelectedIndex non funziona.

Sto attaccando un po 'Repro del problema:

public partial class Window1 : Window, INotifyPropertyChanged
{
    private int j;
    public event PropertyChangedEventHandler PropertyChanged;

    public Window1()
    {
        InitializeComponent();
        DataContext = this;
        Tables = new ObservableCollection<string>();
    }

    public ObservableCollection<string> Tables { get; set; }

    private int _TheIndex;
    public int TheIndex
    {
        get { return _TheIndex; }
        set
        {
            _TheIndex = value;
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("TheIndex"));
            }
        }
    }

    private void aaaa(object sender, RoutedEventArgs e)
    {
        j = (j + 1)%10;
        Tables.Clear();
        for(int i = 0; i < 10 ; i++)
        {
            Tables.Add(i.ToString());
        }
        TheIndex = j;
    }
}

il codice XAML è:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    <Grid>
        <StackPanel>
            <ComboBox x:Name="TablesCombobox"
                      ItemsSource="{Binding Tables}"
                      SelectedIndex="{Binding TheIndex, Mode=OneWay}"/>
            <Button Content="asdasd" Click="aaaa"/>
        </StackPanel>
    </Grid>
</Window>
È stato utile?

Soluzione

Il problema è interamente causato dalla linea Tables.Clear() nel metodo aaaa(). Poiché Tables è una collezione osservabile, cancellando tutti i contenuti della raccolta provoca WPF per aggiornare la visualizzazione con un nuovo elenco vuoto. Poi si cerca di selezionare la voce utilizzando SelectedIndex attualmente attivo, che non esiste (perché l'elenco è ora vuota). Di conseguenza, il motore di associazione è lasciato con un valore che non può essere applicato, e decide di disattivare e staccare la logica vincolante:

System.Windows.Data Warning: Got PropertyChanged event from Window1 for TheIndex
System.Windows.Data Warning: GetValue at level 0 from Window1 using DependencyProperty(TheIndex): '1'
System.Windows.Data Warning: TransferValue - got raw value '1'
System.Windows.Data Warning: TransferValue - using final value '1'
System.Windows.Data Warning: Deactivate
System.Windows.Data Warning: Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: Detach

Con il tempo si arriva al 'TheIndex = j;' linea, il legame non è più attivo e non vedere il cambiamento TheIndex, il che significa che indice desiderato non è selezionato.

Ci sono un paio di soluzioni per risolvere questo:

  1. Non soffiare via l'intera collezione ogni volta. senza cancellare la raccolta, la logica vincolante dei dati ha sempre un indice di scegliere, il che significa che non si stacca.
  2. Usa un TwoWay vincolante Questo funziona perché ora il ComboBox partecipa vincolante.; si Tables chiaro, i tentativi di impostare vincolanti, ma non riesce a trovare l'indice in modo il reset ComboBox alla posizione speciale 'alcun indice' di -1, che poi scrive di nuovo al TheIndex (il doppio senso parte), che è una valida valore in modo vincolante la logica non si stacca.
  3. Seleziona alcun indice (-1) prima di cancellare la collezione. Se non è selezionato alcun indice (-1) quando Tables scompaia e quindi ComboBox non cerca di applicare SelectedItem, che significa che non 'vede' la raccolta svuotato e ri-riempita, e pertanto, non si stacchi.

    private void aaaa(object sender, RoutedEventArgs e)
    {
        TheIndex = -1;
        j = (j + 1)%10;
        Tables.Clear();
        for (int i = 0; i < 10; i++)
        {
            Tables.Add(i.ToString());
        }
        TheIndex = j;
    }
    

Per le prestazioni, architettonico, e le ragioni di chiarezza, mi raccomando l'opzione 1, anche se mi rendo conto che lo scenario reale può essere più complesse e richiedono qualcosa lungo le linee di 3.


Nota a margine:

Individuazione il motivo dietro questioni vincolanti come questo è ragionevolmente facile quando si utilizzano le tracce di legame come quello postato sopra. accenderli per un singolo legame dichiarando lo spazio dei nomi System.Diagnostics e aggiungendo PresentationTraceSources.TraceLevel=Highto il legame che sta causando problemi:

<Window xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase" />
...
<TextBlock Text="{Binding Path=x, diag:PresentationTraceSources.TraceLevel=High}" />

Altri modi di debug attacchi WPF sono qui .

Altri suggerimenti

So che questa è una vecchia questione, ma ho appena sperimentato questo problema io stesso modo hanno scritto un metodo di supporto basata su l'opzione 1 della risposta @Nicholas Armstrong s' e ho pensato condividere, spero che qualcuno lo troverà utile:

public void refreshDropdownOptions(ObservableCollection<object> OldOptions, ObservableCollection<object> NewOptions)
{
    MainWindow application = Application.Current.MainWindow as MainWindow;

    int highestCount = 0;

    if(OldOptions.Count() > NewOptions.Count())
    {
        highestCount = OldOptions.Count();
    }
    else
    {
        highestCount = NewOptions.Count();
    }

    for (int i = 0; i < highestCount; i++)
    {   
        if(i < OldOptions.Count() && i < NewOptions.Count())
        {// If we have not exceeded the count of either list, copy the new value over the old
            application.Dispatcher.Invoke((Action)(() => OldOptions[i] = NewOptions[i]));                   
        }
        else if (i < OldOptions.Count() && i >= NewOptions.Count())
        {// If we have no more new options remove the old option
            application.Dispatcher.Invoke((Action)(() => OldOptions.RemoveAt(i)));
            highestCount = OldOptions.Count();
            i--;
        }
        else if (i >= OldOptions.Count() && i < NewOptions.Count())
        {// if we have no more old options to replace, add the new option to the end of the collection
            application.Dispatcher.Invoke((Action)(() => OldOptions.Add(NewOptions[i])));
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top