WPF:折叠 GridSplitter?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12483017/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 05:24:24  来源:igfitidea点击:

WPF: collapse GridSplitter?

wpfxamlgridsplitter

提问by Jeff Dege

I have a WPF page, with a Grid on it.

我有一个 WPF 页面,上面有一个网格。

There are three rows. Row 0 contains a GridView with Height="*". Row 1 contains a GridSplitter with Height="auto". Row 2 contains a details form with Height="2*".

有三排。第 0 行包含一个 GridView Height="*"。第 1 行包含一个 GridSplitter Height="auto"。第 2 行包含一个带有 的详细信息表单Height="2*"

Here's the thing - I have a button that is supposed to toggle the visibility of the details form. And that works just fine. Except that it just hides the form in Row 2, it doesn't expand the Grid in Row 0 to fill the space. What I want is for the button to toggle GridView in Row 0 to take up all the space, and then to toggle back to where things were.

事情就是这样 - 我有一个按钮,应该可以切换详细信息表单的可见性。这工作得很好。除了它只是隐藏第 2 行中的表单之外,它不会扩展第 0 行中的 Grid 以填充空间。我想要的是按钮在第 0 行中切换 GridView 以占用所有空间,然后切换回原来的位置。

Clearly playing around with the Visibility of the form inside the row won't accomplish what I want.

显然,在行内使用表单的可见性不会实现我想要的。

But what do I need to be playing around with?

但是我需要玩什么?

采纳答案by user7116

I had to introduce an Attached Dependency Property to handle this in my own application:

我不得不引入一个附加依赖属性来在我自己的应用程序中处理这个问题:

<Grid c:GridSplitterController.Watch="{Binding ElementName=GS_DetailsView}">
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />
        <RowDefinition Height="200" />
    </Grid.RowDefinitions>

    <SomeControl Grid.Row="0" />

    <GridSplitter x:Name="GS_DetailsView"
                  Height="4"
                  Grid.Row="1"
                  VerticalAlignment="Top"
                  HorizontalAlignment="Stretch"
                  ResizeBehavior="PreviousAndCurrent"
                  ResizeDirection="Rows"
                  Visibility="{Binding ShowDetails,
                                       Converter={StaticResource boolvis}}" />

    <OtherControl Grid.Row="1"
                  Margin="0,4,0,0"
                  Visibility="{Binding ShowDetails,
                                       Converter={StaticResource boolvis}}" />
</Grid>

First define a suitable attached property on a DependencyObject:

首先在 a 上定义一个合适的附加属性DependencyObject

public static GridSplitter GetWatch(DependencyObject obj)
{
    return (GridSplitter)obj.GetValue(WatchProperty);
}

public static void SetWatch(DependencyObject obj, GridSplitter value)
{
    obj.SetValue(WatchProperty, value);
}

public static readonly DependencyProperty WatchProperty =
    DependencyProperty.RegisterAttached(
        "Watch",
        typeof(GridSplitter),
        typeof(DependencyObject),
        new UIPropertyMetadata(null, OnWatchChanged));

Then listen for IsVisibleChanged:

然后听IsVisibleChanged

private static void OnWatchChanged(DependencyObject obj,
    DependencyPropertyChangedEventArgs e)
{
    if (obj == null) return;
    if (obj is Grid)
    {
        var grid = obj as Grid;
        var gs = e.NewValue as GridSplitter;
        if (gs != null)
        {
            gs.IsVisibleChanged += (_sender, _e) =>
                {
                    UpdateGrid(
                        grid,
                        (GridSplitter)_sender,
                        (bool)_e.NewValue,
                        (bool)_e.OldValue);
                };
        }
    }
}

Once you are watching for these changes, you need to save or restore the GridLengthvalues from the row or columns you are watching (for brevity I'm only including Rows):

观察这些更改后,您需要保存或恢复GridLength正在观察的行或列中的值(为简洁起见,我只包括行):

// Given: static Dictionary<DependencyObject, GridLength> oldValues;
private static void UpdateGrid(Grid grid, GridSplitter gridSplitter, bool newValue, bool oldValue)
{
    if (newValue)
    {
        // We're visible again
        switch (gridSplitter.ResizeDirection)
        {
        case GridResizeDirection.Columns:
            break;
        case GridResizeDirection.Rows:
            int ridx = (int)gridSplitter.GetValue(Grid.RowProperty);
            var prev = grid.RowDefinitions.ElementAt(GetPrevious(gridSplitter, ridx));
            var curr = grid.RowDefinitions.ElementAt(GetNext(gridSplitter, ridx));
            if (oldValues.ContainsKey(prev) && oldValues.ContainsKey(curr))
            {
                prev.Height = oldValues[prev];
                curr.Height = oldValues[curr];
            }

            break;
        }
    }
    else
    {
        // We're being hidden
        switch (gridSplitter.ResizeDirection)
        {
        case GridResizeDirection.Columns:
            break;
        case GridResizeDirection.Rows:
            int ridx = (int)gridSplitter.GetValue(Grid.RowProperty);
            var prev = grid.RowDefinitions.ElementAt(GetPrevious(gridSplitter, ridx));
            var curr = grid.RowDefinitions.ElementAt(GetNext(gridSplitter, ridx));
            switch (gridSplitter.ResizeBehavior)
            {
                // Naively assumes only one type of collapsing!
                case GridResizeBehavior.PreviousAndCurrent:
                    oldValues[prev] = prev.Height;
                    prev.Height = new GridLength(1.0, GridUnitType.Star);

                    oldValues[curr] = curr.Height;
                    curr.Height = new GridLength(0.0);
                    break;
            }
            break;
        }
    }
}

All that is left is a suitable implementation of GetPreviousand GetNext:

剩下的就是一个合适的GetPreviousand实现GetNext

private static int GetPrevious(GridSplitter gridSplitter, int index)
{
    switch (gridSplitter.ResizeBehavior)
    {
        case GridResizeBehavior.PreviousAndNext:
        case GridResizeBehavior.PreviousAndCurrent:
            return index - 1;
        case GridResizeBehavior.CurrentAndNext:
            return index;
        case GridResizeBehavior.BasedOnAlignment:
        default:
            throw new NotSupportedException();
    }
}

private static int GetNext(GridSplitter gridSplitter, int index)
{
    switch (gridSplitter.ResizeBehavior)
    {
        case GridResizeBehavior.PreviousAndCurrent:
            return index;
        case GridResizeBehavior.PreviousAndNext:
        case GridResizeBehavior.CurrentAndNext:
            return index + 1;
        case GridResizeBehavior.BasedOnAlignment:
        default:
            throw new NotSupportedException();
    }
}

回答by Simon Mourier

Let's suppose I have this XAML layout:

假设我有这个 XAML 布局:

  <Grid Name="MyGrid">
      <Grid.RowDefinitions>
          <RowDefinition />
          <RowDefinition Height="Auto" />
          <RowDefinition />
      </Grid.RowDefinitions>
      <MyControl1 ... Grid.Row="0" />
      <GridSplitter Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" ShowsPreview="True" Height="5" />
      <MyControl2 ... Grid.Row="2" />
  </Grid>

Then I can hide the second control (collapse the splitter down) with this code (equivalent of setting Height="0"in XAML):

然后我可以使用以下代码(相当于Height="0"XAML 中的设置)隐藏第二个控件(向下折叠拆分器):

  MyGrid.RowDefinitions[2].Height = new GridLength(0);

And uncollapse it with this code (equivalent of setting Height="1*"in XAML, which is the default for a RowDefinition):

并使用以下代码展开它(相当于Height="1*"XAML 中的设置,这是 RowDefinition 的默认设置):

  MyGrid.RowDefinitions[2].Height = new GridLength(1, GridUnitType.Star);

This is what the splitter does undercovers when the user moves it.

这就是分离器在用户移动它时所做的卧底。

回答by Stefan Zuefeldt

In case anyone wants a purely XAML solution, I was able to conjure a way to hide the splitter and relevant row using styles, setters, and triggers.

如果有人想要纯粹的 XAML 解决方案,我可以想出一种方法来使用样式、设置器和触发器来隐藏分隔符和相关行。

I used a static resource for my style, which was set to change the Heightand MaxHeightwhen a specific bound boolean was set.

我为我的风格使用了一个静态资源,它被设置为更改特定绑定布尔值的时间HeightMaxHeight时间。

<Style x:Key="showRow" TargetType="{x:Type RowDefinition}">
  <Style.Setters>
    <Setter Property="Height" Value="*"/>
  </Style.Setters>
  <Style.Triggers>
    <DataTrigger Binding="{Binding MyShowRowBool}" Value="False">
      <DataTrigger.Setters>
        <Setter Property="Height" Value="0"/>
        <Setter Property="MaxHeight" Value="0"/>
      </DataTrigger.Setters>
    </DataTrigger>
  </Style.Triggers>
</Style>

I simply applied the style to the relevant row definitions, and it worked like a charm:

我只是将样式应用于相关的行定义,它就像一个魅力:

<Grid.RowDefinitions>
  <RowDefinition Height="*"/>
  <RowDefinition Style="{StaticResource showRow}"/>
  <RowDefinition Style="{StaticResource showRow}"/>
</Grid.RowDefinitions>

Notable is that I tried it without the MaxHeight property, and it wasn't collapsing properly. Adding it in seems to have done the trick for me.

值得注意的是,我在没有 MaxHeight 属性的情况下尝试了它,但它没有正确折叠。添加它似乎对我有用。

回答by jpierson

This GridExpandercontrol that inherits form GridSpliter that might to the job you are looking for. Credit goes to Shemeshfor writing the original Silverlight version that I adapted for my own usage in WPF. I find myself wanting this functionality almost everywhere I attempt to use GridSplitter so it can be quite handy.

这个GridExpander控件继承了可能适用于您正在寻找的工作的 GridSpliter 形式。幸得示麦写的原始版本的Silverlight,我适合在WPF我自己的使用。我发现自己几乎在任何尝试使用 GridSplitter 的地方都需要此功能,因此它非常方便。