如何执行XAML转换(例如整个网格或视图框)到png文件?
我需要从视图模型级别执行此操作。
链接到我无法在 ViewModel 中调用的示例函数,因为我无权访问该对象。
有没有一种简单而愉快的方法?
视图将负责根据您链接到的答案实际导出您在屏幕上看到的元素。
不过,视图模型应该初始化操作。它可以通过多种不同的方式做到这一点。
一种选择是使用事件聚合器或信使将松散耦合的事件或消息发送到视图。有关主题的更多信息,请参阅以下博客文章:http://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/。
另一种选择是注入视图模型,并带有对视图的松散引用。视图实现一个接口,并使用构造函数注入或属性注入将自身注入到视图模型中,例如:
public interface IExport
{
void Export(string filename);
}
public partial class MainWindow : Window, IExport
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel(this);
}
public void Export(string filename)
{
//export...
}
}
public class ViewModel
{
private readonly IExport _export;
public ViewModel(IExport export)
{
_export = export;
}
public void DoExport()
{
//...
_export.Export("pic.png");
}
}
这样,视图模型只知道接口并在接口上具有属性。它不依赖于视图,在您的单元测试中,您可以轻松地提供IExport
接口的模拟实现。
但是,视图模型将并且永远不应该访问要导出的实际元素。这些属于视图。
你需要类似交互的东西 - 一种让 VM 从视图中获取某些内容的方法。如果您不想为此安装全新的框架,只需使用 Func 属性:
你的虚拟机:
public Func<string, Bitmap> GetBitmapOfElement {get;set;}
...
//in some command
var bmp = GetBitmapOfElement("elementName");
然后,在您的视图中,您已为该属性分配了一些内容:
ViewModel.GetBitmapOfElement = elementName =>
{
var uiElement = FindElementByName(elementName); // this part you have figure out or just always use the same element
return ExportToPng(FrameworkElement element); // this is the function form the link form your answer modified to return the bitmap instead of saving it to file
}
如果需要异步,只需将属性类型更改为Func<string, Task<Bitmap>>
并在视图中分配异步函数
依赖属性呢?考虑以下用于传递数据的类(数据可以是流或任何您想要的内容):
public class Requester
{
public event Action DataRequested;
public object Data { get; set; }
public void RequestData() => DataRequested?.Invoke();
}
然后,创建一个用户控件并注册Requester类型的依赖项属性:
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty RequesterProperty
= DependencyProperty.Register("Requester", typeof(Requester), typeof(MainWindow),
new PropertyMetadata(default(Requester), OnRequesterChanged));
public MyUserControl()
{
InitializeComponent();
}
public Requester Requester
{
get => (Requester) GetValue(RequesterProperty);
set => SetValue(RequesterProperty, value);
}
private static void OnRequesterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> ((Requester) e.NewValue).DataRequested += ((MyUserControl) d).OnDataRequested;
private void OnDataRequested()
{
Requester.Data = "XD";
}
}
您的视图模型如下所示:
public class MainWindowViewModel
{
public Requester Requester { get; } = new Requester();
public void RequestData() => Requester.RequestData();
}
在 XAML 中,只需将依赖项属性从控件绑定到视图模型中的属性:
<Window x:Class="Test.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:Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<local:MyUserControl Requester="{Binding Requester}"/>
</Grid>
</Window>