首页 > 解决方案 > 自动调整“ListView”中图像的缩放以适合所有人,即在没有滚动条的情况下最大化它们的大小

问题描述

我有一个视图IntroView,其中包含一个ListView,其中包含图像,并且可以缩放(调整每个图像的大小(或更具体地说是UserControl)),由依赖属性ThumbZoom(在后面的代码中IntroView)确定。

我正在尝试自动调整该ThumbZoom值,以便里面的所有图像都适合ListView尺寸(它自己在一个可调整大小的窗口内)。

类似问题的这个选项不起作用:当溢出(垂直滚动条可见)发生时,切换可见性只会使滚动条消失,但不会改变ThumbZoom. 如何做到这一点?

的 xamlIntroView尤其具有:

<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled" Grid.Column="0"  BorderThickness="0"
          Background="Transparent"
          IsSynchronizedWithCurrentItem="True"
          MouseDoubleClick="Control_OnMouseDoubleClick"
          ItemsSource="{Binding RawImagesCollectionView}" SelectedItem="{Binding SelectedImage}">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel IsItemsHost="True" />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.ItemTemplate>
        <DataTemplate DataType="shutterLib:RawImageViewModel">
            <intro:RawImageControl/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

并且在依赖属性后面的代码中有ThumbZoom

    public double ThumbZoom
    {
        get { return (double) GetValue(ThumbZoomProperty); }
        set { SetValue(ThumbZoomProperty, value); }
    }

    public static readonly DependencyProperty ThumbZoomProperty =
        DependencyProperty.Register("ThumbZoom", typeof(double), typeof(IntroView),
            new PropertyMetadata(_minZoom));

UserControl调用的接收图像,如下所示:

<UserControl x:Class="Senso.ShutterModeler.UI.Intro.RawImageControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:ui="clr-namespace:Senso.ShutterModeler.UI"
             xmlns:shutterLib="clr-namespace:ShutterLib;assembly=ShutterLib"
             d:DataContext="{d:DesignInstance Type=shutterLib:RawImageViewModel, IsDesignTimeCreatable=True}"
             mc:Ignorable="d"
             d:DesignHeight="200" d:DesignWidth="400">
    <Grid Margin="5" MinWidth="100" MinHeight="100">
        <Rectangle Fill="DimGray"
                   MaxWidth="{Binding ThumbZoom, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ui:IntroView}}}"
                   MaxHeight="{Binding ThumbZoom, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ui:IntroView}}}">
            <Rectangle.Effect>
                <DropShadowEffect BlurRadius="11" Color="{StaticResource DropShadow}" />
            </Rectangle.Effect>
        </Rectangle>
        <Viewbox
            MaxWidth="{Binding ThumbZoom, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ui:IntroView}}}"
            MaxHeight="{Binding ThumbZoom, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ui:IntroView}}}">
            <Image Stretch="Uniform" Source="{Binding Vignette, IsAsync=false}" MouseMove="Vignette_OnMouseMove" />
        </Viewbox></Grid></UserControl>

要解决的问题(有几个条件:图像周围没有边距(为了简化),以及 1:2 的纵横比)是这样放置的:

在绘制之后,有这个方面(对于给定的T和):Ha WaScrollViewer

这是个好消息:这个最小值是存在的,并且可以通过二等分来解决。在解决方案的左侧,图像的大小太小而无法填充ListView,跳跃对应于每行图像数量的变化。在解决方案的右侧,图像的大小太大并触发垂直滚动条的出现。

不幸的是,根据 的方面 (ActualWidthActualHeight) ScrollViewer,解决方案仅有时在视觉上是正确的。似乎ScrollViewer嵌入的大小ListView有一个方面与.的视觉方面不匹配ListView。在纵横比条件Ha Wa(只有几个像素(~5),增量约为 70 像素)。

到目前为止我不明白。这是我需要帮助的地方。

我用于new ScrollViewer {RenderSize = new Size(1558, 1281) }情节。

解决问题的代码是:

    private double _minZoom = 200.0;
    private double _maxZoom = 900.0;
    private int _totalCount = 17; // total number of images to show in listview.

    /// <summary>
    /// Size of semi-range of solution. 
    /// </summary>
    public double BallRadius { get; set; } = 1e-6;
    
    /// <summary>
    /// Number of steps needed to find optimal zoom.
    /// </summary>
    public int StepCount { get; protected set; }

    /// <summary>
    /// Best zoom, where images are a big as possible, while no scrollbar appears.
    /// </summary>
    public double ZoomFitall => SolveMin(x => Deriv(DistZZbis, x));

    /// <summary>
    /// Solve minimum of func in [_minZoom, _maxZoom] with bisection method.
    /// </summary>
    /// <param name="func"></param>
    /// <returns>optimal zoom</returns>
    public double SolveMin(Func<double, double> func)
    {
        int stepCount = 0;
        double l = _minZoom;
        double r = _maxZoom;
        
        bool IsMinInThisRange(double a, double b) => func(a) * func(b) < 0;
        double GetMiddle(double a, double b) => (a + b) / 2.0;
        double IntervalWidth(double a, double b) => Abs(a - b);

        while (IntervalWidth(l, r) > BallRadius)
        {
            stepCount++;
            double m = GetMiddle(l, r);
            if (IsMinInThisRange(l, m))
                r = m;
            else
                l = m;
        }

        StepCount = stepCount;
        return l;
    }

    /// <summary>
    /// Calculate derivative of <c>func<c/> at <c>x0</c> over interval of <c>2dx</c>.
    /// </summary>
    /// <param name="func">for fitall, use DistZZbis</param>
    /// <param name="x0">deriveative at x0</param>
    /// <param name="dx">infinitesimal semi-interval over x</param>
    /// <returns></returns>
    public double Deriv(Func<double, double> func, double x0, double dx = 1e-6)
    {
        var left = func(x0 - dx);
        var right = func(x0 + dx);
        return right - left;
    }

    /// <summary>
    /// Calculate distance between <c>zoom</c> and <c>zoom bis</c> as described in FindFitAll.
    /// </summary>
    /// <param name="z"></param>
    /// <returns></returns>
    public double DistZZbis(double z) => Abs(z - Zbis(z));

    /// <summary>
    /// Calculate zoom bis.
    /// </summary>
    /// <param name="z"></param>
    /// <returns></returns>
    private double Zbis(double z) => 2 * _scroll.ActualHeight / Ceiling(_totalCount / Floor(_scroll.ActualWidth / z));

标签: c#wpfxaml

解决方案


推荐阅读