0

我有一个 WPF UserControl(在一个内部ElementHost),其中一个ScrollViewer包含一个ItemsControl. HorizontalScrollbarVisibility设置为,因此Auto如果不需要滚动,则会ScrollBar隐藏 。

我的要求是,如果ScrollBar显示/隐藏,ElementHost确实会相应地调整它的高度。为了实现这一点,我正在听SizeChanged事件,我得到了DesiredSizeScrollViewerEventHandler,然后我传递DesiredSize.HeightElementHost.

  1. 可见的 2. 隐 3.不正确的,重叠的

一种方法,这是可行的:在ScrollBar可见的情况下(情况 1),我放大我的窗口,直到所有项目ItemsControl都可见,ScrollBar消失,ElementHost调整到降低的高度(情况 2)。实际上在隐藏DesiredSize的那一刻变小了。ScrollBar

但是,另一种方式不起作用:在ScrollBar不可见的情况下(情况 2),我减小了窗口大小,直到 aScrollBar是必要的并出现。DesiredSize保持不变,并且ElementHost不调整(情况3)。

有任何想法吗?

这是 的 xaml Scrollviewer,有一些 MVVM 的东西,但不要挂断这个,关键是,为什么出现DesiredSize时不增加ScrollBar?为什么只是缩水?

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden" >
    <i:Interaction.Behaviors>
        <beh:HeightChangedBehavior HeightChangedCommand="{Binding HeightChangedCommand}" />
    </i:Interaction.Behaviors>
    <ItemsControl ItemsSource="{Binding TabHeaders}" >
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel IsItemsHost="True" Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="models:TabHeaderButtonModel">
                <RadioButton Content="{Binding Caption}" IsChecked="{Binding IsChecked, Mode=TwoWay}" GroupName="Tabs" 
                            Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding}"
                            Style="{StaticResource TabHeaderToggleButtonStyle}">
                </RadioButton>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

ScrollViewer 样式(基本上是默认的 WPF):

<Style x:Key="ScrollViewerStyle1" TargetType="{x:Type ScrollViewer}">
    <Setter Property="Template" >
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                <Grid x:Name="Grid" Background="{TemplateBinding Background}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
                    <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
                    <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
                    <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Style="{DynamicResource ScrollBarStyle1}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Foreground" Value="White"/>
        </Trigger>
    </Style.Triggers>
</Style>
4

1 回答 1

0

我必须通过获取 ScrollViewer 的内容所需的高度并添加 ScrollBar 高度(如果可见)来计算所需的高度。

这对我来说仍然有点尴尬,所以如果你有更好的解决方案,我很乐意改变接受的答案。

public class HeightChangedBehavior : Behavior<ScrollViewer>
{
    public ICommand HeightChangedCommand { get { return (ICommand)GetValue(HeightChangedCommandProperty); } set { SetValue(HeightChangedCommandProperty, value); } }
    public static readonly DependencyProperty HeightChangedCommandProperty = DependencyProperty.Register("HeightChangedCommand", typeof(ICommand), typeof(HeightChangedBehavior), new PropertyMetadata(null));


    protected override void OnAttached()
    {
        this.AssociatedObject.ScrollChanged += AssociatedObject_ScrollChanged;
        base.OnAttached();
    }

    /// <summary>
    /// Calculates the desired height for the scrollviewer, as the sum of its content
    /// desired height and, if visible, the horizontal scrollbar height.
    /// </summary>
    void AssociatedObject_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        ScrollViewer sv = (ScrollViewer)sender;

        // get content height
        double height = ((FrameworkElement)sv.Content).DesiredSize.Height;

        if (sv.ComputedHorizontalScrollBarVisibility == Visibility.Visible)
        {
            // add scrollbar height
            height += (double)sv.FindResource(SystemParameters.HorizontalScrollBarHeightKey); // template of scrollbar should use this key
        }

        int intHeight = (int)Math.Ceiling(height); // whole pixels

        // execute the command
        ICommand cmd = this.HeightChangedCommand;
        if (cmd != null && intHeight != sv.ActualHeight)
            cmd.Execute(intHeight);
    }
}
于 2013-03-03T14:40:34.417 回答