手动创建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); } }
额外说明
- 如果你只需要控件在可视化树中时的尺寸变化响应,那只需要把实例添加到某个已加载的容器(比如Window的Grid)中,之后修改
Width/Height就会正常触发SizeChanged事件。 - 要注意WPF中
Width/Height是布局约束属性,ActualWidth/ActualHeight才是控件实际渲染后的尺寸,这和WinForm的Size属性直接对应实际尺寸的逻辑有区别,使用时要留意。
内容的提问来源于stack exchange,提问作者walruz
相关产品推荐
相关产品推荐

