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>

これに関する問題は、これを複数回実行できるようにする必要があり、バインディングを残すことを心配しないことです。だから私の質問は、これを一般的に、できればスタイルで行う方法があるということです。もう 1 つの要件は、コード ビハインドではなく 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 の可視性border の可視性

これが以前に回答されている場合は申し訳ありません。回答/コメントをお寄せいただきありがとうございます。

4

2 に答える 2

1

2 つの方法があり、どちらもビヘイビアーが関連付けられています。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 に答える