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>

我看过这些问题,它们让我认为这是不可能的;controltemplate中的绑定stackpanel 可见性边框可见性

对不起,如果这已经回答过。提前感谢您的任何答案/评论。

4

2 回答 2

4

当孩子们被折叠时,你可以使用 aMultiDataTrigger来折叠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>
于 2013-04-09T11:39:12.220 回答
1

有两种方法,都具有附加行为:第一种是在父 GroupBox 和 OnPropertyChanged 回调循环中对所有子项设置附加属性,并将绑定添加到多重绑定,然后将其挂接到 GroupBox Visibility 属性。这种方法的问题是您必须指定要包含在多重绑定中的子项的类型(因为您需要找到它们才能将它们添加到指示父项状态的组中) - 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 附加属性,您可以构建所需的绑定。

于 2013-04-09T15:00:00.943 回答