WPF Telerik.Windows.Controls.Data.PropertyGrid 自定义属性编辑器
1.AI帮忙定义新用户控件
2.在属性上添加TelerikEditorAttribute特性
private ObservableCollection<string> _axisOrder;[Display(Description = "点位", GroupName = "通用", Name = "轴&顺序", Order = 1)][DataMember][TelerikEditorAttribute(typeof(DoubleListBoxEditor), "SelectedItems")]public ObservableCollection<string> AxisOrder{get => _axisOrder;set => this.RaiseAndSetIfChanged(ref _axisOrder, value);}
[TelerikEditorAttribute(typeof(DoubleListBoxEditor), "SelectedItems")]
SelectedItems属性可以根据实体情况进行更换;
3.效果
4.扩展
这个编辑控件包含了两个ListBox,我想根据实体的其他属性变更然后变更其中一个ListBox的数据源:
思路就是在DoubleListBoxEditor类里面监听这个属性变化,要挂载事件;
要想找到这个属性的事件就得找到对应的实体;
(要有控件的Parent属性, 视觉树这些概念)
使用自定义Editor控件的Parent属性,可以得到对应的在PropertyGrid中对应的条目包装:
在这个包装中找到DataContext属性得到对应的实体属性包装;
然后再找到Instance属性得到对应实体,再得到对应想拿到的属性;
挂载事件的时机要把控好,不能写在构造函数里,此时界面对象还未赋值;
代码:
public override void OnApplyTemplate(){base.OnApplyTemplate();if (!(Parent is PropertyGridField field)){return;}var def = (PropertyDefinition)field.DataContext;CustomPropertyDescriptor pd = null;if (def.Instance is MovePositionActionNode node){node.PropertyChanged -= PositionPropertyChanged;node.PropertyChanged += PositionPropertyChanged;}}
更新:
测试中发现会多次调用PositionPropertyChanged方法,和我预想的结果不同,因为我的写法是:
node.PropertyChanged -= PositionPropertyChanged;
node.PropertyChanged += PositionPropertyChanged;
本来想的是始终只会挂载一个方法上去,但是测试的时候发现每调用一次OnApplyTemplate(),就会多挂载一个方法上去。
原因:
在 C# 中,委托实际上是对象(继承自 System.MulticastDelegate
)。每次使用方法名创建委托时,都会在堆上创建一个新的委托对象实例。
写一段代码模拟上面出现的bug:
public class EventSource{public event EventHandler Event;public void RaiseEvent(){Event.Invoke(null,null);}}public class Example{public void MyMethod(object sender, EventArgs e) { Console.WriteLine("Hello, World!"); }}
然后这样调用:
EventSource obj = new EventSource();for (int i = 0; i < 3; i++){Example ex = new Example();obj.Event -= ex.MyMethod;obj.Event += ex.MyMethod;}obj.RaiseEvent();
结果会调用三次MyMethod,因为我的代码中,执行OnApplyTemplate时都是新创建对象的时候,所以效果类似这段测试代码;
查看IL:
node.PropertyChanged -= PositionPropertyChanged;
node.PropertyChanged += PositionPropertyChanged;
IL_0045: ldloc.2 // nodeIL_0046: ldarg.0 // thisIL_0047: ldftn instance void Lithography.Model.ActionAbout.UI.DoubleListBoxEditor::PositionPropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)IL_004d: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)IL_0052: callvirt instance void [ReactiveUI]ReactiveUI.ReactiveObject::remove_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)IL_0057: nop// [82 17 - 82 65]IL_0058: ldloc.2 // nodeIL_0059: ldarg.0 // thisIL_005a: ldftn instance void Lithography.Model.ActionAbout.UI.DoubleListBoxEditor::PositionPropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)IL_0060: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)IL_0065: callvirt instance void [ReactiveUI]ReactiveUI.ReactiveObject::add_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)IL_006a: nop
挂载对象前创建了一个PropertyChangedEventHandler对象,对应着上方:委托实际上是对象(继承自 System.MulticastDelegate
)。每次使用方法名创建委托时,都会在堆上创建一个新的委托对象实例。
也就是说+=操作符相当于执行了Add_TestEvent(new Action(memory.Run)),就是这个new Action包含了对memory指向的内存的引用。而这个引用在CLR看来是可达的,可以通过引发事件来调用该内存,所以这种情况会有内存泄漏的风险。
引用:https://blog.csdn.net/nodeathphoenix/article/details/84549399?fromshare=blogdetail&sharetype=blogdetail&sharerId=84549399&sharerefer=PC&sharesource=qq_59062726&sharefrom=from_link
解决办法:
未完待续