在MVVM模式下保存WPF应用程序中的屏幕区域

3

我是一名有用的助手,可以为您进行文本翻译。

我有一个遵循MVVM架构的WPF应用程序。

在其中一个屏幕上有一个图表,需要在单击保存按钮时将其保存为图像文件。

我可以使用下面的代码后台来保存图表:

        Rectangle rect = new Rectangle(0, 0, 100, 100);
        Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
        Graphics g = Graphics.FromImage(bmp);
        g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
        bmp.Save(fileName, ImageFormat.Jpeg);

但我需要能够在我的ViewModel中完成这个操作。

为此,我将ActualHeight和ActualWidth作为按钮的Click命令的参数传递,方式如下:

<Button Content="Save" Command="{Binding MyViewModel.SaveCommand">
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource DimensionConverter}">
             <Binding Path="ActualWidth" ElementName="MyChart"/>
             <Binding Path="ActualHeight" ElementName="MyChart"/>
        </MultiBinding>
    </Button.CommandParameter>
</Button>

在我的转换器中:

public class DimensionConverter : IMultiValueConverter
{
    public object Convert(object[] values, ...)
    {
        return values.Clone();
    }

    ...
}

然后,在命令执行逻辑中:
public void OnExecute(object parameter)
{
    var values = (object[])parameter;
    var width = (double)values[0];
    var height = (double)values[1];
    ...
}

但是,我也需要能够相对于原点传递图表控件的坐标。这可以使用PointToScreen方法来完成。PointToScreen。但是,在考虑MVVM模式时,如何将它们传递给视图模型呢?
或者,在MVVM体系结构中,有没有其他捕获屏幕区域并保存为图像的方法?

一种可能的实现方式是使用 Messanger 模式(例如 MVVM Light 框架中的 Messager),并从您的 View 发送一个带有要保存的位图的命令到 ViewModel。这样做仍然可以保持您的应用程序符合 MVVM 的方式。 - VitaliyK
@VitaliyK:你能给我提供一个链接或参考资料,让我可以获取更多信息吗? - Ankit
https://mallibone.com/post/mvvmlight-messenger - 作为例子 - VitaliyK
这与命令本身类似。我如何使用它传递位图? - Ankit
1个回答

2

还有其他方法可以在MVVM架构下捕获屏幕的某个区域并将其保存为图像吗?

视图可以实现一个接口,声明了视图模型需要了解的所有信息的属性:

public interface IView
{
    double Width { get; }
    double Height { get; }
    double X { get; }
    double Y { get; }
}

您可以将视图本身作为参数传递给视图模型的命令:
Command = new DelegateCommand<IView>(view => { /* do something */});

这并不会破坏MVVM模式,因为视图模型并没有直接引用视图。它只知道视图实现的某个接口。
该接口甚至可以声明方法。在某些情况下,仅视图或视图中的控件可能知道如何序列化自身,因此必须将此功能委托给视图,因为不能假定视图模型真正知道如何序列化控件。这是控件本身的实现细节。
但是,视图模型仍然必须启动序列化过程,这意味着它必须能够以某种方式与视图通信。使用接口是一种方法。另一种方法是使用信使或事件聚合器。如果要了解该概念,请参阅以下博客文章:https://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/

DelegateCommand在这里意味着它正在实现ICommand接口,对吗? - Ankit
是的,DelegateCommand是Prism对ICommand接口的实现:https://github.com/PrismLibrary/Prism/blob/master/Source/Prism/Commands/DelegateCommand.cs - mm8
我还有一个疑问,我如何将视图作为参数传递给视图模型的命令? - Ankit
通过将CommandParameter绑定到视图本身:<Binding Path="." RelativeSource="{RelativeSource AncestorType=Window}"/>。如果视图是UserControl,则将Window更改为UserControl。请记得接受答案并投票支持它,如果它对您有帮助。 - mm8
这个该如何进行单元测试呢?因为界面是在代码后台实现的,而命令逻辑则在视图模型中。 - Ankit
1
通常情况下,您不会对视图或任何与视图相关的功能进行单元测试。通过为视图模型提供接口的模拟实现,可以轻松地进行单元测试。但如果您有其他问题,请提出新的问题。 - mm8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接