Jus*_*nno 11
实际上比我最初想象的要困难。我创建了一个repo。结果是这样的。
您可以选择 Excel 中的任何元素,复制它们,聚焦 Blazor 页面的内容并将其粘贴。作为一个简单的视图,它显示在表格中。
让我们看一下解决方案。
索引剃刀
@page "/"
<div class="form-group">
<label for="parser">Parser type</label>
<select class="form-control" id="parser" @bind="_parserType">
<option value="text">Text</option>
<option value="html">HTML</option>
</select>
</div>
<PasteAwareComponent OnContentPasted="FillTable">
@if (_excelContent.Any() == false)
{
<p>No Content</p>
}
else
{
<table class="table table-striped">
@foreach (var row in _excelContent)
{
<tr>
@foreach (var cell in row)
{
<td>@cell</td>
}
</tr>
}
</table>
}
</PasteAwareComponent>
<button type="button" class="btn btn-primary" @onclick="@( () => _excelContent = new List<String[]>() )">Clear</button>
@code
{
private IList<String[]> _excelContent = new List<String[]>();
...more content, explained later...
}
Run Code Online (Sandbox Code Playgroud)
如果将 Excel 中的选定内容复制到剪贴板中,则不会复制单个文本,而是复制同一内容的多个表示形式。在我的实验中,它分为三种不同的类型。
我构建了两个不同的解析器:ExcelHtmlContentParser和ExcelTextContentParser。关于 Excel 中单元格内容的多种不同可能性,我的实现仅仅完成了,应该被视为一种启发。要查看两个解析器的运行情况,您可以通过更改选择框中的值在它们之间进行选择。
处理PasteAwareComponent与 Javascript 的交互。您可以在此组件中放置任何内容。如果该组件(或任何子组件)具有焦点,则粘贴事件将得到正确处理。
<span @ref="_reference">
@ChildContent
</span>
@code {
private ElementReference _reference;
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public EventCallback<IEnumerable<IDictionary<String, String>>> OnContentPasted { get; set; }
[JSInvokable("Pasted")]
public async void raisePasteEvent(IEnumerable<IDictionary<String, String>> items)
{
await OnContentPasted.InvokeAsync(items);
}
}
Run Code Online (Sandbox Code Playgroud)
该组件处理与 javascript 的互操作。一旦粘贴事件发生,就会EventCallback<IEnumerable<IDictionary<String, String>>> OnContentPasted被触发。
剪贴板内可能有多个元素。因此,我们需要处理一个集合IEnumerable<>。如上图所示,同一个剪贴板项目可以有多种表示形式。每个表示都有一个 mime 类型,如“text/plain”或“text/html”以及值。这由 表示,IDictionary<String, String>其中键是 mime 类型,值是内容。
在详细讨论 javascript 互操作之前,我们先回到组件Index。
<PasteAwareComponent OnContentPasted="FillTable">
...
</PasteAwareComponent>
@code {
private async Task FillTable(IEnumerable<IDictionary<String, String>> content)
{
if (content == null || content.Count() != 1)
{
return;
}
var clipboardContent = content.ElementAt(0);
IExcelContentParser parser = null;
switch (_parserType)
{
case "text":
parser = new ExcelTextContentParser();
break;
case "html":
parser = new ExcelHtmlContentParser();
break;
default:
break;
}
foreach (var item in clipboardContent)
{
if (parser.CanParse(item.Key) == false)
{
continue;
}
_excelContent = await parser.GetRows(item.Value);
}
}
}
Run Code Online (Sandbox Code Playgroud)
索引组件在方法中使用此事件回调 FillTable。该方法检查剪贴板中是否有一个元素。根据选择,选择解析器。如果所选解析器可以根据提供的 mime 类型解析每个表示,则在下一步中检查它。_excelContent 如果找到正确的解析器,解析器就会发挥其魔力,并更新字段的内容。因为是EventCallback StateHasChanged内部调用,并且视图更新。
文本解析器
在文本表示中,Excel 使用\r\n表示行尾,并使用 a\t表示每个单元格,甚至是空单元格。解析器逻辑非常简单。
public class ExcelTextContentParser : IExcelContentParser
{
public String ValidMimeType { get; } = "text/plain";
public Task<IList<String[]>> GetRows(String input) =>
Task.FromResult<IList<String[]>>(input.Split("\r\n", StringSplitOptions.RemoveEmptyEntries).Select(x =>
x.Split("\t").Select(y => y ?? String.Empty).ToArray()
).ToList());
}
Run Code Online (Sandbox Code Playgroud)
我还没有测试如果内容更复杂的话这种行为会如何变化。我认为 HTML 表示更稳定。因此,第二个解析器。
HTML 解析器
HTML 表示形式是一个表格。与<tr>和<td>. 我使用AngleSharp库作为 HTML 解析器。
public class ExcelHtmlContentParser : IExcelContentParser
{
public String ValidMimeType { get; } = "text/html";
public async Task<IList<String[]>> GetRows(String input)
{
var context = BrowsingContext.New(Configuration.Default);
var document = await context.OpenAsync(reg => reg.Content(input));
var element = document.QuerySelector<IHtmlTableElement>("table");
var result = element.Rows.Select(x => x.Cells.Select(y => y.TextContent).ToArray()).ToList();
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
我们将剪贴板内容作为 HTML 文档加载,获取表格并迭代所有行,并选择每一列。
** js 互操作 ***
@inject IJSRuntime runtime
@implements IDisposable
<span @ref="_reference">
@ChildContent
</span>
@code {
private ElementReference _reference;
private DotNetObjectReference<PasteAwareComponent> _objectReference;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender == true)
{
_objectReference = DotNetObjectReference.Create(this);
await runtime.InvokeVoidAsync("BlazorClipboadInterop.ListeningForPasteEvents", new Object[] { _reference, _objectReference });
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
if (_objectReference != null)
{
_objectReference.Dispose();
}
}
}
Run Code Online (Sandbox Code Playgroud)
该PasteAwareComponent组件覆盖OnAfterRenderAsync生命周期,以调用 js 互操作方法。一定是OnAfterRenderAsync因为之前 HTML 引用不存在,我们需要引用来添加粘贴事件侦听器。当粘贴事件发生时,JavaScript 必须调用这个对象,因此我们需要创建一个DotNetObjectReference实例。我们实现了IDisposable接口并正确处理了引用以防止内存泄漏。
最后一部分是 javascript 部分本身。我创建了一个名为 的文件clipboard-interop.js并将其放置在 wwwroot/js 文件夹中。
@page "/"
<div class="form-group">
<label for="parser">Parser type</label>
<select class="form-control" id="parser" @bind="_parserType">
<option value="text">Text</option>
<option value="html">HTML</option>
</select>
</div>
<PasteAwareComponent OnContentPasted="FillTable">
@if (_excelContent.Any() == false)
{
<p>No Content</p>
}
else
{
<table class="table table-striped">
@foreach (var row in _excelContent)
{
<tr>
@foreach (var cell in row)
{
<td>@cell</td>
}
</tr>
}
</table>
}
</PasteAwareComponent>
<button type="button" class="btn btn-primary" @onclick="@( () => _excelContent = new List<String[]>() )">Clear</button>
@code
{
private IList<String[]> _excelContent = new List<String[]>();
...more content, explained later...
}
Run Code Online (Sandbox Code Playgroud)
我们使用 HTML 引用来注册“粘贴”事件的事件侦听器。在处理方法中,我们创建传递给 C# 方法的对象。
<span @ref="_reference">
@ChildContent
</span>
@code {
private ElementReference _reference;
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public EventCallback<IEnumerable<IDictionary<String, String>>> OnContentPasted { get; set; }
[JSInvokable("Pasted")]
public async void raisePasteEvent(IEnumerable<IDictionary<String, String>> items)
{
await OnContentPasted.InvokeAsync(items);
}
}
Run Code Online (Sandbox Code Playgroud)
当我们使用js互操作时,我们应该使用易于序列化的对象。对于真正的 blob(例如图像),它将是基于 64 编码的字符串,否则只是内容。
该解决方案使用了这些navigator.clipboard功能。用户需要允许它。因此我们看到了对话框。
| 归档时间: |
|
| 查看次数: |
1893 次 |
| 最近记录: |