You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 14:25:42