我有一个程序,用户可以通过鼠标或文本框裁剪选定的区域。现在我只能通过文本框进行裁剪。自从我实现MVVM以来,我不知道如何在我的ViewModel中实现裁剪功能。我已经能够在图像上绘制矩形,我在后面的代码中实现了这个。 我现在的问题是如何在ViewModel中实现裁剪功能,并从我的视图代码中获取值?
我的Xaml(查看)
<Window x:Class="PROSE.MinimizeImage.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PROSE"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:wi="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="575" Width="450">
<StackPanel Margin="10">
<Grid x:Name="GridLoadedImage" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="400" />
<RowDefinition Height="*"/>
<RowDefinition MaxHeight="100" />
</Grid.RowDefinitions>
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=slider1, Path=Value}" ScaleY="{Binding ElementName=slider1, Path=Value}"/>
</Grid.LayoutTransform>
<Canvas Name ="cnvImage">
<Image x:Name="_image" Margin="10" Source="{Binding CurrentImage.ImagePath, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" VerticalAlignment="Top" Stretch="Fill" MaxHeight="300"
MouseDown="_image_MouseDown" MouseMove="_image_MouseMove" MouseUp="_image_MouseUp"/>
</Canvas>
<ListBox ItemsSource="{Binding Images}" SelectedItem="{Binding CurrentImage}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}" MaxHeight="40"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
<WrapPanel>
<Label Content="X Field" />
<TextBox Text="{Binding CurrentImage.CropXPosition, UpdateSourceTrigger=PropertyChanged}" Width="40" />
<Label Content="Y Field" />
<TextBox Text="{Binding CurrentImage.CropYPosition, UpdateSourceTrigger=PropertyChanged}" Width="40" />
<Label Content="Height Field" />
<TextBox Text="{Binding CurrentImage.CropHeight, UpdateSourceTrigger=PropertyChanged}" Width="40" />
<Label Content="Width Field" />
<TextBox Text="{Binding CurrentImage.CropWidth, UpdateSourceTrigger=PropertyChanged}" Width="40" />
</WrapPanel>
<WrapPanel>
<Label Content="Width cm" />
<TextBox Text="{Binding CurrentImage.ResizeWidth, UpdateSourceTrigger=PropertyChanged}" Width="40" />
<Label Content="Height cm" />
<TextBox Text="{Binding CurrentImage.ResizeHeight, UpdateSourceTrigger=PropertyChanged}" Width="40" />
</WrapPanel>
<WrapPanel>
<Button Command="{Binding ResizeCommand}">Resize</Button>
<Button Command="{Binding OpenCommand}" Content="Open"/>
<Button Command="{Binding CropCommand}">Crop</Button>
<Button Command="{Binding SaveCommand}" Content="Save"/>
</WrapPanel>
</StackPanel>
背后的代码(查看)
public partial class MainWindow : Window
{
private Point startPoint;
private Rectangle rectSelectArea;
private bool isDragging = false;
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
private void _image_MouseDown(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(cnvImage);
if (rectSelectArea != null)
cnvImage.Children.Remove(rectSelectArea);
rectSelectArea = new Rectangle
{
Stroke = Brushes.LightBlue,
StrokeThickness = 2
};
Canvas.SetLeft(rectSelectArea, startPoint.X);
Canvas.SetTop(rectSelectArea, startPoint.X);
cnvImage.Children.Add(rectSelectArea);
}
private void _image_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released || rectSelectArea == null)
return;
var pos = e.GetPosition(cnvImage);
//set the position of rectangle
var x = Math.Min(pos.X, startPoint.X);
var y = Math.Min(pos.Y, startPoint.Y);
//set the dimension of rectangle
var w = Math.Max(pos.X, startPoint.X) - x;
var h = Math.Max(pos.Y, startPoint.Y) - y;
rectSelectArea.Width = w;
rectSelectArea.Height = h;
Canvas.SetLeft(rectSelectArea, x);
Canvas.SetTop(rectSelectArea, y);
}
private void _image_MouseUp(object sender, MouseButtonEventArgs e)
{
// rectSelectArea = null;
}
}
我遇到了各种帖子,但没有一个能真正帮助我。
答案 0 :(得分:1)
您可以创建一个实现裁剪操作的Blend行为,并为该行为提供您在MouseUp上调用的可绑定ICommand属性(可能还有一些其他条件)。然后,您可以将ICommand属性(从您的viewmodel)绑定到此属性,以便在完成裁剪时将viewmodel传递给所需的值。
https://msdn.microsoft.com/en-us/library/dn195718(v=vs.110).aspx
这是一个“粗略”样本,用于裁剪图像并保存裁剪后的图像:
<强>视图模型强>
public class ShellViewModel : BindableBase
{
public string Title => "Sample";
public ICommand SelectionCommand => new DelegateCommand<byte[]>(buffer =>
{
var path = @"C:\temp\output.bmp";
File.WriteAllBytes(path, buffer);
Process.Start(path);
});
}
<强> XAML 强>
<Window x:Class="Poc.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Poc.ViewModels"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behaviors="clr-namespace:Poc.Views.Interactivity.Behaviors"
mc:Ignorable="d"
Title="{Binding Title}" Height="350" Width="525">
<Window.DataContext>
<viewModels:ShellViewModel />
</Window.DataContext>
<Grid>
<Image x:Name="Image" Source="http://via.placeholder.com/350x150" Stretch="Fill" />
<Canvas Background="Transparent">
<i:Interaction.Behaviors>
<behaviors:CroppingBehavior x:Name="CroppingBehavior" Stroke="White" Thickness="2" SelectionCommand="{Binding SelectionCommand}" TargetElement="{Binding ElementName=Image}" />
</i:Interaction.Behaviors>
</Canvas>
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="10">
<Button Command="{Binding ElementName=CroppingBehavior, Path=SaveCommand}" Padding="10">Save</Button>
<Button Command="{Binding ElementName=CroppingBehavior, Path=ClearCommand}" Margin="4, 0,0,0" Padding="10">Clear</Button>
</StackPanel>
</Grid>
<强>行为强>
public class CroppingBehavior : Behavior<Canvas>
{
#region Fields
public DependencyProperty SelectionCommandProperty = DependencyProperty.Register(nameof(SelectionCommand), typeof(ICommand), typeof(CroppingBehavior));
public DependencyProperty StrokeProperty = DependencyProperty.Register(nameof(Stroke), typeof(Brush), typeof(CroppingBehavior), new PropertyMetadata(Brushes.Fuchsia));
public DependencyProperty ThicknessProperty = DependencyProperty.Register(nameof(Thickness), typeof(double), typeof(CroppingBehavior), new PropertyMetadata(2d));
public DependencyProperty StrokeDashCapProperty = DependencyProperty.Register(nameof(StrokeDashCap), typeof(PenLineCap), typeof(CroppingBehavior), new PropertyMetadata(PenLineCap.Round));
public DependencyProperty StrokeEndLineCapProperty = DependencyProperty.Register(nameof(StrokeEndLineCap), typeof(PenLineCap), typeof(CroppingBehavior), new PropertyMetadata(PenLineCap.Round));
public DependencyProperty StrokeStartLineCapProperty = DependencyProperty.Register(nameof(StrokeStartLineCap), typeof(PenLineCap), typeof(CroppingBehavior), new PropertyMetadata(PenLineCap.Round));
public DependencyProperty TargetElementProperty = DependencyProperty.Register(nameof(TargetElement), typeof(FrameworkElement), typeof(CroppingBehavior));
private Point _startPoint;
#endregion
#region Properties
public ICommand SelectionCommand
{
get => (ICommand)GetValue(SelectionCommandProperty);
set => SetValue(SelectionCommandProperty, value);
}
public Brush Stroke
{
get => (Brush)GetValue(StrokeProperty);
set => SetValue(StrokeProperty, value);
}
public double Thickness
{
get => (double)GetValue(ThicknessProperty);
set => SetValue(ThicknessProperty, value);
}
public PenLineCap StrokeDashCap
{
get => (PenLineCap)GetValue(StrokeDashCapProperty);
set => SetValue(StrokeDashCapProperty, value);
}
public PenLineCap StrokeEndLineCap
{
get => (PenLineCap)GetValue(StrokeEndLineCapProperty);
set => SetValue(StrokeEndLineCapProperty, value);
}
public PenLineCap StrokeStartLineCap
{
get => (PenLineCap)GetValue(StrokeStartLineCapProperty);
set => SetValue(StrokeStartLineCapProperty, value);
}
public ICommand ClearCommand => new DelegateCommand(() => AssociatedObject.Children.Clear());
public ICommand SaveCommand => new DelegateCommand(OnSave);
public FrameworkElement TargetElement
{
get => (FrameworkElement)GetValue(TargetElementProperty);
set => SetValue(TargetElementProperty, value);
}
#endregion
#region Methods
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseDown += OnMouseDown;
AssociatedObject.MouseMove += OnMouseMove;
AssociatedObject.MouseUp += OnMouseUp;
}
private void OnMouseUp(object sender, MouseButtonEventArgs e)
{
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var pos = e.GetPosition(AssociatedObject);
//set the position of rectangle
var x = Math.Min(pos.X, _startPoint.X);
var y = Math.Min(pos.Y, _startPoint.Y);
//set the dimension of rectangle
var w = Math.Max(pos.X, _startPoint.X) - x;
var h = Math.Max(pos.Y, _startPoint.Y) - y;
var rectangle = new Rectangle
{
Stroke = Stroke,
StrokeThickness = Thickness,
StrokeDashCap = StrokeDashCap,
StrokeEndLineCap = StrokeEndLineCap,
StrokeStartLineCap = StrokeStartLineCap,
StrokeLineJoin = PenLineJoin.Round,
Width = w,
Height = h
};
AssociatedObject.Children.Clear();
AssociatedObject.Children.Add(rectangle);
Canvas.SetLeft(rectangle, x);
Canvas.SetTop(rectangle, y);
}
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(AssociatedObject);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseDown -= OnMouseDown;
AssociatedObject.MouseUp -= OnMouseMove;
AssociatedObject.MouseUp -= OnMouseUp;
}
private void OnSave()
{
if (TargetElement != null)
{
var rectangle = AssociatedObject.Children.OfType<Rectangle>().FirstOrDefault();
if (rectangle != null)
{
var bmp = new RenderTargetBitmap((int)TargetElement.ActualWidth, (int)TargetElement.ActualHeight, 96, 96, PixelFormats.Default);
bmp.Render(TargetElement);
var cropped = new CroppedBitmap(bmp, new Int32Rect((int)_startPoint.X, (int)_startPoint.Y, (int)rectangle.Width, (int)rectangle.Height));
using (var stream = new MemoryStream())
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(cropped));
encoder.QualityLevel = 100;
encoder.Save(stream);
SelectionCommand?.Execute(stream.ToArray());
}
}
}
}
#endregion
}
此应用程序使用Expression.Blend.Sdk.WPF和Prism.Core NuGet包。