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

手动创建WPF UserControl时SizeChanged事件未触发,如何解决?

WPF UserControl SizeChanged事件未触发的解决方法

问题回顾

我有一个简单的WPF UserControl基类:

public class ControlBase : UserControl { 
    public ControlBase() { 
        this.SizeChanged += new SizeChangedEventHandler(OnSizeChanged); 
    } 
    private void OnSizeChanged(object sender, SizeChangedEventArgs e) { 
        // Doing something here 
    } 
}

但手动创建实例后修改Width/Height属性时,不会触发SizeChanged事件:

ControlBase ctrl = new ControlBase(); 
ctrl.Width = 100; // <--- no event fired

我的应用同时使用WinForm和WPF的UserControl,希望二者表现一致(WinForm控件修改Size相关属性会正常触发SizeChanged),该如何配置WPF UserControl实现相同行为?

原因分析

WPF和WinForm的SizeChanged事件触发逻辑本质不一样:

  • WinForm的SizeChanged是属性值变更即触发,只要修改了Width/Height(或者Size),事件就会立刻响应。
  • 而WPF的SizeChanged是控件实际渲染尺寸变更时才触发——只有当控件被加入可视化树(Visual Tree),经过布局系统的测量(Measure)和排列(Arrange)阶段,实际尺寸发生变化时,这个事件才会被唤起。单纯设置Width/Height只是给控件加了布局约束,此时控件还没参与渲染,自然不会触发事件。

解决办法

如果想要和WinForm的行为对齐,我们可以通过监听Width和Height依赖属性的变更来实现,不管控件是否在可视化树中,只要属性值变了就触发回调。

代码实现

public class ControlBase : UserControl
{
    public ControlBase()
    {
        // 保留原SizeChanged事件,处理控件实际渲染后的尺寸变化
        SizeChanged += OnSizeChanged;

        // 监听Width和Height属性的变更
        var widthPropDescriptor = DependencyPropertyDescriptor.FromProperty(WidthProperty, typeof(ControlBase));
        widthPropDescriptor.AddValueChanged(this, OnWidthHeightPropertyChanged);

        var heightPropDescriptor = DependencyPropertyDescriptor.FromProperty(HeightProperty, typeof(ControlBase));
        heightPropDescriptor.AddValueChanged(this, OnWidthHeightPropertyChanged);
    }

    private void OnSizeChanged(object sender, SizeChangedEventArgs e)
    {
        // 这里处理控件实际渲染尺寸变化的逻辑
    }

    private void OnWidthHeightPropertyChanged(object sender, EventArgs e)
    {
        // 这里处理Width/Height属性直接变更的逻辑,和WinForm的SizeChanged行为对齐
        // 注意:此时控件可能还没进入可视化树,ActualWidth/ActualHeight可能还是初始值(比如NaN)
        // 如果需要复用原SizeChanged的逻辑,可以在这里调用对应的处理方法
    }

    // 控件卸载时移除属性监听,避免内存泄漏
    protected override void OnUnloaded(RoutedEventArgs e)
    {
        base.OnUnloaded(e);
        
        var widthPropDescriptor = DependencyPropertyDescriptor.FromProperty(WidthProperty, typeof(ControlBase));
        widthPropDescriptor.RemoveValueChanged(this, OnWidthHeightPropertyChanged);

        var heightPropDescriptor = DependencyPropertyDescriptor.FromProperty(HeightProperty, typeof(ControlBase));
        heightPropDescriptor.RemoveValueChanged(this, OnWidthHeightPropertyChanged);
    }
}

额外说明

  1. 如果你只需要控件在可视化树中时的尺寸变化响应,那只需要把实例添加到某个已加载的容器(比如Window的Grid)中,之后修改Width/Height就会正常触发SizeChanged事件。
  2. 要注意WPF中Width/Height是布局约束属性,ActualWidth/ActualHeight才是控件实际渲染后的尺寸,这和WinForm的Size属性直接对应实际尺寸的逻辑有区别,使用时要留意。

内容的提问来源于stack exchange,提问作者walruz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:02:44