Teams Toolkit Blazor多标签页下Resize事件无法跨页使用求助
问题:Teams Toolkit标签页应用中实现Resize事件全局注册
我正在尝试在切换标签页时重新注册Resize事件,当前实现存在重复绑定或响应异常的问题,希望实现全局唯一的Resize事件监听,确保所有标签页都能正确响应窗口大小变化。
现有实现代码
1. JavaScript代码
window.resizeListener = function (dotnethelper) { $(window).resize(() => { let browserWidth = $(window).innerWidth(); let browserHeight = $(window).innerHeight(); dotnethelper.invokeMethodAsync('SetBrowserDimensions', browserWidth, browserHeight).then(() => { // success, do nothing }).catch(error => { console.log("Error during browser resize: " + error); }); }); } window.getWindowDimensions = function () { return { width: window.innerWidth, height: window.innerHeight }; };
2. C# BrowserService类
using Microsoft.JSInterop; namespace DevBlazor.Data { public class BrowserService { private IJSRuntime JS = null; public event EventHandler<GetDimensions> Resize; public async void Init(IJSRuntime js) { // enforce single invocation if (JS == null) { this.JS = js; _ = await JS.InvokeAsync<string>("resizeListener", DotNetObjectReference.Create(this)); } } [JSInvokable] public void SetBrowserDimensions(int jsBrowserWidth, int jsBrowserHeight) { // For simplicity, we're just using the new width this.Resize?.Invoke(this, new GetDimensions() { Width = jsBrowserWidth, Height = jsBrowserHeight }); } } public class GetDimensions { public int Width { get; set; } public int Height { get; set; } } }
3. 服务注册(Program.cs)
builder.Services.AddSingleton<BrowserService>();
4. 标签页组件使用代码
private static Boolean bigWindowSize = true; protected void UpdatedBrowserWidth(object sender, GetDimensions getDimensions) { wd = getDimensions; Console.WriteLine("Width: " + wd.Width + Environment.NewLine + "Height: " + wd.Height); if (wd.Width >= wd.Height) { bigWindowSize = true; base.StateHasChanged(); } else { bigWindowSize = false; base.StateHasChanged(); } } async Task CreateSizeDimensions() { //just used to create an initial non-null starting value wd = await JsRuntime.InvokeAsync<GetDimensions>("getWindowDimensions"); Console.WriteLine("Init Width: " + wd.Width + Environment.NewLine + "Init Height: " + wd.Height); if (wd.Width >= wd.Height) bigWindowSize = true; else bigWindowSize = false; Browser.Init(JsRuntime); Browser.Resize += UpdatedBrowserWidth; }
5. 组件初始化代码
protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); if (firstRender) { isInTeams = await MicrosoftTeams.IsInTeams(); if (isInTeams) { await CreateSizeDimensions(); } StateHasChanged(); } }
已尝试的方法
- 添加IDisposable接口
- 尝试通过TabConfig.razor全局注册,但无法获取调用变量
- 在Welcome.razor中将主变量设为静态供其他标签页使用
- 将每个标签页相关主变量设为私有静态,但无法调用全局页面
解决方案
1. 优化BrowserService:实现IDisposable并管理对象引用
修改服务类,确保DotNetObjectReference正确释放,同时保证初始化逻辑安全:
using Microsoft.JSInterop; using System; namespace DevBlazor.Data { public class BrowserService : IDisposable { private IJSRuntime _jsRuntime; private DotNetObjectReference<BrowserService> _dotNetRef; public event EventHandler<GetDimensions> Resize; public async Task Init(IJSRuntime jsRuntime) { if (_jsRuntime != null) return; _jsRuntime = jsRuntime; _dotNetRef = DotNetObjectReference.Create(this); await _jsRuntime.InvokeVoidAsync("resizeListener", _dotNetRef); } [JSInvokable] public void SetBrowserDimensions(int width, int height) { Resize?.Invoke(this, new GetDimensions { Width = width, Height = height }); } public void Dispose() { _dotNetRef?.Dispose(); } } public class GetDimensions { public int Width { get; set; } public int Height { get; set; } } }
2. 全局初始化服务:避免重复绑定事件
在根组件(如App.razor)中统一初始化BrowserService,确保resize事件只绑定一次:
@inject BrowserService BrowserService @inject IJSRuntime JsRuntime @implements IAsyncDisposable <!-- 根组件内容 --> @code { protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await BrowserService.Init(JsRuntime); } } public async ValueTask DisposeAsync() { (BrowserService as IDisposable)?.Dispose(); } }
3. 标签页组件:仅订阅/取消订阅事件
每个标签页组件在生命周期内管理事件订阅,切换时自动清理:
@inject BrowserService BrowserService @inject IJSRuntime JsRuntime @implements IDisposable <!-- 标签页内容 --> @code { private GetDimensions _currentDimensions; private bool _isBigWindow = true; protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); if (firstRender) { bool isInTeams = await MicrosoftTeams.IsInTeams(); if (isInTeams) { // 初始化获取窗口尺寸 _currentDimensions = await JsRuntime.InvokeAsync<GetDimensions>("getWindowDimensions"); _isBigWindow = _currentDimensions.Width >= _currentDimensions.Height; // 订阅Resize事件 BrowserService.Resize += OnWindowResized; } StateHasChanged(); } } private void OnWindowResized(object sender, GetDimensions dimensions) { _currentDimensions = dimensions; _isBigWindow = dimensions.Width >= dimensions.Height; StateHasChanged(); } public void Dispose() { // 取消订阅,防止内存泄漏 BrowserService.Resize -= OnWindowResized; } }
4. 优化JS代码:确保全局仅绑定一次resize事件
修改JS代码,添加全局标记避免重复绑定:
window._resizeListenerBound = false; window.resizeListener = function (dotnetHelper) { if (window._resizeListenerBound) return; $(window).resize(() => { const width = $(window).innerWidth(); const height = $(window).innerHeight(); dotnetHelper.invokeMethodAsync('SetBrowserDimensions', width, height) .catch(error => console.error("Resize error:", error)); }); window._resizeListenerBound = true; } window.getWindowDimensions = function () { return { width: window.innerWidth, height: window.innerHeight }; };
核心改进点
- 全局唯一监听:通过根组件初始化服务,结合JS全局标记,确保resize事件仅绑定一次
- 生命周期管理:组件实现IDisposable,订阅/取消订阅事件,避免内存泄漏
- 对象引用安全:BrowserService实现IDisposable,正确释放DotNetObjectReference
内容的提问来源于stack exchange,提问作者JuniorDev
相关产品推荐
相关产品推荐

