你能将GroupBox的可见性与其子元素的可见性绑定吗?

5

我有一个问题,试图让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>

我看了这些问题,它们让我觉得这不可能实现:控件模板中的绑定StackPanel可见性Border可见性

如果这些问题已经有答案,请见谅。感谢您提供任何回答或评论。

2个回答

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>

但是如果我想再创建一个GroupBox或添加另一个项目,那么我不是每次都要添加条件吗?这将与MultiBinding存在相同的问题。 - davidcorne

1
有两种方法,均带有附属行为: 第一种是在父级GroupBox上设置一个附加属性,在OnPropertyChanged回调中循环遍历所有子项,并添加一个绑定到MultiBinding,然后将其连接到GroupBox的Visibility属性。这种方法的问题在于,您将不得不指定要包括在MultiBinding中的子项的类型(因为您需要找到它们以将它们添加到决定父项状态的组中)-如果您想捕获您喜欢的所有内容,则需要使用多个泛型类型调用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>

将附加属性放在子元素上,并通过 OnPropertyChanged 沿着树向上查找父 GroupBox,可能会更好,但是你会遇到不知道有多少元素的麻烦。这只是绑定的限制。至少使用 GroupBox 附加属性,您可以构建所需的绑定。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接