为什么这个数据绑定不改变 GridUnitType?

Why does this data binding not change the GridUnitType?

我的目标是保存一个 GridSplitter 位置供以后调用。拆分器位于 Grid 控件内,该控件具有如下定义的三列:

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="{Binding GridPanelWidth, Mode=TwoWay}" />
    <ColumnDefinition Width="3" />  <!--splitter itself is in this column-->
    <ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>

属性 GridPanelWidth 在视图模型中是这样定义的:

private GridLength _gridPanelWidth = new GridLength(1, GridUnitType.Star);
public GridLength GridPanelWidth
{
    get { return _gridPanelWidth; }
    set
    {
        if (_gridPanelWidth != value)
            SetProperty(ref _gridPanelWidth, value, () => GridPanelWidth);
    }
}

我遇到的问题是,当拆分器移动时,绑定仅更新绑定 属性 的 Double(值)组件,而不更新 GridUnitType 部分

示例:属性 默认为 1*。用户拖动拆分器,值变为 354*,而不仅仅是 354。那么,在恢复值时,它是巨大的(354 倍,而不是 354 像素)。

为什么会这样,你会怎么做?

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="{Binding GridPanelWidth, Mode=TwoWay}" />
        <ColumnDefinition Width="4" />
        <!--splitter itself is in this column-->
        <ColumnDefinition x:Name="RightColumn" Width="2*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Border
        BorderBrush="Gray"
        BorderThickness="1"
        Grid.Column="0"
        Grid.Row="0"
        />

    <GridSplitter
        Background="SteelBlue"
        ResizeBehavior="PreviousAndNext"
        ResizeDirection="Columns"
        VerticalAlignment="Stretch"
        HorizontalAlignment="Stretch"
        ShowsPreview="False"
        Grid.Column="1"
        Grid.Row="0"
        />

    <Border
        BorderBrush="Gray"
        BorderThickness="1"
        Grid.Column="2"
        Grid.Row="0"
        />

    <StackPanel
        Grid.Row="1"
        Grid.ColumnSpan="3"
        Grid.Column="0"
        >
        <TextBlock>
            <Run>GridPanelWidth: </Run>
            <Run Text="{Binding GridPanelWidth.Value, Mode=OneWay}" />
            <Run Text="{Binding GridPanelWidth.GridUnitType, Mode=OneWay}" />
        </TextBlock>
        <TextBlock>
            <Run>RightColumn.Width: </Run>
            <Run Text="{Binding Width.Value, ElementName=RightColumn, Mode=OneWay}" />
            <Run Text="{Binding Width.GridUnitType, ElementName=RightColumn, Mode=OneWay}" />
        </TextBlock>
    </StackPanel>
</Grid>

截图一:

截图二:

截图三:

Res ipsa loquitor,就我而言,但为了安全起见:

因为父级可能会调整大小,网格拆分器会更改两列之间的比例,同时为每一列保留 GridUnitType.Star,以便在调整父级大小时,比例自然会保持不变。这保留了 XAML.

中初始宽度值的意图 左列的

Width.Value 结果与左 Border 的 ActualWidth 相同,右列也是如此。你必须同时抓住两者并保存比例。

更新

我发现 Grid/GridSplitter 对于日常使用有点过度设计,因为我想要的只是另一个导航窗格,所以我最近写了一个 SplitterControl,它有两个内容属性和在模板中设置带有样式的网格和拆分器。我还没来得及让分流比保持不变,所以我现在就做了。

我做的比较痛苦,因为控件是可配置的,代码也不是很好,但如果你有兴趣,我可以分享。

业务端简单:

当列调整大小时,设置一个标志来阻止递归和

PaneRatio = _PART_ContentColumn.Width.Value / _PART_NavColumn.Width.Value;

PaneRatio 发生变化时,如果它不是由列大小更改处理程序设置的

_PART_NavColumn.Width = new GridLength(1, GridUnitType.Star);
_PART_ContentColumn.Width = new GridLength(PaneRatio, GridUnitType.Star);

实际上,navigator/content 列可以交换,或者它们可以是行。这两个都是通过在作为拆分控件模板的子项的 HeaderedContentControl 上切换模板来完成的。