Android动画之ObjectAnimator中ofXX函数全解析-自定义Property,TypeConverter,TypeEvaluator

动画入门和进阶文章列表:

Animation动画概述和执行原理
Android动画之补间动画TweenAnimation
Android动画之逐帧动画FrameAnimation
Android动画之插值器简介和系统默认插值器
Android动画之插值器Interpolator自定义
Android动画之视图动画的缺点和属性动画的引入
Android动画之ValueAnimator用法和自定义估值器
Android动画之ObjectAnimator实现补间动画和ObjectAnimator自定义属性
Android动画之ObjectAnimator中ofXX函数全解析-自定义Property,TypeConverter,TypeEvaluator
Android动画之AnimatorSet联合动画用法
Android动画之LayoutTransition布局动画
Android动画之共享元素动画
Android动画之ViewPropertyAnimator(专用于view的属性动画)
Android动画之Activity切换动画overridePendingTransition实现和Theme Xml方式实现
Android动画之ActivityOptionsCompat概述
Android动画之场景变换Transition动画的使用
Android动画之Transition和TransitionManager使用
Android动画之圆形揭露动画Circular Reveal

1 Android属性动画中ofXX函数概述

前面一篇属性动画文章中讲解了如何利用ObjectAnimator实现几种补间动画效果和如何自定义ObjectAnimator属性,其中用到了ObjectAnimator对象的ofInt,ofFLoat,ofObject,但这些函数都有众多的重载函数,这一篇接着讲解ObjectAnimator众多的ofXXX函数的用法。
观看本篇文章请务必先看Android动画之ValueAnimator用法和自定义估值器
ObjectAnimator of函数列表:

ofArgb(Object target, String propertyName, int... values)   api21可用
ofArgb(T target, Property<T, Integer> property, int... values)  api21可用

ofFloat(Object target, String propertyName, float... values)  api11 可用
ofFloat(Object target, String xPropertyName, String yPropertyName, Path path)   api21可用
ofFloat(T target, Property<T, Float> property, float... values) api14可用
ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path)  api21可用

ofInt(Object target, String propertyName, int... values)  api11可用
ofInt(Object target, String xPropertyName, String yPropertyName,Path path) api21可用
ofInt(T target, Property<T, Integer> property, int... values) api14可用
ofInt(T target, Property<T, Integer> xProperty,Property<T, Integer> yProperty, Path path) api21可用

ofMultiFloat(Object target, String propertyName,float[][] values)  api21可用
ofMultiFloat(Object target, String propertyName, Path path)        api21可用
ofMultiFloat(Object target, String propertyName,
            TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, T... values)   api21可用
ofMultiInt(Object target, String propertyName, int[][] values)    api21可用
ofMultiInt(Object target, String propertyName, Path path)     api21可用
ofMultiInt(Object target, String propertyName,
            TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, T... values)    api21可用

ofObject(Object target, String propertyName,
            @Nullable TypeConverter<PointF, ?> converter, Path path)  api21可用
ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)  api11
ofObject(T target, Property<T, P> property,
            TypeConverter<V, P> converter, TypeEvaluator<V> evaluator, V... values)  api21
ofObject(T target, @NonNull Property<T, V> property,
            @Nullable TypeConverter<PointF, V> converter, Path path)   api2可用
ofObject(T target, Property<T, V> property, TypeEvaluator<V> evaluator, V... values)  api14可用

ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)  api11可用

2 OfFloat & ofInt

把ofFLoat和ofInt两个函数一起讲解是因为这两个函数除了类型不一样,重载函数很相似。

ofFloat(Object target, String propertyName, float... values)
ofFloat(Object target, String xPropertyName, String yPropertyName, Path path)
ofFloat(T target, Property<T, Float> property, float... values)
ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path)

ofInt(Object target, String propertyName, int... values)
ofInt(Object target, String xPropertyName, String yPropertyName,Path path)
ofInt(T target, Property<T, Integer> property, int... values)
ofInt(T target, Property<T, Integer> xProperty,Property<T, Integer> yProperty, Path path)

ObjectAnimator 的ofFloat和ofInt函数用法相似,所以只讲解ofFloat,

ofFloat(Object target, String propertyName, float… values)

这个函数比较简单,第一个参数传入目标对象,第二个参数传入要改变的属性(配合setXX函数,关于如何定义propertyName前面的文章中已经说明),第三个参数是个渐变属性,可以传多个值。
代码示例:
实现TextView的旋转:

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, "rotation", 0, 270);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

ofFloat(Object target, String xPropertyName, String yPropertyName, Path path)

可以同时操纵两个参数变化,实现动画。
参数说明:
target:动画目标对象,这个目标对象有些特别,沿着一条路径Path能够使用两个属性,路径Path动画在二维空间中移动,由动画坐标(x,y)决定效果,(重要)所以对象必须有两个函数一个是setNameX(),另外一个是setNameY(),类似view的setTranslationX,SetTranslationY,当然也可以自己定义属性,同时对应的xPropertyName和yPropertyName分别为translationX和translationY。
xPropertyName:Path对应的X轴方向的属性值,
yPropertyName:Path对应的Y轴方向的属性值,
path:动画路径。

代码示例:
TextView 在X轴和Y轴方向上移动

Path path = new Path();
path.moveTo(0,0);
path.lineTo(50,50);
path.lineTo(100,20);
path.lineTo(900,400);
path.lineTo(500,1000);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, "translationX","translationY",path );
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述
上面的示例代码利用了translationX和translationX属性,这两个属性是View的自带的属性,同时也可以是两个互不相干的属性,可以实现类似组合动画的效果。

Path path = new Path();
path.moveTo(0,0);
path.lineTo(50,1);
path.lineTo(100,2);
path.lineTo(900,0.5f);
path.lineTo(500,1);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, "translationX","scaleX",path );
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

ofFloat(T target, Property<T, Float> property, float… values)

参数说明:
target:动画目标对象
property:动画作用的属性,有了这个属性对象, 就可以不用写属性对应的字段值,类似不用写“scale”
values:动画取值,如果是一个值则将target开始的状态作为开始值,将values的一个值,作为结束值,如果是两个值则第一个为动画开始值,第二个为动画结束值。

这个函数用到了Property属性,是API14添加的方法,不知道大家注意到没有,每次使用属性动画,我们都需要记得目标对象的setXXX函数后面的相应字符串,虽然不复杂但有时确实会记不清,需要再次确认,而这个带有Property的函数就大大简化了这个过程。

Android为我们提供了简单的常量对象来实现旋转动画:
ObjectAnimator.ofFloat(mTextView, View.ROTATION, 0,30);
这里的View.ROTATION就是个Property对象,可以简单地实现旋转的属性动画。

View.ROTATION源码

public static final Property<View, Float> ROTATION = new FloatProperty<View>("rotation") {
    @Override
    public void setValue(View object, float value) {
        object.setRotation(value);
    }

    @Override
    public Float get(View object) {
        return object.getRotation();
    }
};

View.ROTATION示例

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, View.ROTATION, 0,270);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述
View中具有和View.ROTATION作用的常量还有如下:
ALPHA,TRANSLATION_X,TRANSLATION_Y,TRANSLATION_Z,X,Y,ROTATION, ROTATION_X , ROTATION_Y,SCALE_X,SCALE_Y。

自定义Property

分析上面常量的他们都是实现了FloatProperty,或者IntProperty然后重写setValue和get方法,而FloatProperty,IntProperty又是继承了Property,所以我们可以通过实现FloatPropety,IntProperty或者直接实现Property来自定义Property。

自定义Property:

public static class MyProperty extends Property<TextView,String>{
    public MyProperty(String name) {
        super(String.class, name);
    }

    @Override
    public String get(TextView object) {
        return object.getText().toString();
    }

    @Override
    public void set(TextView object, String value) {
        object.setText(value);
    }
}

自定义估值器:

public static class IntEvaluator implements TypeEvaluator<String>{

    @Override
    public String evaluate(float fraction, String startValue, String endValue) {
        int startInt = Integer.parseInt(startValue) ;
        int endInt = Integer.parseInt(endValue);
        int cur = (int) (startInt + fraction * (endInt - startInt));
        return cur+"";
    }
}

使用自定义Property:

IntEvaluator intEvaluator = new IntEvaluator();
MyProperty property = new MyProperty("text");
ObjectAnimator objectAnimator = ObjectAnimator.ofObject(mTextView,property,intEvaluator,"1", "10");
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path)
函数也用到了Property属性,需要结合property和Path,类似上面函数的用法,不再说明。

3 ofArgb 颜色属性动画

ofArgb(Object target, String propertyName, int… values) 对颜色属性进行动画。

参数说明:
target:动画作用对象
propertyName:动画作用的属性
values:动画使用的可变参数
代码示例:

ObjectAnimator objectAnimator = ObjectAnimator.ofArgb(mTextView,"backgroundColor", Color.RED, Color.GREEN);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

ofArgb(T target, Property<T, Integer> property, int… values)

用到了Property属性,但是View中没有类似ROTATION属性的对颜色属性的简写,可以自定义Property,下面的例子只是数值的渐变,如果真的需要颜色渐变,需要设置颜色估值器:

public static class MyProperty extends Property<TextView,Integer>{
    public MyProperty(String name) {
        super(Integer.class, name);
    }

    @Override
    public Integer get(TextView object) {
        Drawable drawable =  object.getBackground();
        if (drawable instanceof ColorDrawable){
            return ((ColorDrawable) drawable).getColor();
        }
        return Color.YELLOW;
    }

    @Override
    public void set(TextView object, Integer value) {
        object.setBackgroundColor(value);
    }
}

使用:

MyProperty property = new MyProperty("background");
ObjectAnimator objectAnimator = ObjectAnimator.ofArgb(mTextView,property, Color.RED, Color.GREEN);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

4 ofMultiFloat,ofMultiInt

被称为多参数布局,用的不太多,下面简单介绍:
ofMultiFloat(Object target, String propertyName,float[][] values)
ofMultiFloat(Object target, String propertyName, Path path)
ofMultiFloat(Object target, String propertyName,
TypeConverter<T, float[]> converter, TypeEvaluator evaluator, T… values)
ofMultiInt(Object target, String propertyName, int[][] values)
ofMultiInt(Object target, String propertyName, Path path)
ofMultiInt(Object target, String propertyName,
TypeConverter<T, int[]> converter, TypeEvaluator evaluator, T… values)
ofMultiFloat和ofMultiInt用法相似,由于上面只介绍了ofFloat,下面只介绍ofMultiInt方法:

ofMultiInt(Object target, String propertyName, int[][] values)

参数说明:
propertyName:进行动画的属性名
values[][]:二维数组,至少两组数据,每个values[]中存放一个setter函数中所有的参数,然后从values[0]中取值为动画开始值,从values[最后一组]中取值为动画最后的值,如果之间还有值,就作为过渡,从values[0]-values[1]-…(大家明白的)
需要自定义view添加getter,setter函数。

代码示例:

public class ViewDemo24 extends android.support.v7.widget.AppCompatTextView {

    public ViewDemo24(Context context) {
        this(context,null,0);
    }

    public ViewDemo24(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ViewDemo24(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
    }

    public void setMulText(int data1,int data2){
        String data = "";
       data = data + data1 + data2;
        setText(data);
    }

    public String getMulText(){
        return getText().toString();
    }
}

使用:

int[][] data = {{1,9},{4,12}} ;
ObjectAnimator objectAnimator = ObjectAnimator.ofMultiInt(viewDemo24,"mulText",data);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

ofMultiInt(Object target, String propertyName, Path path)

用法和上面的函数类似,只不过把二维数组换成了Path,并且setter函数只能接收两个int参数,从path中取动画开始值和结束值(从path.moveTo中取动画开始值,后面的值为动画结束值)。
代码示例
**自定义view还用上面的ViewDemo24 **

Path path = new Path();
path.moveTo(0,6);
path.lineTo(5,9);
ObjectAnimator objectAnimator = ObjectAnimator.ofMultiInt(viewDemo24,"mulText",path);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

ofMultiInt(Object target, String propertyName,TypeConverter<T, int[]> converter, TypeEvaluator evaluator, T… values)

Converter:把int[]数组转换成需要的T类型,然后利用估值器计算T 得到ObjectAnimator需要的类型。

依然用上面的自定义view ,setter函数需要两个参数:
用到的自定义T类型:

public static class Point {
    int x;
    int y;

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

自定义类型转换,把上面自定义的Point类型转换成int[]数组:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static class IntConverter extends TypeConverter <Point ,int[]>{
    public IntConverter(Class<Point> fromClass, Class<int[]> toClass) {
        super(fromClass, toClass);
    }

    @Override
    public int[] convert(Point value) {
        int[] intarr = {value.getX(),value.getY()};
        return intarr;
    }
}

自定义估值器:

public static class PointEvaluator implements TypeEvaluator<Point> {

    @Override
    public Point evaluate(float fraction, Point startValue, Point endValue) {
        int startxInt = startValue.getX() ;
        int endxInt = endValue.getX();
        int curx = (int) (startxInt + fraction * (endxInt - startxInt));

        int startyInt = startValue.getY() ;
        int endyInt = endValue.getY();
        int cury = (int) (startyInt + fraction * (endyInt - startyInt));
        Point point = new Point();
        point.setX(curx);
        point.setY(cury);
        return point;
    }
}

代码中使用:

IntConverter intConverter = new IntConverter(Point.class,int[].class);
PointEvaluator pointEvaluator = new PointEvaluator();
Point point1 = new Point();
point1.setX(1);
point1.setY(5);
Point point2 = new Point();
point2.setX(4);
point2.setY(9);
ObjectAnimator objectAnimator = ObjectAnimator.ofMultiInt(viewDemo24,"mulText",intConverter,pointEvaluator,point1,point2);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

5 ofObject

对对象进行动画:

ofObject(Object target, String propertyName,
            @Nullable TypeConverter<PointF, ?> converter, Path path)
ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
ofObject(T target, Property<T, P> property,
            TypeConverter<V, P> converter, TypeEvaluator<V> evaluator, V... values)
ofObject(T target, @NonNull Property<T, V> property,
            @Nullable TypeConverter<PointF, V> converter, Path path)
ofObject(T target, Property<T, V> property, TypeEvaluator<V> evaluator, V... values)

上面讲解了ofInt ,ofFloat,ofMultiInt,ofMultiFloat等函数,仔细观察上面的ofObject函数,可以分析得到ofObject把类型泛型化了,每个函数都提供了TypeEvaluator供ObjectAnimator识别参数,所以ofObject的用法和ofInt,ofFloat相同。

ofObject(T target, Property<T, V> property, TypeEvaluator evaluator, V… values)
参数说明:
target:动画目标对象
property:自定义property,内部调用getter,setter函数,不用再指定propertyName
evaluator:估值器,生成动画所需对象
values:动画传入参数

利用ofObject 实现view的移动
自定义类:

public static class Point {
    int x;
    int y;

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

自定义property

public static class MyProperty2 extends Property<TextView,Point>{
    public MyProperty2(String name) {
        super(Point.class, name);
    }

    @Override
    public Point get(TextView object) {
        Point point = new Point();
        point.setX((int) object.getTranslationX());
        point.setY((int) object.getTranslationY());
        return  point;
    }

    @Override
    public void set(TextView object, Point value) {
        object.setTranslationX(value.getX());
        object.setTranslationY(value.getY());
    }
}

自定义Evaluator估值器

public static class PointEvaluator implements TypeEvaluator<Point> {

    @Override
    public Point evaluate(float fraction, Point startValue, Point endValue) {
        int startxInt = startValue.getX() ;
        int endxInt = endValue.getX();
        int curx = (int) (startxInt + fraction * (endxInt - startxInt));

        int startyInt = startValue.getY() ;
        int endyInt = endValue.getY();
        int cury = (int) (startyInt + fraction * (endyInt - startyInt));
        Point point = new Point();
        point.setX(curx);
        point.setY(cury);
        return point;
    }
}

使用:

MyProperty2 property2 = new MyProperty2("tran");//參數只是为了标识无具体意义
PointEvaluator evaluator = new PointEvaluator();
Point point1 = new Point();
point1.setY(0);
point1.setX(100);

Point point2 = new Point();
point2.setY(700);
point2.setX(1000);
ObjectAnimator objectAnimator = ObjectAnimator.ofObject(mTextView,property2,evaluator,point1,point2);
objectAnimator.setDuration(3000);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.setRepeatCount(-1);
objectAnimator.start();

在这里插入图片描述

6 ofPropertyValuesHolder(Object target, PropertyValuesHolder… values)

参数说明:
target:动画目标对象
values:PropertyValuesHolder 动画可变参数。
多动画执行,多个动画一起执行。

PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1,2);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY",0, 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 0.5f,1.0f);

ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mTextView, holder1, holder2, holder3);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

在这里插入图片描述

Keyframe 实现关键帧操作(来自Android Developer)
利用Keyframe可以添加一些关键帧,来控制动画的执行,例如:

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
rotationAnim.setDuration(5000);

上面的代码作用为动画执行到一半时旋转360度,动画执行完时再从360旋转到0度。关键帧的作用和插值器的作用一样都是动画执行过程中,返回动画完成程度。
自定义插值器
ofXXX函数全部讲完了

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,我们需要创建一个自定义类来作为 PropertyGrid 的对象。这个类可以包含多个属性以及子属性。例如: ```c# public class Person { public string Name { get; set; } public int Age { get; set; } public Address Address { get; set; } } public class Address { public string Street { get; set; } public string City { get; set; } public string State { get; set; } } ``` 接下来,我们需要创建一个自定义PropertyDescriptor 类,用于控制 PropertyGrid 的显示方式。这个类需要继承自 PropertyDescriptor 类,并重写几个方法,包括 GetValue()、SetValue()、CanResetValue()、ResetValue()、ShouldSerializeValue() 等方法。例如: ```c# public class NestedPropertyDescriptor : PropertyDescriptor { private PropertyDescriptor _parent; private PropertyDescriptor _child; public NestedPropertyDescriptor(PropertyDescriptor parent, PropertyDescriptor child) : base(child.Name, null) { _parent = parent; _child = child; } public override object GetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return null; return _child.GetValue(parentValue); } public override void SetValue(object component, object value) { object parentValue = _parent.GetValue(component); if (parentValue == null) return; _child.SetValue(parentValue, value); OnValueChanged(component, EventArgs.Empty); } public override bool CanResetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return false; return _child.CanResetValue(parentValue); } public override void ResetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return; _child.ResetValue(parentValue); OnValueChanged(component, EventArgs.Empty); } public override bool ShouldSerializeValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return false; return _child.ShouldSerializeValue(parentValue); } public override Type ComponentType { get { return _parent.ComponentType; } } public override bool IsReadOnly { get { return _child.IsReadOnly; } } public override Type PropertyType { get { return _child.PropertyType; } } public override string DisplayName { get { return _child.DisplayName; } } } ``` 最后,我们需要创建一个自定义TypeConverter 类,用于控制 PropertyGrid 的显示方式。这个类需要继承自 ExpandableObjectConverter 类,并重写几个方法,包括 GetProperties()、GetPropertiesSupported() 等方法。例如: ```c# public class NestedTypeConverter : ExpandableObjectConverter { public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { PropertyDescriptorCollection properties = base.GetProperties(context, value, attributes); List<PropertyDescriptor> nestedProperties = new List<PropertyDescriptor>(); foreach (PropertyDescriptor property in properties) { if (property.PropertyType.IsClass && property.PropertyType != typeof(string)) { PropertyDescriptorCollection nestedProps = TypeDescriptor.GetProperties(property.PropertyType, new Attribute[] { new BrowsableAttribute(true) }); foreach (PropertyDescriptor nestedProp in nestedProps) { nestedProperties.Add(new NestedPropertyDescriptor(property, nestedProp)); } } else { nestedProperties.Add(property); } } return new PropertyDescriptorCollection(nestedProperties.ToArray()); } public override bool GetPropertiesSupported(ITypeDescriptorContext context) { return true; } } ``` 最后,我们可以在窗体使用 PropertyGrid 控件来显示我们自定义的类。例如: ```c# private void Form1_Load(object sender, EventArgs e) { PropertyGrid1.SelectedObject = new Person { Name = "John", Age = 30, Address = new Address { Street = "123 Main St", City = "Anytown", State = "CA" } }; PropertyGrid1.BrowsableAttributes = new Attribute[] { new BrowsableAttribute(true) }; PropertyGrid1.ExpandAllGridItems(); } ``` 以上就是 C# PropertyGrid 自定义多层显示的示例。希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值