2013-04-09 3 views
5

Mam problem podczas próby zamknięcia GroupBox. Chcę GroupBox, który zawali się, jeśli wszystkie jego dzieci zostaną zwinięte.Czy można powiązać widoczność GroupBox z widocznością jego dzieci?

Udało mi się to osiągnąć za pomocą multibinding do właściwości, jak pokazano poniżej.

<StackPanel> 
    <GroupBox> 
     <GroupBox.Visibility> 
     <MultiBinding 
      Converter="{StaticResource multiBoolOrToVis}" 
      ConverterParameter="{x:Static Visibility.Collapsed}" 
     > 
      <Binding Path="a_visible"/> 
      <Binding Path="b_visible"/> 
     </MultiBinding> 
     </GroupBox.Visibility> 
     <GroupBox.Header> 
     <Label Content="GroupBox"/> 
     </GroupBox.Header> 
     <StackPanel> 
     <Label 
      Content="A" 
      Visibility="{Binding Path=a_visible, Converter={StaticResource boolToVis}}" 
     /> 
     <Label 
      Content="B" 
      Visibility="{Binding Path=b_visible, Converter={StaticResource boolToVis}}" 
     /> 
     </StackPanel> 
    </GroupBox> 
    <Grid> 
     <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="*"/> 
     <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 
     <CheckBox 
     Content="A Visible" 
     Grid.Column="0" 
     Grid.Row="1" 
     IsChecked="{Binding Path=a_visible, Mode=TwoWay}" 
     /> 
     <CheckBox 
     Content="B Visible" 
     Grid.Column="1" 
     Grid.Row="1" 
     IsChecked="{Binding Path=b_visible, Mode=TwoWay}" 
     /> 
    </Grid> 
    </StackPanel> 

Problem polega na tym, że chcemy to zrobić kilka razy i nie martwimy się o pozostawienie wiązania. Moje pytanie brzmi, czy można to zrobić w sposób ogólny, najlepiej w stylu. Kolejnym wymaganiem jest to, że musi być w kodzie xaml, a nie w kodzie.

Więc moją idealną odpowiedzią byłby styl, więc mogłem wykonać następujące czynności w moim xaml.

<StackPanel> 
    <GroupBox Style="ChildrenVisibilityStyle"> 
     <GroupBox.Header> 
     <Label Content="GroupBox"/> 
     </GroupBox.Header> 
     <StackPanel> 
     <Label 
      Content="A" 
      Visibility="{Binding Path=a_visible, Converter={StaticResource boolToVis}}" 
     /> 
     <Label 
      Content="B" 
      Visibility="{Binding Path=b_visible, Converter={StaticResource boolToVis}}" 
     /> 
     </StackPanel> 
    </GroupBox> 
    <Grid> 
     <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="*"/> 
     <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 
     <CheckBox 
     Content="A Visible" 
     Grid.Column="0" 
     Grid.Row="1" 
     IsChecked="{Binding Path=a_visible, Mode=TwoWay}" 
     /> 
     <CheckBox 
     Content="B Visible" 
     Grid.Column="1" 
     Grid.Row="1" 
     IsChecked="{Binding Path=b_visible, Mode=TwoWay}" 
     /> 
    </Grid> 
    </StackPanel> 

Przyjrzałem się tym pytaniom i doprowadziły mnie do przekonania, że ​​nie jest to możliwe; binding in controltemplate, stackpanel visibility, border visibility.

Przepraszamy, jeśli wcześniej udzielono odpowiedzi. Z góry dziękuję za wszelkie odpowiedzi/komentarze.

Odpowiedz

4

Można użyć MultiDataTrigger aby zwinąć GroupBox gdy dzieci są zwinięte

Oto przykład roboczych:

<StackPanel> 
    <GroupBox> 
     <GroupBox.Header> 
      <Label Content="GroupBox"/> 
     </GroupBox.Header> 
     <StackPanel> 
      <Label x:Name="lbl_a" Content="A" Visibility="{Binding IsChecked, ElementName=chk_a, Converter={StaticResource boolToVis}}" /> 
      <Label x:Name="lbl_b" Content="B" Visibility="{Binding IsChecked, ElementName=chk_b, Converter={StaticResource boolToVis}}" /> 
     </StackPanel> 
     <GroupBox.Style> 
      <Style TargetType="GroupBox"> 
       <Style.Triggers> 
        <MultiDataTrigger> 
         <MultiDataTrigger.Conditions> 
          <Condition Binding="{Binding Visibility, ElementName=lbl_a}" Value="Collapsed" /> 
          <Condition Binding="{Binding Visibility, ElementName=lbl_b}" Value="Collapsed" /> 
         </MultiDataTrigger.Conditions> 
         <MultiDataTrigger.Setters> 
          <Setter Property="GroupBox.Visibility" Value="Collapsed" /> 
         </MultiDataTrigger.Setters> 
        </MultiDataTrigger> 
       </Style.Triggers> 
      </Style> 
     </GroupBox.Style> 
    </GroupBox> 

    <CheckBox x:Name="chk_a" Content="A Visible" Grid.Column="0" Grid.Row="1" /> 
    <CheckBox x:Name="chk_b" Content="B Visible" Grid.Column="1" Grid.Row="1" /> 

</StackPanel> 
+0

Ale jeśli chcę utworzyć kolejny blok grupowy lub dodać inny element, czy nie muszę każdorazowo dodawać warunki? To byłby ten sam problem, co w przypadku MultiBinding – davidcorne

0

Istnieją dwa podejścia, zarówno z dołączonymi zachowań: Pierwszym z nich jest ustaw dołączoną właściwość na nadrzędnym bloku GroupBox iw pętli zwrotnej OnPropertyChanged na wszystkich elementach podrzędnych i dodaj powiązanie z multibinding, a następnie dołącz je do właściwości widoczności GroupBox. Problem z tym podejściem polega na tym, że będziesz musiał określić typy dzieci, które chcesz uwzględnić w multibinding (ponieważ musisz je znaleźć, aby dodać je do grupy, która dyktuje stan rodzica) - FindVisualChildren będą musiały być wywołana z wielu typów generycznych, jeśli chcesz, aby uchwycić wszystko, co chcesz go łatwo zrobić chociaż ...:

public sealed class GroupBoxCloseBehavior : DependencyObject 
{ 
    public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(GroupBoxCloseBehavior), new PropertyMetadata(false, OnIsEnabledChanged)); 

    public static bool GetIsEnabled(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(IsEnabledProperty); 
    } 

    public static void SetIsEnabled(DependencyObject obj, bool value) 
    { 
     obj.SetValue(IsEnabledProperty, value); 
    } 

    private static void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
    { 
     GroupBox parent = obj as GroupBox; 
     if (parent == null) 
     { 
      return;//Do nothing, or throw an exception depending on your preference 
     } 

     if (parent.IsLoaded) 
     { 

      MultiBinding mb = new MultiBinding(); 
      mb.Converter = new MultiVisibilityToVisibilityConverter(); 
      if ((bool)e.NewValue) 
      { 
       foreach (CheckBox child in FindVisualChildren<CheckBox>(parent)) 
       { 
        mb.Bindings.Add(new Binding("Visibility") { Mode = BindingMode.OneWay, Source = child }); 
       } 
       BindingOperations.SetBinding(parent, UIElement.VisibilityProperty, mb); 
      } 
      else 
      { 
       BindingOperations.ClearBinding(parent, UIElement.VisibilityProperty); 
      } 
     } 
     else 
     { 
      parent.Loaded += (sender, eventArgs) => 
      { 
       MultiBinding mb = new MultiBinding(); 
       mb.Converter = new MultiVisibilityToVisibilityConverter(); 
       if ((bool)e.NewValue) 
       { 
        foreach (CheckBox child in FindVisualChildren<CheckBox>(parent)) 
        { 
         mb.Bindings.Add(new Binding("Visibility") { Mode = BindingMode.OneWay, Source = child }); 
        } 
        BindingOperations.SetBinding(parent, UIElement.VisibilityProperty, mb); 
       } 
       else 
       { 
        BindingOperations.ClearBinding(parent, UIElement.VisibilityProperty); 
       } 
      }; 
     } 
    } 

    private sealed class MultiVisibilityToVisibilityConverter : IMultiValueConverter 
    { 
     public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      return values.OfType<Visibility>().Any(vis => vis != Visibility.Collapsed) ? Visibility.Visible : Visibility.Collapsed; 
     } 

     public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 
     { 
      throw new NotSupportedException(); 
     } 
    } 

    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject 
    { 
     if (depObj != null) 
     { 
      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) 
      { 
       DependencyObject child = VisualTreeHelper.GetChild(depObj, i); 
       if (child != null && child is T) 
       { 
        yield return (T)child; 
       } 

       foreach (T childOfChild in FindVisualChildren<T>(child)) 
       { 
        yield return childOfChild; 
       } 
      } 
     } 
    } 
} 

<StackPanel> 
    <GroupBox Header="GroupBox" cl2:GroupBoxCloseBehavior.IsEnabled="True"> 
     <StackPanel> 
      <CheckBox x:Name="CheckOne" Content="CheckBox One"/> 
      <CheckBox x:Name="CheckTwo" Content="CheckBox Two"/> 
     </StackPanel> 
    </GroupBox> 
    <StackPanel> 
     <Button Content="Hide One" Click="Button_Click_1"/> 
     <Button Content="Hide Two" Click="Button_Click_2"/> 
    </StackPanel> 
</StackPanel> 

Robi to w drugą stronę, kładąc załączony właściwość na elementach potomnych i OnPropertyChanged podchodząc do drzewa szukając nadrzędnego GroupBox może być lepszy, ale masz kłopot z niewiedzą, ile elementów istnieje. To tylko ograniczenie wiązania. Przynajmniej z właściwością dołączoną do GroupBox możesz skonstruować wiązanie, którego potrzebujesz.