动态更改 WPF 应用程序的布局以在网格上显示一两个组件

问题描述 投票:0回答:1

我有一个带有树列表主视图的 WPF 应用程序,我还希望能够在按下按钮时打开第二个组件(并在再次按下时隐藏它)。

但是我无法使第二个组件占据所需的空间,并且树视图其余部分不重叠。

在XAML中,我声明了一个包含5行的网格,如下所示:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="50"/>
        <RowDefinition />
        <RowDefinition Height="auto"/>
        <RowDefinition />
        <RowDefinition Height="35" />
    </Grid.RowDefinitions>

    <views:InputsControl Grid.Row="0"/>

    <views:TreeViewControl Grid.Row="1" />

    <GridSplitter Grid.Row="2"
          HorizontalAlignment="Stretch"
          Height="3" 
          Visibility="{Binding LoggerVisibility}" >
    </GridSplitter>

    <TextBox Grid.Row="3"
             BorderThickness="1"
             Margin="2 ,0, 2, 2"
             IsReadOnly="True"
             Visibility="{Binding LoggerVisibility}">
        Some logger info
    </TextBox>

    <views:StatusBar Grid.Row="4"/>
</Grid>

视图模型:

private Visibility _LoggerVisibility = Visibility.Collapsed;
public Visibility LoggerVisibility
{
    get => _LoggerVisibility;
    set
    {
        _LoggerVisibility = value;
        OnPropertyChanged(nameof(LoggerVisibility));
    }
}

private void OnPropertyChanged(string propertyName) =>
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

public MainWindowViewModel()
{
    Messenger.Default.Register<Actions>(this, ProcessAction);
}

private void ProcessAction(Actions action)
{
    if (action == Actions.ToggleLogger)
    {
        if (LoggerVisibility == Visibility.Collapsed)
        {
            LoggerVisibility = Visibility.Visible;
        }
        else if (LoggerVisibility == Visibility.Visible)
        {
            LoggerVisibility = Visibility.Collapsed;
        }
    }
}

该应用程序如下所示:

首次运行时

单击第一个按钮后

点击第二个按钮后

所以我希望它在第二次单击后进入第一张照片中的布局。

我注意到,如果我更改树列表视图行的行跨度,我可以在单独的步骤中获得所需的行为。如果记录器可见,则上面的代码工作得很好。如果记录器关闭并且我将 Grid.RowSpan = "3" 添加到树列表行,看起来也不错。所以我想我可以在后面的代码中绑定 rowspan 的值,并使其与

LoggerVisibility
同步,当按下按钮时,它也会在后面的代码中切换。

但由于某种原因,行跨度没有更新。我认为我绑定它的方式是正确的,因为我对

LoggerVisibility
绑定做了同样的事情,并且它完成了它的工作,并且按下按钮时记录器确实可以折叠或可见。

你能帮我吗?

c# wpf xaml mvvm gridview
1个回答
0
投票

GridSpliter
的目的是重新分配行/列之间的空间。它通过更改相邻行/列的大小来实现此目的。换句话说,
GridSplitter
会覆盖
RowDefinition
ColumnDefinition
的原始值。这意味着您必须备份原始
GridLength
值,并在
GridSplitter
折叠时恢复它们。

以下示例显示了一个简单的附加行为,通过在

GridSplitterService.IsVisible
上设置/绑定
GridSplitter
附加属性来处理此问题。
因为我们现在可以引入一个布尔属性来切换视图模型的可见性,所以解决方案在 MVVM 方面变得更清晰。

ViewModel.cs

private bool isLoggerEnabled;
public Visibility IsLoggerEnabled
{
    get => this.isLoggerEnabled;
    set
    {
        this.isLoggerEnabled = value;
        OnPropertyChanged(nameof(IsLoggerEnabled));
    }
}

private void ProcessAction(Actions action)
{
  if (action == Actions.ToggleLogger)
  {
    // Simply togglöe the boolean value using XOR
    this.IsLoggerEnabled ^= true;     
  }
}

MainWindow.xaml

<Grid>
  <Grid.Resources>

    <!-- use the built-in BooleanToVisibilityConverter -->
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
  </Grid.Resources>

  <Grid.RowDefinitions>
    <RowDefinition Height="50"/>
    <RowDefinition />
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="35" />
  </Grid.RowDefinitions>

  <views:InputsControl Grid.Row="0"/>

  <views:TreeViewControl Grid.Row="1" />

  <GridSplitter Grid.Row="2"
                HorizontalAlignment="Stretch"
                Height="3" 
                GridSplitterService.IsVisible="{Binding IsLoggerEnabled}" />

  <!-- Consider to use the lightweight TextBlock instead -->
  <TextBox Grid.Row="3"
           BorderThickness="1"
           Margin="2 ,0, 2, 2"
           IsReadOnly="True"
           Visibility="{Binding IsLoggerEnabled, Converter={StaticResource BooleanToVisibilityConverter}}">
           Text="Some logger info" />

  <views:StatusBar Grid.Row="4"/>
</Grid>

GridSplitterService.cs

public class GridSplitterService : DependencyObject
{
  class BackupInfo
  {
    public BackupInfo(GridLength adjacentPreviousGridLength, GridLength adjacentNextGridLength, DefinitionBase adjacentPreviousGridElementDefinition, DefinitionBase adjacentNextGridElementDefinition)
    {
      this.AdjacentPreviousGridLength = adjacentPreviousGridLength;
      this.AdjacentNextGridLength = adjacentNextGridLength;
      this.AdjacentPreviousGridElementDefinition = adjacentPreviousGridElementDefinition;
      this.AdjacentNextGridElementDefinition = adjacentNextGridElementDefinition;
    }

    public void RestoreAdjacentColumns()
    {
      ((ColumnDefinition)this.AdjacentPreviousGridElementDefinition).Width = this.AdjacentPreviousGridLength;
      ((ColumnDefinition)this.AdjacentNextGridElementDefinition).Width = this.AdjacentNextGridLength;
    }

    public void RestoreAdjacentRows()
    {
      ((RowDefinition)this.AdjacentPreviousGridElementDefinition).Height = this.AdjacentPreviousGridLength;
      ((RowDefinition)this.AdjacentNextGridElementDefinition).Height = this.AdjacentNextGridLength;
    }

    public GridLength AdjacentPreviousGridLength { get; }
    public GridLength AdjacentNextGridLength { get; }
    public DefinitionBase AdjacentPreviousGridElementDefinition { get; }
    public DefinitionBase AdjacentNextGridElementDefinition { get; }
  }

  public static bool GetIsVisible(GridSplitter attachingElement) 
    => (bool)attachingElement.GetValue(IsVisibleProperty);

  public static void SetIsVisible(GridSplitter attachingElement, bool value) 
    => attachingElement.SetValue(IsVisibleProperty, value);

  public static readonly DependencyProperty IsVisibleProperty = DependencyProperty.RegisterAttached(
    "IsVisible", 
    typeof(bool), 
    typeof(GridSplitterService), 
    new PropertyMetadata(default(bool), OnIsVisibleChanged));

  private static ConditionalWeakTable<GridSplitter, BackupInfo> OriginalGridLengthMap { get; }
    = new ConditionalWeakTable<GridSplitter, BackupInfo>();

  private static void OnIsVisibleChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    if (attachingElement is not GridSplitter gridSplitter)
    {
      throw new ArgumentException($"Attaching element must be of type {typeof(GridSplitter).FullName}.", nameof(attachingElement));
    }

    bool isResizingRows = gridSplitter.ResizeDirection is GridResizeDirection.Rows
      || gridSplitter.HorizontalAlignment is HorizontalAlignment.Stretch;
    var host = (Grid)gridSplitter.Parent;

    if (!GridSplitterService.OriginalGridLengthMap.TryGetValue(gridSplitter, out _))
    {
      StoreOriginalGridLengthBeforeGridSplitterResize(gridSplitter, isResizingRows, host);
    }

    bool isVisible = (bool)e.NewValue;
    if (isVisible)
    {
      gridSplitter.Visibility = Visibility.Visible;
    }
    else if (GridSplitterService.OriginalGridLengthMap.TryGetValue(gridSplitter, out BackupInfo backupInfo))
    {
      gridSplitter.Visibility = Visibility.Collapsed;

      if (isResizingRows)
      {
        backupInfo.RestoreAdjacentRows();
      }
      else
      {
        backupInfo.RestoreAdjacentColumns();
      }
    }
  }

  private static void StoreOriginalGridLengthBeforeGridSplitterResize(GridSplitter gridSplitter, bool isResizingRows, Grid host)
  {
    GridLength originalAdjacentPreviousGridLength;
    GridLength originalAdjacentNextGridLength;
    DefinitionBase originalAdjacentPreviousGridElementDefinition;
    DefinitionBase originalAdjacentNextGridElementDefinition;

    if (isResizingRows)
    {
      int gridSplitterRowIndex = Grid.GetRow(gridSplitter);
      int gridSplitterTopRowIndex = gridSplitterRowIndex - 1;
      int gridSplitterBottomRowIndex = gridSplitterRowIndex + 1;

      originalAdjacentPreviousGridElementDefinition = host.RowDefinitions[gridSplitterTopRowIndex];
      originalAdjacentPreviousGridLength = ((RowDefinition)originalAdjacentPreviousGridElementDefinition).Height;
      originalAdjacentNextGridElementDefinition = host.RowDefinitions[gridSplitterBottomRowIndex];
      originalAdjacentNextGridLength = ((RowDefinition)originalAdjacentNextGridElementDefinition).Height;
    }
    else
    {
      int gridSplitterColumnIndex = Grid.GetColumn(gridSplitter);
      int gridSplitterLeftColumnIndex = gridSplitterColumnIndex - 1;
      int gridSplitterRigthColumnIndex = gridSplitterColumnIndex + 1;

      originalAdjacentPreviousGridElementDefinition = host.ColumnDefinitions[gridSplitterLeftColumnIndex];
      originalAdjacentPreviousGridLength = ((ColumnDefinition)originalAdjacentPreviousGridElementDefinition).Width;
      originalAdjacentNextGridElementDefinition = host.ColumnDefinitions[gridSplitterRigthColumnIndex];
      originalAdjacentNextGridLength = ((ColumnDefinition)originalAdjacentNextGridElementDefinition).Width;
    }

    var backupInfo = new BackupInfo(originalAdjacentPreviousGridLength, originalAdjacentNextGridLength, originalAdjacentPreviousGridElementDefinition, originalAdjacentNextGridElementDefinition);
    GridSplitterService.OriginalGridLengthMap.Add(gridSplitter, backupInfo);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.