Wpf Scroll Image使用ScrollViewer和动态Stretch

时间:2015-07-14 07:10:17

标签: wpf image scrollviewer

我正在开发一个简单的imageviewer应用程序。我基于ViewModel属性控制绑定上的Stretch属性。

当我使用绑定到ViewModel的'Combobox'更改Stretch属性时,会出现问题,并且当使用'UniformToFill'时,图像'切断'宽图像的角落。因此,使用ScrollViewer可以滚动图像内容。

问题是ScrollViewer似乎没有显示滚动条让我能够滚动。

WPF加价:

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="*"/>
</Grid.RowDefinitions>

<!-- Other Grids removed -->

<Grid Name="Container" Grid.Column="2" Grid.Row="0" Grid.RowSpan="2">
    <ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">                    
        <Image Source="{Binding SelectedPhoto.Value.Image}" 
                Stretch="{Binding ImageStretch}" Name="PhotoImage" />               
    </ScrollViewer>           
</Grid>

我理解如果我将固定的高度和宽度设置为ScrollViewer和Image,它将起作用。但我想动态地做:

  • ScrollView将具有来自Parent'Grid(Contaioner)'控件的高度和宽度。
  • 图像将具有自身的高度和宽度,但在该计算中将Stretch考虑在内。

可以用ActualHeight,ActualWidth解决?和DependecyProperty?

2 个答案:

答案 0 :(得分:2)

这几乎是不可能的,或者我应该说期望ScrollViewerStretch = UniformToFill来了解图像的边界并没有多大意义。根据{{​​3}}:

  

<强> UniformToFill :   调整内容(您的图像)以填充目标尺寸(窗口或网格)   保留其原始宽高比。如果长宽比为   目标矩形与源不同,源内容是   剪裁以适合目标尺寸(因此图像将被剪切)。

所以我认为我们真正需要的是使用Uniform + Proper Scaling代替UniformToFill

解决方案是Stretch设置为UniformToFill时必须设置为Uniform,然后设置为Image.Width = image actual width * scalingParamImage.Height= image actual height * scalingParam,其中scalingParam = Grid.Width (or Height) / image actual width (or Height)。这种方式ScrollViewer边界将与图像缩放尺寸相同。

我已经提供了一个工作解决方案给你一个想法,我不确定它对你的情况有多合适但是在这里:

首先,我为我的图像定义了一个简单的视图模型:

    public class ImageViewModel: INotifyPropertyChanged
    {

    // implementation of INotifyPropertyChanged ...

    private BitmapFrame _bitmapFrame;

    public ImageViewModel(string path, Stretch stretch)
    {
         // determining the actual size of the image.
        _bitmapFrame = BitmapFrame.Create(new Uri(path), BitmapCreateOptions.DelayCreation, BitmapCacheOption.None);

        Width = _bitmapFrame.PixelWidth;
        Height = _bitmapFrame.PixelHeight;
        Scale = 1;
        Stretch = stretch;
    }

    public int Width { get; set; }
    public int Height { get; set; }

    double _scale;
    public double Scale
    {
        get
        {
            return _scale;
        }
        set
        {
            _scale = value;
            OnPropertyChanged("Scale");
        }
    }
    Stretch _stretch;
    public Stretch Stretch
    {
        get
        {
            return _stretch;
        }
        set
        {
            _stretch = value;
            OnPropertyChanged("Stretch");
        }
    }
}

在上面的代码中,BitmapFrame用于确定图像的实际大小。 然后我在我的Mainwindow(或主视图模型)中进行了一些初始化:

    // currently displaying image
    ImageViewModel _imageVm;
    public ImageViewModel ImageVM
    {
        get
        {
            return _imageVm;
        }
        set
        {
            _imageVm = value;
            OnPropertyChanged("ImageVM");
        }
    }

    // currently selected stretch type
    Stretch _stretch;
    public Stretch CurrentStretch
    {
        get
        {
            return _stretch;
        }
        set
        {
            _stretch = value;
            //ImageVM should be notified to refresh UI bindings
            ImageVM.Stretch = _stretch;
            OnPropertyChanged("ImageVM");
            OnPropertyChanged("CurrentStretch");
        }
    }

    // a list of Stretch types
    public List<Stretch> StretchList { get; set; }
    public string ImagePath { get; set; }
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;

        // sample image path
        ImagePath = @"C:\Users\...\YourFile.png";

        StretchList = new List<Stretch>();
        StretchList.Add( Stretch.None);
        StretchList.Add( Stretch.Fill);
        StretchList.Add( Stretch.Uniform);
        StretchList.Add( Stretch.UniformToFill);

        ImageVM = new ImageViewModel(ImagePath, Stretch.None);

        CurrentStretch = StretchList[0];

    }

我的Xaml看起来像这样:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid Grid.Row="0" Grid.Column="0" >
        <Grid.Resources>
            <local:MultiConverter x:Key="multiC"/>
        </Grid.Resources>
        <ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
            <Image Source="{Binding ImagePath}" Name="PhotoImage">
                <Image.Stretch>
                    <MultiBinding Converter="{StaticResource multiC}">
                        <Binding Path="ImageVM" />
                        <Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="ActualWidth"/>
                        <Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="ActualHeight"/>
                    </MultiBinding>
                </Image.Stretch>
                <Image.LayoutTransform>
                    <ScaleTransform ScaleX="{Binding ImageVM.Scale}" ScaleY="{Binding ImageVM.Scale}"
                        CenterX="0.5" CenterY="0.5" />
            </Image.LayoutTransform>
        </Image>
        </ScrollViewer>
    </Grid>
    <ComboBox Grid.Row="2" Grid.Column="0" ItemsSource="{Binding StretchList}" SelectedItem="{Binding CurrentStretch}" DisplayMemberPath="."/>
</Grid>

如您所见,我使用了一个带有3个参数的多值转换器:当前图像视图模型和窗口宽度和高度。此参数用于计算图像填充区域的当前大小。我还使用ScaleTransform将该区域缩放到计算出的大小。这是多值转换器的代码:

public class MultiConverter : IMultiValueConverter
{
    public object Convert(
        object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] is ImageViewModel)
        {
            var imageVm = (ImageViewModel)values[0];

            // if user selects UniformToFill
            if (imageVm.Stretch == Stretch.UniformToFill)
            {
                var windowWidth = (double)values[1];
                var windowHeight = (double)values[2];

                var scaleX = windowWidth / (double)imageVm.Width;
                var scaleY = windowHeight / (double)imageVm.Height;

                // since it's "uniform" Max(scaleX, scaleY) is used for scaling in both horizontal and vertical directions
                imageVm.Scale = Math.Max(scaleX, scaleY);

                // "UniformToFill" is actually "Uniform + Proper Scaling"
                return Stretch.Uniform;
            }
            // if user selects other stretch types
            // remove scaling
            imageVm.Scale = 1;
            return imageVm.Stretch;
        }
        return Binding.DoNothing;
    }

    public object[] ConvertBack(
        object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

答案 1 :(得分:0)

问题可能来自你的布局的其余部分 - 如果Grid包含在一个无限可调整大小的容器中(Grid Column / Row设置为Auto,StackPanel,另一个ScrollViewer ......),它将随着形象一起成长。 ScrollViewer也是如此,而不是激活滚动条。