2011-12-30 16 views
10

Próbuję powiązać 2 różne formanty WPF z tą samą właściwością w ViewModel, CheckBox.IsChecked i Expander.IsExpanded. Zachowanie, które chcę osiągnąć, polega na tym, że CheckBox wpływa na ViewModel (a zatem i na Expander), ale nie w inny sposób związany. Coś jak: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 

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

i Kod:

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

Teraz mój problem jest, jak tylko kliknąć na Expander aby rozwinąć/zwinąć to, Binding wydaje się przestać działać. Czy ktoś może mi wyjaśnić, dlaczego tak się dzieje i jak mam to osiągnąć? Z góry dziękuję!

Odpowiedz

10

Nowe odpowiedzi

Odkryte można to zrobić poprzez ustawienie swojej UpdateSourceTrigger do Explicit na Expander. Zachowuje to powiązanie jako Dwukierunkowe, ale nigdy nie aktualizuje źródła, ponieważ mówisz mu, żeby nie aktualizował źródła, chyba że wyraźnie to powiesz.

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

Pozostawiając mój stary odpowiedź poniżej tak komentarzach sensu, a ponieważ nadal czuję nie ma problemu z widokiem specyficzny kod dzieje w opóźnieniem kodu z widokiem :)


Stara odpowiedź

Osobiście, ponieważ jest to kod specyficzny dla widoku, nie widzę problemu z użyciem zdarzenia kliknięcia CheckBox, aby ustawić wartość Expander na wartość IsExpanded.

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

Można zrobić to nawet bardziej ogólne usuwając nazwiska i nawigowania Visual Drzewo znaleźć Expander związanego z wyboru. Oto przykład za pomocą niektórych 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

Tworzę widok z co najmniej 6 ekspanderów z Checkboxes na ich nagłówkach. Jest za dużo kodu i wolę nie używać żadnego kodu, ponieważ zmniejsza on elastyczność widoku. –

+0

@HighCore Jeśli masz wiele modułów Expanders/CheckBox, prawdopodobnie masz dla nich domyślny styl i możesz ustawić zdarzenie Click jako EventSetter w stylu. Chciałbym użyć czegoś do nawigacji po drzewie wizualnym, aby znaleźć Expander związany z tym CheckBoxem i nie musiałbyś w ogóle używać nazwanych wartości. – Rachel

+0

@HighCore Zobacz moją zaktualizowaną odpowiedź. Działa bez kodu źródłowego – Rachel

1

Jeśli chcesz pole wyboru, aby wpłynąć ekspandera (ale nie odwrotnie), a następnie związać ekspandera normalnie i używać OneWayToSource pole wyboru:

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

Korzystanie OneWayToSource pole wyboru pozwoli go:

  • zmodyfikować właściwość podstawową (a tym samym wpłynąć na ekspander, który jest również powiązany z tą właściwością)
  • nie wpływać na inne komponenty wprowadzające zmiany t O nieruchomość stanowiącą
+1

nie mogę zrób to, ponieważ pole wyboru jest tutaj podstawową reprezentacją danych.Powinien zawsze odzwierciedlać wartość w ViewModel. –

+0

@HighCore Miałem to dać +1, dopóki nie pomyślałem o tym, więc pomyślałem, że poczekam. Ładne rozwiązanie, jeśli to nie jest potrzebne. – Rachel

+1

Według MSDN domyślnym trybem dla pól wyboru jest zazwyczaj TwoWay, ale czy próbowałeś go wyraźnie napisać? "{Binding IsChecked, Mode = TwoWay}" –

1

Jeśli chcesz, aby uniknąć jakiegokolwiek kodu z opóźnieniem, można dodać stopień separacji między Expander i CheckBox państw w 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

Tak, pomyślałem o tym w ostateczności. –