PDA

View Full Version : سوال: تعریف یک Property از نوع PointF در UserControl



md3848
سه شنبه 15 بهمن 1398, 12:12 عصر
سلام - من کدمو به صورت زیر نوشتم، اما موقع استفاده از برنامه، این خوصویت غیرفعال هستش!
private PointF airstrip_p1 = new PointF(10, 20);

[Description("Description")]
[Category("AAA_Airstrip")]
[DefaultValue(true)]
public PointF Airstrip_P1
{
get
{
return airstrip_p1;
}
set
{
airstrip_p1 = value;
Invalidate();
}
}

151322

میخوام این طوری بشه :
151323

md3848
سه شنبه 15 بهمن 1398, 12:43 عصر
خب یافتم، باید این خصوصیت ( اسم دقیقش نمیدونم چیه! ) رو هم بهش اضافه کنم.
[TypeConverter(typeof(ExpandableObjectConverter))]

the king
سه شنبه 15 بهمن 1398, 13:47 عصر
سلام - من کدمو به صورت زیر نوشتم، اما موقع استفاده از برنامه، این خوصویت غیرفعال هستش!
private PointF airstrip_p1 = new PointF(10, 20);

[Description("Description")]
[Category("AAA_Airstrip")]
[DefaultValue(true)]
public PointF Airstrip_P1
{
get
{
return airstrip_p1;
}
set
{
airstrip_p1 = value;
Invalidate();
}
}

151322

میخوام این طوری بشه :
151323
برای PointF مقدار پیشفرض رو با DefaultValue تعریف نکنید، اولا مقدار true که برای PointF معنی نداره، مشخصه تون bool که نیست. ثانیا DefaultValue برای مقادیر ثابت قابل استفاده است، برای PointF که میخواهید با new مقدار دهی کنید مناسب نیست.
از طرف دیگه انواع داده ای مثل Point و Size که در PropertyGrid نمایش داده میشه یک نوع Type Converter دارند که بشه داخل Property مقدارشون رو به نوع دیگری مثل string تبدیل کرد و یا از string به اون نوع داده تبدیل کرد.
مثلا System.Drawing.PointConverter یا System.Drawing.SizeConverter
چون استفاده ای از PointF در کنترل های استاندارد NET. نشده و خود مایکروسافت نیازی ندیده که PointF رو در PropertyGrid نشون بده، برای PointF این Type Converter رو طراحی نکرده اند، یعنی PropertyGrid الان نمیدونه چطور باید اون PointF رو برای ویرایش ارائه کنه.
اگر اصرار داشته باشید که از PointF استفاده کنید، باید همچین کلاسی رو خودتون بنویسید و برای مشخصه Airstrip_P1 اون کلاس رو معرفی کنید.
حتی اگر در گوگل عبارت public class PointFConverter رو جستجو کنید در بعضی کتابخانه ها برای همین کاربرد مجبور شده اند که نمونه اش رو بنویسند و این مورد جدیدی نیست.
کد کلاسش اینه که در پیوست هم فایلش رو قرار میدم، این رو با تغییر کد PointConverter خود مایکروسافت نوشتم :

public class PointFConverter : System.ComponentModel.TypeConverter
{
public override bool CanConvertFrom(System.ComponentModel.ITypeDescript orContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}

public override bool CanConvertTo(System.ComponentModel.ITypeDescriptor Context context, Type destinationType)
{
return destinationType == typeof(System.ComponentModel.Design.Serialization. InstanceDescriptor) || base.CanConvertTo(context, destinationType);
}

public override object ConvertFrom(System.ComponentModel.ITypeDescriptorC ontext context, System.Globalization.CultureInfo culture, object value)
{
var text = value as string;
if (text == null)
{
return base.ConvertFrom(context, culture, value);
}
var text2 = text.Trim();
if (text2.Length == 0)
{
return null;
}
if (culture == null)
{
culture = System.Globalization.CultureInfo.CurrentCulture;
}
var c = culture.TextInfo.ListSeparator[0];
var array = text2.Split(new char[] { c });
var array2 = new float[array.Length];
var converter = System.ComponentModel.TypeDescriptor.GetConverter( typeof(float));
for (var i = 0; i < array2.Length; i++)
{
array2[i] = (float)converter.ConvertFromString(context, culture, array[i]);
}
if (array2.Length == 2)
{
return new System.Drawing.PointF(array2[0], array2[1]);
}
throw new ArgumentException();
}

public override object ConvertTo(System.ComponentModel.ITypeDescriptorCon text context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == null)
{
throw new ArgumentNullException(nameof(destinationType));
}
if (value is System.Drawing.PointF)
{
if (destinationType == typeof(string))
{
var point = (System.Drawing.PointF)value;
if (culture == null)
{
culture = System.Globalization.CultureInfo.CurrentCulture;
}
var separator = culture.TextInfo.ListSeparator + " ";
var converter = System.ComponentModel.TypeDescriptor.GetConverter( typeof(float));
var array = new string[2];
array[0] = converter.ConvertToString(context, culture, point.X);
array[1] = converter.ConvertToString(context, culture, point.Y);
return string.Join(separator, array);
}
if (destinationType == typeof(System.ComponentModel.Design.Serialization. InstanceDescriptor))
{
var point2 = (System.Drawing.PointF)value;
var constructor = typeof(System.Drawing.PointF).GetConstructor(new Type[]
{
typeof(float),
typeof(float)
});
if (constructor != null)
{
return new System.ComponentModel.Design.Serialization.Instanc eDescriptor(constructor, new object[]
{
point2.X,
point2.Y
});
}
}
}
return base.ConvertTo(context, culture, value, destinationType);
}

public override object CreateInstance(System.ComponentModel.ITypeDescript orContext context, System.Collections.IDictionary propertyValues)
{
if (propertyValues == null)
{
throw new ArgumentNullException(nameof(propertyValues));
}
var obj = propertyValues["X"];
var obj2 = propertyValues["Y"];
if (obj == null || obj2 == null || !(obj is float) || !(obj2 is float))
{
throw new ArgumentException();
}
return new System.Drawing.PointF((float)obj, (float)obj2);
}

public override bool GetCreateInstanceSupported(System.ComponentModel.I TypeDescriptorContext context)
{
return true;
}

public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescripto rContext context, object value, Attribute[] attributes)
{
var properties = System.ComponentModel.TypeDescriptor.GetProperties (typeof(System.Drawing.PointF), attributes);
return properties.Sort(new string[] { "X", "Y" });
}

public override bool GetPropertiesSupported(System.ComponentModel.IType DescriptorContext context)
{
return true;
}
}


و اینطوری باید برای مشخصه تون تعریف اش کنید :

private PointF airstrip_p1 = new PointF(10, 20);

[Description("Description")]
[Category("AAA_Airstrip")]
[TypeConverter(typeof(PointFConverter))]
public PointF Airstrip_P1
{
get
{
return airstrip_p1;
}
set
{
airstrip_p1 = value;
Invalidate();
}
}


ولی مساله دیگه این بود که چطور به PropertyGrid حالی کنیم که PointF(10, 20) مقدار پیشفرض ما است. از ShouldSerialize و Reset استفاده می کنیم، دو تا متد که نام خاصی دارند.
درسته اینها ظاهرا متد های عادی بی ربطی هستند که ته اسم شون Airstrip_P1 اضافه شده، ولی واقعا اینطوری نیست، Designer ویژوال استدیو متد های Reset و ShouldSerialize رو به نوع دیگری تفسیر میکنه و ازشون استفاده خواهد کرد.

private static readonly PointF DefaultAirstrip_P1 = new PointF(10, 20);

private PointF airstrip_p1 = DefaultAirstrip_P1;

private bool ShouldSerializeAirstrip_P1()
{
return (Airstrip_P1 != DefaultAirstrip_P1);
}

private void ResetAirstrip_P1()
{
Airstrip_P1 = DefaultAirstrip_P1;
}

[Description("Description")]
[Category("AAA_Airstrip")]
[TypeConverter(typeof(PointFConverter))]
public PointF Airstrip_P1
{
get
{
return (airstrip_p1.IsEmpty) ? DefaultAirstrip_P1 : airstrip_p1;
}
set
{
airstrip_p1 = value;
Invalidate();
}
}


کد کلاس PointFConverter :
151325

the king
سه شنبه 15 بهمن 1398, 13:59 عصر
خب یافتم، باید این خصوصیت ( اسم دقیقش نمیدونم چیه! ) رو هم بهش اضافه کنم.
[TypeConverter(typeof(ExpandableObjectConverter))]

ExpandableObjectConverter هر ساختار داده ای که یکسری فیلد و مشخصه عمومی داره (مثل X و Y) رو باز می کنه تا بتوانید اجزاء داخلش رو در PropertyGrid ببینید و ویرایش کنید.
دلیل اینکه ویرایش شدن شون هم اونطور که انتظار میره موفقیت آمیز نیست قبلا در یکی از تاپیک ها با جزئیات توضیح دادم.

md3848
سه شنبه 15 بهمن 1398, 14:01 عصر
الان من برا حالت FLOAT مشکلی ندارم - با کد زیر :
public PointF airstrip_p1 = new PointF(37.4847485621F, 57.2794953547F);

[Description("Description")]
[Category("AAA_Airstrip")]
[DefaultValue(true)]
[TypeConverter(typeof(ExpandableObjectConverter))]
public PointF Airstrip_Point1
{
get
{
return airstrip_p1;
}
set
{
airstrip_p1 = value;
Invalidate();
}
}
که به صورت زیر میشه :
151326

مشکل من الان اینه که میخوایم این به صورت DOUBLE باشه، چون همون طور که میبینید اعداد گرد شدن ( پست شما رو الان بررسی میکنم، حین نوشتن این پست دیدمش - با تشکر )

( اینا نقاط روی صفحه نیستند، یه جفت عدد DOBULE که مختصات GPS هستند. )

the king
سه شنبه 15 بهمن 1398, 14:03 عصر
الان من برا حالت FLOAT مشکلی ندارم - با کد زیر :
public PointF airstrip_p1 = new PointF(37.4847485621F, 57.2794953547F);

[Description("Description")]
[Category("AAA_Airstrip")]
[DefaultValue(true)]
[TypeConverter(typeof(ExpandableObjectConverter))]
public PointF Airstrip_Point1
{
get
{
return airstrip_p1;
}
set
{
airstrip_p1 = value;
Invalidate();
}
}
که به صورت زیر میشه :
151326

مشکل من الان اینه که میخوایم این به صورت DOUBLE باشه، چون همون طور که میبینید اعداد گرد شدن ( پست شما رو الان بررسی میکنم، حین نوشتن این پست دیدمش - با تشکر )

( اینا نقاط روی صفحه نیستند، یه جفت عدد DOBULE که مختصات GPS هستند. )
سعی کنید اون مقادیری که در PropertyGrid و با کمک ExpandableObjectConverter می بینید رو ویرایش کنید تا متوجه بشید منظورم چیه. شما الان مشکل نمایش مقادیر رو حل کردید، ویرایش یک مساله دیگه است.