我有一个带有树列表主视图的 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
绑定做了同样的事情,并且它完成了它的工作,并且按下按钮时记录器确实可以折叠或可见。
你能帮我吗?
GridSpliter
的目的是重新分配行/列之间的空间。它通过更改相邻行/列的大小来实现此目的。换句话说,GridSplitter
会覆盖 RowDefinition
或 ColumnDefinition
的原始值。这意味着您必须备份原始 GridLength
值,并在 GridSplitter
折叠时恢复它们。
以下示例显示了一个简单的附加行为,通过在
GridSplitterService.IsVisible
上设置/绑定 GridSplitter
附加属性来处理此问题。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);
}
}