2011-12-30 28 views
10

Sto tentando di associare 2 diversi controlli WPF alla stessa proprietà nel ViewModel, a CheckBox.IsChecked e Expander.IsExpanded. Il comportamento che voglio ottenere è far sì che il CheckBox influenzi il ViewModel (e quindi anche l'Expander), ma non il contrario. Qualcosa di simile:WPF One Way Binding broken

Checkbox Checked -> ViewModel property set to frue -> Expander.Expand 
Checkbox Unchecked -> ViewModel property set to false -> Expander.Collapse 
Expander Expanded -> Nothing else affected 
Expander Collapsed -> Nothing else affected 

Ecco il XAML:

<Window x:Class="WpfApplication9.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Expander IsExpanded="{Binding IsChecked, Mode=OneWay}"> 
     <Expander.Header> 
      <CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked"/> 
     </Expander.Header> 
     <TextBlock Text="Expanded!"/> 
    </Expander> 
</Window> 

e del Codice:

using System.ComponentModel; 
using System.Windows; 

namespace WpfApplication9 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = new ViewModel(); 
     } 
    } 

    public class ViewModel: INotifyPropertyChanged 
    { 
     private bool _isChecked; 
     public bool IsChecked 
     { 
      get { return _isChecked; } 
      set 
      { 
       _isChecked = value; 
       NotifyPropertyChange("IsChecked"); 
      } 
     } 

     protected void NotifyPropertyChange(string PropertyName) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(PropertyName)); 
     } 

     public event PropertyChangedEventHandler PropertyChanged = delegate { }; 
    } 
} 

Ora il mio problema è, non appena clicco sul Expander per espandere/crollo esso, il legame sembra smettere di funzionare. Qualcuno può spiegarmi perché questo sta accadendo e come posso ottenere questo? Grazie in anticipo!

risposta

10

Nuova risposta

scoperto che si potrebbe fare questo impostando la UpdateSourceTrigger per Explicit sul Expander. Ciò mantiene l'associazione come Two-Way, ma non aggiorna mai la Source poiché stai dicendo di non aggiornare l'origine a meno che tu non lo dica esplicitamente.

<Expander IsExpanded="{Binding IsChecked, UpdateSourceTrigger=Explicit}"> 
    <Expander.Header> 
     <CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked"/> 
    </Expander.Header> 
    <TextBlock Text="Expanded!"/> 
</Expander> 

Lasciando il mio vecchio risposta qui sotto in modo che i commenti hanno senso, e perché mi sento ancora non v'è alcun problema con codice specifico per view andare in code-behind di una visione :)


Vecchio risposta

Personalmente dal momento che questo è il codice View-specifico, non vedo alcun problema con l'utilizzo di un evento CheckBox click per impostare il valore del Expander IsExpanded.

private void MyCheckBox_Click(object sender, RoutedEventArgs e) 
{ 
    MyExpander.IsExpanded = ((CheckBox)sender).IsChecked.GetValueOrDefault(); 
} 

Si potrebbe fare questo ancora più generico, eliminando i nomi e la navigazione ad albero visuale per trovare l'Expander associata alla CheckBox. Ecco un esempio utilizzando alcuni Visual Tree Helpers I built

private void CheckBox_Click(object sender, RoutedEventArgs e) 
{ 
    var chk = (CheckBox)sender; 
    var expander = VisualTreeHelpers.FindAncestor<Expander>(chk); 

    if (expander != null) 
     expander.IsExpanded = chk.IsChecked.GetValueOrDefault(); 
} 
+0

Sto creando una vista con almeno 6 espansori con caselle di controllo sulle intestazioni. È troppo codice e preferisco non usare alcun codice perché riduce la flessibilità della vista. –

+0

@HighCore Se hai più espansioni/checkbox, probabilmente hai uno stile predefinito per loro e potresti impostare l'evento Click come EventSetter nello stile. Vorrei usare qualcosa per navigare nell'albero visivo per trovare l'Expander associato a quel CheckBox e non avresti assolutamente bisogno di usare valori nominali. – Rachel

+0

@HighCore Vedere la mia risposta aggiornata. Funziona senza code-behind – Rachel

1

Se si desidera che la casella di controllo per influenzare l'espansore (ma non viceversa) quindi associare l'espansore normalmente e utilizzare OneWayToSource sulla casella di controllo:

<Window x:Class="WpfApplication9.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Expander IsExpanded="{Binding IsChecked}"> 
     <Expander.Header> 
     <CheckBox IsChecked="{Binding IsChecked, Mode=OneWayToSource}" Content="Is Checked"/> 
     </Expander.Header> 
     <TextBlock Text="Expanded!"/> 
    </Expander> 
</Window> 

Utilizzando OneWayToSource sulla casella di controllo le consentirà di:

  • modificare la struttura sottostante (e quindi influenzano l'espansore, che è anche legato a tale proprietà)
  • non essere influenzato da altri componenti che modifiche t o la proprietà sottostante
+1

non posso fai questo perché la casella di controllo è la rappresentazione dei dati primari qui.Dovrebbe sempre riflettere il valore nel ViewModel. –

+0

@HighCore Stavo per fare +1 su questo fino a quando non ci ho pensato, quindi ho pensato di aspettare. Bella soluzione se non è necessario. – Rachel

+1

Secondo MSDN, la modalità predefinita per le caselle di controllo è generalmente TwoWay, ma hai provato a scriverla in modo esplicito? "{Binding IsChecked, Mode = TwoWay}" –

1

Se vuoi per evitare code-behind, è possibile aggiungere un grado di separazione tra i Expander e CheckBox stati nel vostro ViewModel:

  private bool _isChecked; 
      public bool IsChecked 
      { 
       get { return _isChecked; } 
       set 
       { 
        _isChecked = value; 
        NotifyPropertyChange("IsChecked"); 
        IsExpanded = value; 
       } 
      } 

      private bool _isExpanded; 
      public bool IsExpanded 
      { 
       get { return _isExpanded; } 
       set 
       { 
        _isExpanded = value; 
        NotifyPropertyChange("IsExpanded"); 
       } 
      } 

    <Expander IsExpanded="{Binding IsExpanded}"> 
     <Expander.Header> 
      <CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked" x:Name="cb"/> 
     </Expander.Header> 
     <TextBlock Text="Expanded!"/> 
    </Expander> 
+0

Sì, ho pensato a questa come ultima risorsa. –