无法在 Blazor 中移动 Div
Cannot Move Divs in Blazor
我有一个带有以下 DIV 标签的 Blazor 服务器应用程序
<div class=mainScreen id="outerBox" style="width:@(TotalWidth)px;height:@(TotalHeight)px;">
foreach(Device thisDevice in MyDevices)
{
<div class=column id="@MainDiv" style="width:@(thisDevice.Width)px;height:@(thisDevice.Height)px;left:@thisDevice.XCoordinate;top:@thisDevice.YCoordinate">
Main Content Here...
</div>
}
</div>
我尝试使用此页面中的代码示例设置高度、宽度和 X/Y 坐标 - https://blazor.tips/blazor-how-to-ready-window-dimensions/ 但无论我将其放置在何处,它都没有用,只是抛出了一个未捕获的异常 Try ...块。
然后我转向更直接的 JS 调用:
await Task.Run(async () =>
{
//Commenting out the OnInitializeAsync makes no difference but needs to be commented out when embedded
//On the main component
await this.OnInitializedAsync();
string data = await JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { });
JObject offsets = (JObject)JsonConvert.DeserializeObject(data);
TotalHeight = offsets.Value<int>("height");
TotalHeight = offsets.Value<int>("width");
}
//In my JS file, the function looks as follows:
function getMyWindow() {
var obj = {};
obj.width = window.width;
obj.height = window.height;
return JSON.stringify(obj);
}
如果我直接在代码中进行此调用,则什么也不会发生 - 即使 OnInitializeAsync 已被注释掉。
var result = SetDimensions().Result;
如果我将此方法放在 OnAfterRendor 方法中:
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
if (!SetTheDivs)
SetTheDivs = SetDimensions().Result;
StateHasChanged();
}
}
protected override void OnInitialized()
{
base.OnInitialized();
this.OnAfterRender(true);
}
一切都挂起,直到我终止该项目。从来没有任何错误,但是当我在高度或宽度语句上放置断点时,代码永远不会 运行s。
我什至在异步版本中添加也无济于事:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await SetDimensions();
StateHasChanged();
}
}
protected override async Task OnInitializedAsync()
{
await this.OnAfterRenderAsync(true);
}
与所有内容挂起的结果相同。我完全不知道如何继续,我真的需要一些帮助!
为了清楚起见,是对 JS 的调用导致了挂起:
string data = await JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { });
我添加了一些警报,但它们从来没有 运行:
function getMyWindow() {
var obj = {};
alert("hi");
obj.width = screen.width;
obj.height = screen.height;
alert("ho");
return JSON.stringify(obj);
}
感谢您的宝贵时间!
顺便说一句 - 我确实将双重等待更改为 string data = JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { }).Result;
更新:我将 JS 调用完全移到了 await 之外,我得到了错误:
InvalidOperationException:JavaScript 此时无法发出互操作调用。这是因为组件是静态渲染的。启用预呈现后,JavaScript 互操作调用只能在 OnAfterRenderAsync 生命周期方法期间执行。
在这种情况下,我实际上是从 OnAfterRenderAsync 方法调用方法:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnInitializedAsync();
if (firstRender)
{
await SetDimensions();
StateHasChanged();
}
}
不确定您想要什么...复制下面的代码并运行它,然后告诉我们这是否是您想要的。
Index.razor
@page "/"
<div class=mainScreen id="outerBox" style="width:@($"{TotalWidth}px");height:@($"{TotalHeight}px"); background-color: green; top:60px; position:absolute">
@foreach (Device device in devices)
{
<div class=column style="width:@($"{device.Width}px");height:@($"{device.Height}px");margin-left:@($"{device.Left}px");margin-top:@($"{device.Top}px"); background-color:aliceblue">
@device.Name: Main Content Here...
</div>
}
</div>
@code {
private int TotalWidth = 520;
private int TotalHeight = 530;
private IList<Device> devices = Enumerable.Range(1, 5).Select(i => new Device { Name = $"Name {i}", Width = 520, Height = 100, Left = 0, Top = 5 }).ToList();
public class Device
{
public string Name { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
}
注意:OnInitialized{Async}
对方法是基础的生命周期方法 class ComponentBase.
它们在创建 Razor 组件时由 Blazor 框架自动调用.它们只执行一次。您可以覆盖它们,并添加您的逻辑,但您永远不应该从您的代码中手动调用它们。
这个:
protected override async Task OnInitializedAsync()
{
await this.OnAfterRenderAsync(true);
}
这是错误的,绝不能这样做。您不应该调用 OnAfterRender{Async}.
应该调用 OnAfterRender{Async}
的是 Blazor 框架,而不是开发人员。你能试着理解你的代码在做什么吗...
尝试了解虽然 Razor 组件定义为 C# classes,但它们是 类 的特例,需要框架进行特殊处理...
更新
Ken Tola,我相信以下代码可以满足您的需求。它读取 window 对象的宽度和高度,将其传递给 Index 组件,并重新定位您亲爱的 divs。请注意,在应用重新定位 div 之前,我检查了宽度和高度的值,并确定了 div 的尺寸。这当然是为了演示目的而完成的,您可以根据需要操作这些值...
Index.razor
@page "/"
@implements IDisposable
@inject IJSRuntime JSRuntime
<div class=mainScreen id="outerBox" style="width:@($" {TotalWidth}px");height:@($"{TotalHeight}px"); background-color: green; top:60px; position:absolute">
@foreach (Device device in devices)
{
<div class=column style="width:@($" {device.Width}px");height:@($"{device.Height}px");margin-left:@($"{device.Left}px");margin-top:@($"{device.Top}px"); background-color:aliceblue">
@device.Name: Main Content Here...
</div>
}
</div>
@code
{
private DotNetObjectReference<BrowserService> objRef;
private BrowserService BSS;
private int TotalWidth;
private int TotalHeight;
private IList<Device> devices = Enumerable.Range(1, 5).Select(i => new Device { Name = $"Name {i}", Width = 520, Height = 100, Left = 0, Top = 5 }).ToList();
public class Device
{
public string Name { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
protected override void OnInitialized()
{
BSS = new BrowserService();
objRef = DotNetObjectReference.Create(BSS);
BSS.Notify += OnNotify;
}
public void Dispose()
{
BSS.Notify -= OnNotify;
objRef?.Dispose();
}
public async Task OnNotify()
{
// Note that the notifier only notify your component
// that data is ready, and that the dimensions are read
// from a property. You can instead define event handler
// that pass the data in the form of EventArgs...
TotalWidth = BSS.Dimension.Width >= 877 ? 520 : 550;
TotalHeight = BSS.Dimension.Height >= 550 ? 800 : 1200;
await InvokeAsync(() => StateHasChanged());
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// This code is excuted only once, in order to initialize
// the JavaScript objects
if (firstRender)
{
await JSRuntime.InvokeAsync<object>
("myJsFunctions.getDimensions", objRef);
}
}
}
BrowserService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class BrowserService
{
public event Func<Task> Notify;
#nullable enable
public Dimension? Dimension { get; set; }
#nullable disable
[JSInvokableAttribute("GetDimensions")]
public async Task GetDimensions(string dimension)
{
JsonSerializerOptions options = new(JsonSerializerDefaults.Web)
{
WriteIndented = true
};
var _dimension = System.Text.Json.JsonSerializer.Deserialize(dimension, typeof(Dimension), options);
Dimension = (Dimension)_dimension;
if (Notify != null)
{
await Notify?.Invoke();
}
}
}
public class Dimension
{
public int Width { get; set; }
public int Height { get; set; }
}
}
Startup.ConfigureServices
services.AddScoped<BrowserService>();
_Host.cshtml
<script src="_framework/blazor.server.js"></script>
<script type="text/javascript">
window.myJsFunctions = {
getDimensions: function (dotnetHelper) {
var dimension = {
width: window.innerWidth,
height: window.innerHeight
};
var json = JSON.stringify(dimension);
return dotnetHelper.invokeMethodAsync('GetDimensions', json);
}
};
</script>
注意:考虑在 window 调整大小时处理 div 元素的重定位。它应该是响应式的,对吧?不确定您的情况是否可以使用媒体查询。无论如何,如您所见,我设计代码的方式考虑到您的 div 元素可能需要一次又一次地重新定位,因此它会不断(在调整大小时)通知您的 Index 组件变化的维度。我想这值得提出一个新问题......
我有一个带有以下 DIV 标签的 Blazor 服务器应用程序
<div class=mainScreen id="outerBox" style="width:@(TotalWidth)px;height:@(TotalHeight)px;">
foreach(Device thisDevice in MyDevices)
{
<div class=column id="@MainDiv" style="width:@(thisDevice.Width)px;height:@(thisDevice.Height)px;left:@thisDevice.XCoordinate;top:@thisDevice.YCoordinate">
Main Content Here...
</div>
}
</div>
我尝试使用此页面中的代码示例设置高度、宽度和 X/Y 坐标 - https://blazor.tips/blazor-how-to-ready-window-dimensions/ 但无论我将其放置在何处,它都没有用,只是抛出了一个未捕获的异常 Try ...块。
然后我转向更直接的 JS 调用:
await Task.Run(async () =>
{
//Commenting out the OnInitializeAsync makes no difference but needs to be commented out when embedded
//On the main component
await this.OnInitializedAsync();
string data = await JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { });
JObject offsets = (JObject)JsonConvert.DeserializeObject(data);
TotalHeight = offsets.Value<int>("height");
TotalHeight = offsets.Value<int>("width");
}
//In my JS file, the function looks as follows:
function getMyWindow() {
var obj = {};
obj.width = window.width;
obj.height = window.height;
return JSON.stringify(obj);
}
如果我直接在代码中进行此调用,则什么也不会发生 - 即使 OnInitializeAsync 已被注释掉。
var result = SetDimensions().Result;
如果我将此方法放在 OnAfterRendor 方法中:
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
if (!SetTheDivs)
SetTheDivs = SetDimensions().Result;
StateHasChanged();
}
}
protected override void OnInitialized()
{
base.OnInitialized();
this.OnAfterRender(true);
}
一切都挂起,直到我终止该项目。从来没有任何错误,但是当我在高度或宽度语句上放置断点时,代码永远不会 运行s。
我什至在异步版本中添加也无济于事:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await SetDimensions();
StateHasChanged();
}
}
protected override async Task OnInitializedAsync()
{
await this.OnAfterRenderAsync(true);
}
与所有内容挂起的结果相同。我完全不知道如何继续,我真的需要一些帮助!
为了清楚起见,是对 JS 的调用导致了挂起:
string data = await JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { });
我添加了一些警报,但它们从来没有 运行:
function getMyWindow() {
var obj = {};
alert("hi");
obj.width = screen.width;
obj.height = screen.height;
alert("ho");
return JSON.stringify(obj);
}
感谢您的宝贵时间!
顺便说一句 - 我确实将双重等待更改为 string data = JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { }).Result;
更新:我将 JS 调用完全移到了 await 之外,我得到了错误:
InvalidOperationException:JavaScript 此时无法发出互操作调用。这是因为组件是静态渲染的。启用预呈现后,JavaScript 互操作调用只能在 OnAfterRenderAsync 生命周期方法期间执行。
在这种情况下,我实际上是从 OnAfterRenderAsync 方法调用方法:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnInitializedAsync();
if (firstRender)
{
await SetDimensions();
StateHasChanged();
}
}
不确定您想要什么...复制下面的代码并运行它,然后告诉我们这是否是您想要的。
Index.razor
@page "/"
<div class=mainScreen id="outerBox" style="width:@($"{TotalWidth}px");height:@($"{TotalHeight}px"); background-color: green; top:60px; position:absolute">
@foreach (Device device in devices)
{
<div class=column style="width:@($"{device.Width}px");height:@($"{device.Height}px");margin-left:@($"{device.Left}px");margin-top:@($"{device.Top}px"); background-color:aliceblue">
@device.Name: Main Content Here...
</div>
}
</div>
@code {
private int TotalWidth = 520;
private int TotalHeight = 530;
private IList<Device> devices = Enumerable.Range(1, 5).Select(i => new Device { Name = $"Name {i}", Width = 520, Height = 100, Left = 0, Top = 5 }).ToList();
public class Device
{
public string Name { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
}
注意:OnInitialized{Async}
对方法是基础的生命周期方法 class ComponentBase.
它们在创建 Razor 组件时由 Blazor 框架自动调用.它们只执行一次。您可以覆盖它们,并添加您的逻辑,但您永远不应该从您的代码中手动调用它们。
这个:
protected override async Task OnInitializedAsync()
{
await this.OnAfterRenderAsync(true);
}
这是错误的,绝不能这样做。您不应该调用 OnAfterRender{Async}.
应该调用 OnAfterRender{Async}
的是 Blazor 框架,而不是开发人员。你能试着理解你的代码在做什么吗...
尝试了解虽然 Razor 组件定义为 C# classes,但它们是 类 的特例,需要框架进行特殊处理...
更新
Ken Tola,我相信以下代码可以满足您的需求。它读取 window 对象的宽度和高度,将其传递给 Index 组件,并重新定位您亲爱的 divs。请注意,在应用重新定位 div 之前,我检查了宽度和高度的值,并确定了 div 的尺寸。这当然是为了演示目的而完成的,您可以根据需要操作这些值...
Index.razor
@page "/"
@implements IDisposable
@inject IJSRuntime JSRuntime
<div class=mainScreen id="outerBox" style="width:@($" {TotalWidth}px");height:@($"{TotalHeight}px"); background-color: green; top:60px; position:absolute">
@foreach (Device device in devices)
{
<div class=column style="width:@($" {device.Width}px");height:@($"{device.Height}px");margin-left:@($"{device.Left}px");margin-top:@($"{device.Top}px"); background-color:aliceblue">
@device.Name: Main Content Here...
</div>
}
</div>
@code
{
private DotNetObjectReference<BrowserService> objRef;
private BrowserService BSS;
private int TotalWidth;
private int TotalHeight;
private IList<Device> devices = Enumerable.Range(1, 5).Select(i => new Device { Name = $"Name {i}", Width = 520, Height = 100, Left = 0, Top = 5 }).ToList();
public class Device
{
public string Name { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
protected override void OnInitialized()
{
BSS = new BrowserService();
objRef = DotNetObjectReference.Create(BSS);
BSS.Notify += OnNotify;
}
public void Dispose()
{
BSS.Notify -= OnNotify;
objRef?.Dispose();
}
public async Task OnNotify()
{
// Note that the notifier only notify your component
// that data is ready, and that the dimensions are read
// from a property. You can instead define event handler
// that pass the data in the form of EventArgs...
TotalWidth = BSS.Dimension.Width >= 877 ? 520 : 550;
TotalHeight = BSS.Dimension.Height >= 550 ? 800 : 1200;
await InvokeAsync(() => StateHasChanged());
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// This code is excuted only once, in order to initialize
// the JavaScript objects
if (firstRender)
{
await JSRuntime.InvokeAsync<object>
("myJsFunctions.getDimensions", objRef);
}
}
}
BrowserService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class BrowserService
{
public event Func<Task> Notify;
#nullable enable
public Dimension? Dimension { get; set; }
#nullable disable
[JSInvokableAttribute("GetDimensions")]
public async Task GetDimensions(string dimension)
{
JsonSerializerOptions options = new(JsonSerializerDefaults.Web)
{
WriteIndented = true
};
var _dimension = System.Text.Json.JsonSerializer.Deserialize(dimension, typeof(Dimension), options);
Dimension = (Dimension)_dimension;
if (Notify != null)
{
await Notify?.Invoke();
}
}
}
public class Dimension
{
public int Width { get; set; }
public int Height { get; set; }
}
}
Startup.ConfigureServices
services.AddScoped<BrowserService>();
_Host.cshtml
<script src="_framework/blazor.server.js"></script>
<script type="text/javascript">
window.myJsFunctions = {
getDimensions: function (dotnetHelper) {
var dimension = {
width: window.innerWidth,
height: window.innerHeight
};
var json = JSON.stringify(dimension);
return dotnetHelper.invokeMethodAsync('GetDimensions', json);
}
};
</script>
注意:考虑在 window 调整大小时处理 div 元素的重定位。它应该是响应式的,对吧?不确定您的情况是否可以使用媒体查询。无论如何,如您所见,我设计代码的方式考虑到您的 div 元素可能需要一次又一次地重新定位,因此它会不断(在调整大小时)通知您的 Index 组件变化的维度。我想这值得提出一个新问题......