我正在开发一个简单的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,它将起作用。但我想动态地做:
可以用ActualHeight,ActualWidth解决?和DependecyProperty?
答案 0 :(得分:2)
这几乎是不可能的,或者我应该说期望ScrollViewer
用Stretch = UniformToFill
来了解图像的边界并没有多大意义。根据{{3}}:
<强> UniformToFill 强>: 调整内容(您的图像)以填充目标尺寸(窗口或网格) 保留其原始宽高比。如果长宽比为 目标矩形与源不同,源内容是 剪裁以适合目标尺寸(因此图像将被剪切)。
所以我认为我们真正需要的是使用Uniform + Proper Scaling
代替UniformToFill
。
解决方案是Stretch
设置为UniformToFill
时必须设置为Uniform
,然后设置为Image.Width = image actual width * scalingParam
和Image.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也是如此,而不是激活滚动条。