你能否将GroupBox的可见性绑定到它的孩子的可见性?

时间:2013-04-09 11:08:36

标签: c# wpf xaml data-binding

我有一个问题试图让GroupBox崩溃。我想要一个GroupBox,如果它的所有子节点都被折叠,它将崩溃。

我已经设法使用对属性的多重绑定来实现这一点,如下所示。

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

这个问题是我们希望能够多次执行此操作,而不必担心不使用绑定。所以我的问题是有什么方法可以做到这一点,最好是一种风格。另一个要求是它必须是xaml而不是代码。

所以我的理想答案是风格,所以我可以在我的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>

我看过这些问题,他们让我觉得这是不可能的; binding in controltemplatestackpanel visibilityborder visibility

很抱歉,如果之前已经回答过这个问题。提前感谢您的任何答案/评论。

2 个答案:

答案 0 :(得分:4)

当孩子们崩溃时,您可以使用MultiDataTrigger折叠GroupBox

这是一个有效的例子:

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

答案 1 :(得分:0)

有两种方法,都附有行为: 第一种是在父GroupBox和OnPropertyChanged回调循环上为所有子项设置附加属性,并为多绑定添加绑定,然后将其绑定到GroupBox Visibility属性。这种方法的问题在于您必须指定要包含在多重绑定中的子(ren)的类型(因为您需要找到它们以将它们添加到指示父级状态的组中) - FindVisualChildren需要使用多种泛型类型进行调用,如果你想捕获你想要的所有内容......可以轻松完成:

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>

以另一种方式执行此操作,在子元素上放置附加属性,并且在树中查找父GroupBox的OnPropertyChanged可能会更好,但您不必知道有多少元素。这只是绑定的限制。至少使用GroupBox附加属性,您可以构建所需的绑定。