C# 使用MVVM裁剪图像

C# 使用MVVM裁剪图像,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,我有一个程序,用户可以通过鼠标或文本框裁剪选定区域。现在我只能通过文本框进行裁剪。因为我正在实现MVVM,所以我不知道如何在我的ViewModel中实现裁剪功能。我已经能够在图像上绘制矩形了,我在后面的代码中实现了这一点。 我现在的问题是,如何使用从视图代码中获得的值在ViewModel中实现裁剪功能 我的Xaml(视图) 我遇到了各种post线程,但没有一个能真正帮助我。您可以创建一个混合行为来实现裁剪操作,并为该行为提供一个可绑定的ICommand属性,您可以在MouseUp上调用该属性(可

我有一个程序,用户可以通过鼠标或文本框裁剪选定区域。现在我只能通过文本框进行裁剪。因为我正在实现MVVM,所以我不知道如何在我的ViewModel中实现裁剪功能。我已经能够在图像上绘制矩形了,我在后面的代码中实现了这一点。 我现在的问题是,如何使用从视图代码中获得的值在ViewModel中实现裁剪功能

我的Xaml(视图)


我遇到了各种post线程,但没有一个能真正帮助我。

您可以创建一个混合行为来实现裁剪操作,并为该行为提供一个可绑定的ICommand属性,您可以在MouseUp上调用该属性(可能还有一些其他条件)。然后,可以将ICommand属性(来自viewmodel)绑定到此属性,以便在裁剪完成时向viewmodel传递所需的值

以下是一个“粗略”示例,用于裁剪图像并保存裁剪后的图像:

视图模型

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);
    });
}
公共类ShellViewModel:BindableBase
{
公共字符串标题=>“示例”;
公共ICommand SelectionCommand=>new DelegateCommand(缓冲区=>
{
var path=@“C:\temp\output.bmp”;
writealBytes(路径、缓冲区);
进程启动(路径);
});
}
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
}
公共类裁剪行为:行为
{
#区域字段
public dependencProperty SelectionCommandProperty=dependencProperty.Register(name of(SelectionCommand)、typeof(ICommand)、typeof(cropingbehavior));
public DependencyProperty StrokeProperty=DependencyProperty.Register(笔划名称、画笔类型、裁剪行为类型、新属性元数据(画笔.品红));
public dependencProperty thicknesproperty=dependencProperty.Register(name of(Thickness)、typeof(double)、typeof(cropingbehavior)、new PropertyMetadata(2d));
public dependencProperty StrokeDashCapProperty=dependencProperty.Register(name of(StrokeDashCap)、typeof(PenLineCap)、typeof(cropingbehavior)、new PropertyMetadata(PenLineCap.Round));
public dependencProperty StrokeEndLineCapProperty=dependencProperty.Register(name of(StrokeEndLineCap)、typeof(PenLineCap)、typeof(cropingbehavior)、new PropertyMetadata(PenLineCap.Round));
public DependencyProperty StrokeStartLineCapProperty=DependencyProperty.Register(名称(StrokeStartLineCap)、类型(PenLineCap)、类型(裁剪行为)、新属性元数据(PenLineCap.Round));
public dependencProperty TargetElementProperty=dependencProperty.Register(name of(TargetElement)、typeof(FrameworkElement)、typeof(cropingBehavior));
私人点(startPoint);;
#端区
#区域属性
公共ICommand SelectionCommand
{
get=>(ICommand)GetValue(SelectionCommandProperty);
set=>SetValue(SelectionCommandProperty,value);
}
公共画笔
{
get=>(画笔)GetValue(StrokeProperty);
set=>SetValue(StrokeProperty,value);
}
公共双层厚度
{
get=>(双精度)GetValue(ThicknessProperty);
set=>SetValue(ThicknessProperty,value);
}
公共PenLineCap StrokeDashCap
{
get=>(PenLineCap)GetValue(StrokeDashCapProperty);
set=>SetValue(StrokeDashCapProperty,value);
}
公共笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒笔筒
{
get=>(PenLineCap)GetValue(StrokeEndLineCapProperty);
set=>SetValue(StrokeEndLineCapProperty,value);
}
公共笔头笔头笔头笔头笔头笔头笔头笔头笔头
{
get=>(PenLineCap)GetValue(StrokeStartLineCapProperty);
set=>SetValue(StrokeStartLineCapProperty,值);
}
public ICommand ClearCommand=>newdelegatecommand(()=>AssociatedObject.Children.Clear());
公共ICommand SaveCommand=>newdelegateCommand(OnSave);
公共框架元素TargetElement
{
get=>(FrameworkElement)GetValue(TargetElementProperty);
set=>SetValue(TargetElementProperty,value);
}
#端区
#区域方法
受保护的覆盖无效附加()
{
base.onatached();
AssociatedObject.MouseDown+=OnMouseDown;
AssociatedObject.MouseMove+=OnMouseMove;
AssociatedObject.MouseUp+=OnMouseUp;
}
MouseUp上的私有void(对象发送器,鼠标按钮ventargs e)
{
}
MouseMove上的私有void(对象发送方,MouseEventArgs e)
{
如果(e.LeftButton==鼠标按钮状态。按下)
{
var pos=e.GetPosition(关联对象);
//设置矩形的位置
var x=数学最小值(位置x,起点x);
变量y=数学最小值(位置y,起点y);
//设置矩形的尺寸
var w=数学最大值(位置X,起点X)-X;
var h=数学最大值(位置Y,起点Y)-Y;
变量矩形=新矩形
{
冲程=冲程,
冲程厚度=厚度,
StrokeDashCap=StrokeDashCap,
StrokeEndLineCap=StrokeEndLineCap,
StrokeStartLineCap=StrokeStartLineCap,
StrokeLineJoin=PenLineJoin.Round,
宽度=w,
高度=h
};
AssociatedObject.Children.Clear();
AssociatedObject.Children.Add(矩形);
Canvas.SetLeft(矩形,x);
Canvas.SetTop(矩形,y);
}
}
mousedown上的私有void(对象发送器,鼠标按钮ventargs e)
{
_startPoint=e.GetPosition(关联对象);
}
附加时受保护的覆盖无效()
{
base.OnDetaching();
AssociatedObject.MouseDown-=OnMouseDown;
AssociatedObject.MouseUp-=OnMouseMove;
AssociatedObject.MouseUp
<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
}