WPF:动态隐藏网格列并不总是使用 Styles 或 IValueConverter 正常工作
Posted
技术标签:
【中文标题】WPF:动态隐藏网格列并不总是使用 Styles 或 IValueConverter 正常工作【英文标题】:WPF: Dynamically hiding a grid column doesn't always work correctly using Styles or IValueConverter 【发布时间】:2022-01-12 09:08:14 【问题描述】:我有一个包含三列的网格 - 左、右和中间的网格拆分器。我需要隐藏其中一个面板。我需要为两个 Splitter 列和 Panel 列设置 Width=0。但是,这种方法仅适用于代码。当我使用styles 或value converters 时,它只在某些情况下有效。
在我移动拆分器、面板隐藏但留下空白(对于案例#2/样式)或不隐藏其中一个面板(对于案例#3/IValueConverter)之前,一切都按预期工作。只有“代码背后”的方式在所有情况下都能正常工作。下面的 GIF 图片说明了这种行为。
代码如下所示。主要思想是将网格列的 Width 和 MaxWidth 属性设置为 0,然后将面板设置为 *,将拆分器设置为 Auto。
1.它的工作方式(代码隐藏):
private void TogglePanelVisibility(bool isVisible)
if (isVisible)
// Restore saved parameters:
_mainWindow.ColumnPanel.Width = new GridLength(_columnPanelWidth, GridUnitType.Star);
_mainWindow.ColumnPanel.MaxWidth = double.PositiveInfinity;
_mainWindow.ColumnSplitter.Width = new GridLength(_columnSplitterWidth, GridUnitType.Auto);
_mainWindow.ColumnSplitter.MaxWidth = double.PositiveInfinity;
return;
// Save parameters:
_columnSplitterWidth = _mainWindow.ColumnSplitter.Width.Value;
_columnPanelWidth = _mainWindow.ColumnPanel.Width.Value;
// Hide panel:
_mainWindow.ColumnPanel.Width = new GridLength(0);
_mainWindow.ColumnPanel.MaxWidth = 0;
_mainWindow.ColumnSplitter.Width = new GridLength(0);
_mainWindow.ColumnSplitter.MaxWidth = 0;
2。它不起作用的方式(XAML 样式)
<Window.Resources>
<Style x:Key="showColumnStar" TargetType="x:Type ColumnDefinition">
<Style.Setters>
<Setter Property="Width" Value="*" />
<Setter Property="MaxWidth" Value="x:Static system:Double.PositiveInfinity" />
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="Binding IsPanelVisible" Value="False">
<DataTrigger.Setters>
<Setter Property="Width" Value="0" />
<Setter Property="MaxWidth" Value="0" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="showColumnAuto" TargetType="x:Type ColumnDefinition">
<Style.Setters>
<Setter Property="Width" Value="Auto" />
<Setter Property="MaxWidth" Value="x:Static system:Double.PositiveInfinity" />
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="Binding IsPanelVisible" Value="False">
<DataTrigger.Setters>
<Setter Property="Width" Value="0" />
<Setter Property="MaxWidth" Value="0" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<!-- ... -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" Style="StaticResource showColumnAuto" />
<ColumnDefinition Width="*" Style="StaticResource showColumnStar" />
</Grid.ColumnDefinitions>
<!-- ... -->
</Grid>
3。最后一个使用值转换器的场景
XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Binding IsPanelVisible, Converter=StaticResource BoolToGridSizeConverter, ConverterParameter='Auto'" />
<ColumnDefinition Width="Binding IsPanelVisible, Converter=StaticResource BoolToGridSizeConverter, ConverterParameter='*'" />
</Grid.ColumnDefinitions>
<!-- ... -->
</Grid>
C#:
internal class BoolToGridRowColumnSizeConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
var param = parameter as string;
var unitType = GridUnitType.Star;
if (param != null && string.Compare(param, "Auto", StringComparison.InvariantCultureIgnoreCase) == 0)
unitType = GridUnitType.Auto;
return ((bool)value == true) ? new GridLength(1, unitType) : new GridLength(0);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
throw new NotImplementedException();
我从调试案例 #3 中学到了另外一件事 - 在移动拆分器后尝试显示或隐藏面板时,Converted 只调用一次。
您能否告诉我发生了什么,为什么案例 #2 和 #3 不能正常工作?
演示项目的完整源代码在GitHub。
提前谢谢你!
【问题讨论】:
为什么不只使用列的 Visibility 属性? @Ugur,网格列没有可见性。这是关于如何添加可见性的article on CodeProject,但它仍然建议相同 - 将宽度设置为零。 抱歉可能不好。您可以将 gui 元素隐藏在网格内,而不是更改列的宽度。这意味着,您可以将列设置为“*”或“auto”,然后将组件隐藏在里面。 你的建议我试过了,谢谢!然而,不幸的是,它的工作方式相同 - 刚启动后就完美了,但如果我移动分离器,屏幕上会留下一个白色的“洞”(与情况 #2 完全相同)。 【参考方案1】:我克隆了你的代码:
至于#2:
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" Style="StaticResource showColumnAuto" />
<ColumnDefinition Width="Auto" Style="StaticResource showColumnStar" />
</Grid.ColumnDefinitions>
将第三列设置为“自动”就可以了
对于#3: 如果您调试解决方案并在转换器中设置断点,您会看到,在将拆分器移动到第三个块中之后,转换器仅到达一次,因此对于带有拆分器的列。移动拆分器会破坏第三列上的宽度绑定(正如@mm8 在他的回答中指出的那样)。
因此,您要么在拆分器移动后重新定义绑定(例如 PreviewMouseLeftButtonUp 事件),要么放弃这种方法。
【讨论】:
谢谢,将第 3 列更改为Auto
可以解决问题,谢谢!但为什么? Star
和 Auto
之间有什么区别?根据@mm8 的回答,'GridSplitter` 应该设置ColumnDefinition
的内部Width
,因此,跳过Style
中设置的Width
。
for #3:我尝试重新定义DragCompleted
中的绑定(在GridSplitter
被移动后它会上升),并且它有效(有一些抱怨)。但是,我的问题的主要思想是能够以最少的代码在 XAML 中显示和隐藏面板——在我的 ViewModel 中使用布尔值IsPanelVisible
属性,并可以选择在ValueConverter
的帮助下。如果使用代码,#1 方法可能会更好。【参考方案2】:
您能否告诉我发生了什么,为什么案例 #2 和 #3 不能正常工作?
因为GridSplitter
设置ColumnDefinition
的Width
属性的本地值,并且本地值始终优先于样式设置器值,如docs 中所述。
因此,您必须像在代码隐藏中那样设置 Width
属性的本地值。
【讨论】:
感谢您的回答,它解释了很多。但是我仍然有一个问题:为什么只有当我将拆分器向右移动时才会出现问题?如果我将它向左移动,GridSplitter
也应该设置Width
的本地值,并且面板不应该消失,但确实会消失。以上是关于WPF:动态隐藏网格列并不总是使用 Styles 或 IValueConverter 正常工作的主要内容,如果未能解决你的问题,请参考以下文章