Xamarin Mac中KVO模型绑定变更事件触发两次的技术求助
我之前在Xamarin.Mac项目里也碰到过一模一样的KVO重复触发问题,折腾了好一阵子才找到根源,给你梳理下解决思路:
问题根源
你手动调用WillChangeValue和DidChangeValue的同时,Xamarin会自动为标记了[Export]的属性生成KVO兼容的代码——也就是说,每次修改属性时,会触发两次KVO通知:一次是你手动调用的,另一次是Xamarin自动插入的代码触发的。这就是为什么移除手动调用后重复触发消失,但代码修改属性时没通知的原因:此时只有控件绑定触发的KVO生效,而Xamarin的自动通知可能因为模型类的配置问题没正常工作。
解决方案
1. 移除手动KVO调用,让Xamarin自动处理
首先把setter里的WillChangeValue("MyVal")和DidChangeValue("MyVal")删掉,然后确保你的模型类满足以下条件:
- 必须继承自
NSObject(KVO是Cocoa机制,只有NSObject子类才能使用) - 加上
[Register]属性,让Xamarin能生成正确的KVO兼容代码
修改后的模型类示例:
[Register("CameraSettingsModel")] public class CameraSettingsModel : NSObject { private int _myVal; [Export("MyVal")] public int MyVal { get => _myVal; set { // 先判断值是否变化,避免无意义的通知 if (_myVal == value) return; _myVal = value; // 无需手动调用KVO通知方法 } } }
这样修改后,不管是代码设置MyVal还是控件通过绑定修改,都会只触发一次KVO通知——Xamarin会自动在setter中插入WillChangeValue和DidChangeValue的调用。
2. 用SetValueForKey完全依赖Cocoa原生KVO机制
如果上面的方法还是有问题,可以直接使用NSObject的原生方法来处理属性,彻底交给Cocoa的KVO机制:
[Register("CameraSettingsModel")] public class CameraSettingsModel : NSObject { [Export("MyVal")] public int MyVal { get => (int)GetValueForKey(new NSString("MyVal")); set => SetValueForKey(NSNumber.FromInt32(value), new NSString("MyVal")); } }
这种方式完全遵循Cocoa的KVO规则,不会出现重复通知的问题。
3. 检查keyPath拼写并添加上下文过滤
另外注意两个细节:
- 你的属性Export的是
"MyVal",但观察者监听的是"MyValue"(看你代码里的switch case),这明显是拼写错误!必须保证keyPath和Export的属性名完全一致,否则会导致监听异常。 - 添加观察者时指定唯一的context,过滤掉非你注册的通知,避免系统其他KVO通知干扰:
// 定义唯一的上下文标识 private static readonly IntPtr MyValObservationContext = new IntPtr(1); // 注册观察者时传入上下文 SettingsModel.AddObserver(this, new NSString("MyVal"), NSKeyValueObservingOptions.New, MyValObservationContext); // 在ObserveValue中过滤 public override void ObserveValue(NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context) { // 不是我们的通知,交给父类处理 if (context != MyValObservationContext) { base.ObserveValue(keyPath, ofObject, change, context); return; } switch (keyPath) { case "MyVal": // 现在只会触发一次API调用 break; } }
按照这些步骤调整后,重复触发的问题应该就能解决了!
内容的提问来源于stack exchange,提问作者Matt Bryson
相关产品推荐
相关产品推荐

