2015-06-24 23 views
5

Ho un problema durante il tentativo di associare 2 o più Combobox SelectedValue a una proprietà, ovvero null. Solo 1 delle caselle combinate associate a questa proprietà mostrerà il valore reale.WPF ComboBox SelectedValue binding con valore null viene visualizzato vuoto

Di seguito è il mio Xaml in cui utilizzo DataTemplate per selezionare un Combobox per la presentazione di viewModel.

Xaml:

<Window x:Class="Test.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:Test" 
    Title="MainWindow" Height="350" Width="525"> 
<Window.Resources> 
    <DataTemplate DataType="{x:Type local:PropertyValueViewModel}"> 
     <ComboBox SelectedValue="{Binding Value}" ItemsSource="{Binding SelectableValues}" DisplayMemberPath="Description" SelectedValuePath="Value"/> 
    </DataTemplate> 
</Window.Resources> 
<StackPanel> 
    <Label Content="These uses template:"></Label> 
    <ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter> 
    <ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter> 
    <ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter> 
</StackPanel> 

E il codice dietro:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     ValueSelector = new PropertyValueViewModel() 
     { 
      SelectableValues = new List<SelectableValue>() 
      { 
       new SelectableValue("NULL", null), 
       new SelectableValue("1", 1) 
      }, 
      Value = null 
     }; 

     DataContext = this; 
    } 

    public static readonly DependencyProperty ValueSelectorProperty = DependencyProperty.Register(
     "ValueSelector", typeof(PropertyValueViewModel), typeof(MainWindow), new PropertyMetadata(default(PropertyValueViewModel))); 

    public PropertyValueViewModel ValueSelector 
    { 
     get { return (PropertyValueViewModel)GetValue(ValueSelectorProperty); } 
     set { SetValue(ValueSelectorProperty, value); } 
    } 
} 

/// <summary> 
/// My viewModel 
/// </summary> 
public class PropertyValueViewModel 
{ 
    public object Value { get; set; } 
    public object SelectableValues { get; set; } 
} 

/// <summary> 
/// The items in the combobox 
/// </summary> 
public class SelectableValue 
{ 
    public SelectableValue(string header, object value) 
    { 
     Value = value; 
     Description = header; 
    } 

    public object Value { get; set; } 

    public string Description { get; set; } 
} 

Ora mi chiedo perché solo 1 di loro possono mostrare il valore NULL all'avvio? Posso cambiare il valore in ognuno di essi e si sincronizzeranno tutti con il valore nella proprietà - se seleziono 1 e poi torno a NULL, tutti mostreranno NULL. Sembra che solo il valore iniziale non sia mostrato correttamente.

Se evito di utilizzare DataTemplate, funziona anche il binding. Qualcuno sa perché il DAtaTemplate si comporta in questo modo?

risposta

3

Interessante problema.

Fondamentalmente, questo sembra essere causato dalla scelta di utilizzare null come uno dei valori selezionabili. null, ovviamente, ha un significato speciale, per C#, .NET e WPF. Il problema riguarda anche l'ordine in cui è stata eseguita l'inizializzazione dell'elemento ComboBox. La proprietà SelectedValuePath è inizializzata dopo la proprietà SelectedValue.

Questo significa che come programma si sta avviando e vengono creati gli ComboBox elementi, quando null viene assegnato alla proprietà SelectedValue attraverso il suo legame, il ComboBox non hanno ancora sufficienti informazioni per gestire tale valore come selezione della voce legittima. Invece, lo interpreta come nessuna selezione.

Perché lo ultimoComboBox viene ancora inizializzato nel modo desiderato? Non sono proprio sicuro, ma non ho indagato molto a riguardo. Potrei speculare, ma le probabilità di indovinare correttamente sembrano basse, quindi non mi preoccuperò. Dal momento che è l'anomalia e non è necessariamente in linea con il comportamento previsto (basato su sopra, anche se il comportamento è il comportamento desiderato), lo invierò a uno dei tanti "capricci" di WPF. :)

ho trovato diversi work-around per il rilascio:

  • non utilizzare null come un valore selezionabile. Se ogni valore selezionabile non è nullo, viene mantenuto il valore non nullo utilizzato per inizializzare la proprietà SelectedValue di ogni elemento e quando viene inizializzato lo SelectedValuePath, la selezione corrente per ComboBox è impostata correttamente.
  • Non utilizzare SelectedValuePath. Invece, legare semplicemente a SelectedItem e inizializzare la proprietà Value con l'istanza di classe SelectableValue desiderata (ad esempio, la prima nell'elenco).
  • Nell'evento Loaded di ComboBox, aggiornare la destinazione della rilegatura.

I primi due sono partenze significative dal design attuale. Personalmente, se possibile, vorrei andare con uno o l'altro. Mi sembra che ci sia un evidente pericolo nell'uso di null come valore selezionabile in un ComboBox, e questa potrebbe non essere l'unica stranezza in cui ci si imbatte. A lungo termine, il mantenimento di questa parte del codice potrebbe costare molto di più se si continua a utilizzare null.

Detto questo, la terza opzione funziona e, se si è fortunati, l'unico vero pericolo nell'utilizzo di null è l'inizializzazione. La mia proposta di work-around per quella opzione sarebbe simile a questa:

XAML:

<DataTemplate DataType="{x:Type local:PropertyValueViewModel}"> 
    <ComboBox SelectedValue="{Binding Value}" 
       ItemsSource="{Binding SelectableValues}" 
       DisplayMemberPath="Description" 
       SelectedValuePath="Value" 
       Loaded="comboBox_Loaded"/> 
</DataTemplate> 

C#:

private void comboBox_Loaded(object sender, RoutedEventArgs e) 
{ 
    ComboBox comboBox = (ComboBox)e.OriginalSource; 

    BindingOperations.GetBindingExpression(comboBox, ComboBox.SelectedValueProperty) 
        .UpdateTarget(); 
} 

Questo costringe WPF per aggiornare il target (cioè il SelectedValue proprietà del controllo). Poiché a questo punto è stato impostato SelectedValuePath, assegnando null alla proprietà questa volta si aggiorna correttamente l'elemento selezionato per ComboBox.


A proposito, ti consiglio vivamente di non chiarire i nomi delle proprietà Value nei tuoi modelli. Avere due diverse proprietà Value utilizzate per i collegamenti in un singolo elemento XAML è molto confuso. Vorrei utilizzare, ad esempio, SelectedValue e ItemValue, rispettivamente per la classe PropertyValueViewModel e la classe SelectableValue.

+0

Grazie. Segnalo come soluzione, poiché ha un paio di modi per risolvere il problema e una buona spiegazione del problema. – Patrick