2011-12-30 14 views
10

Estoy tratando de vincular 2 controles WPF diferentes a la misma propiedad en el modelo de vista, un CheckBox.IsChecked y un Expander.IsExpanded. El comportamiento que quiero lograr es hacer que CheckBox afecte al ViewModel (y por lo tanto también al Expansor), pero no a la otra manera. Algo así como:WPF One Way Enlace roto

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 

Aquí está el 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> 

y el Código:

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 { }; 
    } 
} 

Ahora mi problema es que tan pronto como haga clic en el expansor para expandir/contraer esto, el Enlace parece dejar de funcionar. ¿Alguien puede explicarme por qué sucede esto y cómo lo logro? ¡Gracias por adelantado!

Respuesta

10

Nueva respuesta

descubrió que podía hacer esto ajustando su UpdateSourceTrigger a Explicit en su Expander. Esto mantiene el enlace como bidireccional, pero nunca actualiza la fuente ya que le está diciendo que no actualice la fuente a menos que se lo indique explícitamente.

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

Dejando mi viejo respuesta a continuación por lo que los comentarios tienen sentido, y porque todavía me siento no hay ningún problema con el código específico de la vista va en el código subyacente de un punto de vista :)


respuesta Antiguo

lo personal ya que este es el código específicas para la vista, no veo ningún problema con el uso de un evento CheckBox posición para ajustar el valor del expansor IsExpanded.

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

Se podría hacer esto aún más genérica mediante la eliminación de los nombres y navegar por el árbol visual para encontrar el expansor asociado a la casilla de verificación. He aquí un ejemplo el uso de algunos 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

Im creando una vista con al menos 6 expansores con casillas de verificación en sus encabezados. Es demasiado código, y prefiero no usar ningún código porque reduce la flexibilidad de la vista. –

+0

@HighCore Si tiene múltiples Expansores/Casillas de verificación, es probable que tenga un estilo predeterminado para ellos y podría establecer el evento Click como un EventSetter en el estilo. Utilizaría algo para navegar por el árbol visual para encontrar el expansor asociado con ese CheckBox y no necesitaría usar ningún valor con nombre. – Rachel

+0

@HighCore Ver mi respuesta actualizada. Funciona sin código subyacente – Rachel

1

Si desea que la casilla de verificación para afectar el expansor (pero no viceversa) a continuación, se unen el expansor normalmente y usar OneWayToSource en la casilla:

<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> 

Usando OneWayToSource en la casilla le permitirá:

  • modificar la propiedad subyacente (y por lo tanto afectan el expansor, que también está unido a que la propiedad)
  • no ser afectado por otros componentes que hacen cambios t o la propiedad subyacente
+1

no puedo haz esto porque la casilla de verificación es la representación de datos primaria aquí.Siempre debe reflejar el valor en ViewModel. –

+0

@HighCore Estaba a punto de +1 hasta que pensé en eso, así que pensé que esperaría. Buena solución, si eso no es necesario. – Rachel

+1

Según MSDN, el modo predeterminado para las casillas de verificación generalmente es TwoWay, pero ¿ha intentado escribirlo explícitamente? "{Binding IsChecked, Mode = TwoWay}" –

1

Si desea evitar cualquier código subyacente, se puede añadir un grado de separación entre los Expander y CheckBox estados en su modelo de vista:

  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í, pensé en esto como último recurso. –