Advertisement
Guest User

Untitled

a guest
Jul 22nd, 2019
51
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.00 KB | None | 0 0
  1. class PropertyMetadataBuilder<T, TValue> where T : DependencyObject
  2. {
  3. private readonly PropertyMetadata _propertyMetadata;
  4.  
  5. internal PropertyMetadataBuilder()
  6. {
  7. _propertyMetadata = new PropertyMetadata();
  8. }
  9.  
  10. public PropertyMetadataBuilder<T, TValue> DefaultValue(
  11. TValue defaultValue
  12. )
  13. {
  14. _propertyMetadata.DefaultValue = defaultValue;
  15. return this;
  16. }
  17.  
  18. public PropertyMetadataBuilder<T, TValue> PropertyChanged(
  19. Action<T, DependencyPropertyChangedEventArgs<TValue>> propertyChangedCallback
  20. )
  21. {
  22. _propertyMetadata.PropertyChangedCallback = new PropertyChangedCallback(
  23. (sender, e) =>
  24. propertyChangedCallback((T)sender,
  25. new DependencyPropertyChangedEventArgs<TValue>(e)
  26. )
  27. );
  28. return this;
  29. }
  30.  
  31. public PropertyMetadataBuilder<T, TValue> PropertyChanging(
  32. Action<T, PropertyChangingEventArgs<TValue>> coerceValueCallback
  33. )
  34. {
  35. _propertyMetadata.CoerceValueCallback = new CoerceValueCallback(
  36. (d, baseValue) =>
  37. {
  38. var e = new PropertyChangingEventArgs<TValue>((TValue)baseValue);
  39. coerceValueCallback((T)d, e);
  40. return
  41. e.Canceled
  42. ? DependencyProperty.UnsetValue
  43. : e.CoercedValue;
  44. }
  45. );
  46. return this;
  47. }
  48.  
  49. public static implicit operator PropertyMetadata(
  50. PropertyMetadataBuilder<T, TValue> builder
  51. )
  52. {
  53. return builder._propertyMetadata;
  54. }
  55. }
  56.  
  57. static class DependencyPropertyBuilderExtensions
  58. {
  59. public static DependencyPropertyBuilder<T, TValue> PropertyMetadata<T, TValue>(
  60. this DependencyPropertyBuilder<T, TValue> builder,
  61. Action<PropertyMetadataBuilder<T, TValue>> build
  62. ) where T : DependencyObject
  63. {
  64. var metadataBuilder = new PropertyMetadataBuilder<T, TValue>();
  65. build(metadataBuilder);
  66. return builder.Metadata(metadataBuilder);
  67. }
  68. }
  69.  
  70. public DependencyPropertyBuilder<T, TValue> Metadata(
  71. PropertyMetadata propertyMetadata
  72. )
  73. {
  74. _propertyMetadata = propertyMetadata;
  75. return this;
  76. }
  77.  
  78. class PropertyChangingEventArgs<TValue> : EventArgs
  79. {
  80. internal PropertyChangingEventArgs(TValue baseValue)
  81. {
  82. NewValue = baseValue;
  83. CoercedValue = baseValue;
  84. }
  85. public TValue NewValue { get; }
  86. public TValue CoercedValue { get; set; }
  87. public bool Canceled { get; set; }
  88. }
  89.  
  90. public PropertyMetadataBuilder<T, TValue> PropertyChanging(
  91. Action<T, PropertyChangingEventArgs<TValue>> coerceValueCallback
  92. )
  93. {
  94. _propertyMetadata.CoerceValueCallback = new CoerceValueCallback(
  95. (d, baseValue) =>
  96. {
  97. var e = new PropertyChangingEventArgs<TValue>((TValue)baseValue);
  98. coerceValueCallback((T)d, e);
  99. return
  100. e.Canceled
  101. ? DependencyProperty.UnsetValue
  102. : e.CoercedValue;
  103. }
  104. );
  105. return this;
  106. }
  107.  
  108. class TestObject : DependencyObject
  109. {
  110. public static readonly DependencyProperty CountProperty =
  111. DependencyPropertyBuilder
  112. .Register<TestObject, int>(nameof(TestObject.Count))
  113. .PropertyMetadata(b => b
  114. .DefaultValue(5)
  115. .PropertyChanged((testObject, e) =>
  116. {
  117. Console.WriteLine($"{e.Property.Name} = {e.OldValue} --> {e.NewValue}");
  118. })
  119. .PropertyChanging((testObject, e) =>
  120. {
  121. if (e.NewValue > 20)
  122. {
  123. e.CoercedValue = 15;
  124. }
  125.  
  126. if (e.NewValue < 1)
  127. {
  128. e.Canceled = true;
  129. }
  130. })
  131. ).ValidateValue(value => value >= 0);
  132.  
  133. public int Count
  134. {
  135. get { return CountProperty.GetValue<int>(this); }
  136. set { CountProperty.SetValue(this, value); }
  137. }
  138. }
  139.  
  140. var testObject = new TestObject();
  141. testObject.Count.Dump("Default");
  142.  
  143. testObject.Count2 = 8;
  144. testObject.Count2.Dump("Changed");
  145.  
  146. testObject.Count = 22;
  147. testObject.Count.Dump("Coerced to max");
  148.  
  149. testObject.Count = 0;
  150. testObject.Count.Dump("Property change canceled");
  151.  
  152. testObject.Count = -2; // bam!
  153.  
  154. public DependencyProperty Build()
  155. {
  156. BuildDefaultValue();
  157. BuildValidateValueCallback();
  158.  
  159. return DependencyProperty.Register(
  160. _name,
  161. _propertyType,
  162. _ownerType,
  163. _propertyMetadata,
  164. _validateValueCallback
  165. );
  166. }
  167.  
  168. private void BuildDefaultValue()
  169. {
  170. var property = _ownerType.GetProperty(_name);
  171.  
  172. // Use the default value specified by the user or try to use the attribute.
  173. _propertyMetadata.DefaultValue =
  174. _propertyMetadata.DefaultValue ??
  175. new Func<object>(() =>
  176. // Get the defualt value from the attribute...
  177. property.GetCustomAttribute<DefaultValueAttribute>()?.Value ??
  178. // or use the default value for the type.
  179. (property.PropertyType.IsValueType
  180. ? Activator.CreateInstance(property.PropertyType)
  181. : null
  182. )
  183. )();
  184. }
  185.  
  186. private void BuildValidateValueCallback()
  187. {
  188. var property = _ownerType.GetProperty(_name);
  189.  
  190. // Use the callback specified by the user or try to use the attributes.
  191. _validateValueCallback =
  192. _validateValueCallback ??
  193. (value =>
  194. new Func<bool>(() => (
  195. property.GetCustomAttributes<ValidationAttribute>() ??
  196. Enumerable.Empty<ValidationAttribute>()
  197. ).All(x => x.IsValid(value)))()
  198. );
  199. }
  200.  
  201. public PropertyMetadataBuilder<T, TValue> CoerceValue(
  202. Func<T, TValue, object> coerceValueCallback
  203. )
  204. {
  205. _propertyMetadata.CoerceValueCallback = (d, baseValue) =>
  206. coerceValueCallback((T) d, (TValue)baseValue);
  207. return this;
  208. }
  209.  
  210. public static PropertyMetadataBuilder<T, TValue> CoerceValue<T, TValue>(
  211. this PropertyMetadataBuilder<T, TValue> builder,
  212. Action<T, CoerceValueEventArgs<TValue>> coerceValueCallback
  213. ) where T : DependencyObject
  214. {
  215. builder.CoerceValue((d, baseValue) =>
  216. {
  217. var e = new CoerceValueEventArgs<TValue>(baseValue);
  218. coerceValueCallback(d, e);
  219. return
  220. e.Canceled
  221. ? DependencyProperty.UnsetValue
  222. : e.CoercedValue;
  223. });
  224. return builder;
  225. }
  226.  
  227. public class CoerceValueEventArgs<TValue> : EventArgs
  228. {
  229. internal CoerceValueEventArgs(TValue baseValue)
  230. {
  231. NewValue = baseValue;
  232. CoercedValue = baseValue;
  233. }
  234. public TValue NewValue { get; }
  235. public TValue CoercedValue { get; set; }
  236. public bool Canceled { get; set; }
  237. }
  238.  
  239. internal class TestObject : DependencyObject
  240. {
  241. public static readonly DependencyProperty CountProperty =
  242. DependencyPropertyBuilder
  243. .Register<TestObject, int>(nameof(Count))
  244. .PropertyMetadata(b => b
  245. .PropertyChanged((testObject, e) =>
  246. {
  247. Console.WriteLine($"{e.Property.Name} = {e.OldValue} --> {e.NewValue}");
  248. })
  249. .CoerceValue((testObject, e) =>
  250. {
  251. if (e.NewValue > 20)
  252. {
  253. e.CoercedValue = 15;
  254. }
  255.  
  256. if (e.NewValue < 1)
  257. {
  258. e.Canceled = true;
  259. }
  260. })
  261. );
  262.  
  263. [DefaultValue(5)]
  264. [Range(0, 15)]
  265. public int Count
  266. {
  267. get { return CountProperty.GetValue<int>(this); }
  268. set { CountProperty.SetValue(this, value); }
  269. }
  270. }
  271.  
  272. [TestMethod]
  273. public void Count_DefaultValue()
  274. {
  275. var testObject = new TestObject();
  276. Assert.AreEqual(5, testObject.Count, "Default value.");
  277. }
  278.  
  279. [TestMethod]
  280. public void Count_ChangeValue()
  281. {
  282. var testObject = new TestObject
  283. {
  284. Count = 8
  285. };
  286. Assert.AreEqual(8, testObject.Count, "Changed value");
  287. }
  288.  
  289. [TestMethod]
  290. [ExpectedException(typeof(ArgumentException))]
  291. public void Count_ValueOutOfRange()
  292. {
  293. new TestObject
  294. {
  295. Count = 22
  296. };
  297. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement