CSLA.NET 6.0.0
CSLA .NET is a software development framework that helps you build a reusable, maintainable object-oriented business layer for your app.
PropertyStatus.cs
Go to the documentation of this file.
1#if !NETFX_CORE && !XAMARIN
2//-----------------------------------------------------------------------
3// <copyright file="PropertyStatus.cs" company="Marimer LLC">
4// Copyright (c) Marimer LLC. All rights reserved.
5// Website: https://cslanet.com
6// </copyright>
7// <summary>Displays validation information for a business</summary>
8//-----------------------------------------------------------------------
9using System;
10using System.Collections.ObjectModel;
11using System.ComponentModel;
12using System.Linq;
13using System.Windows;
14using System.Windows.Controls;
15using System.Windows.Controls.Primitives;
16using System.Windows.Input;
17using System.Windows.Media;
18using Csla.Core;
19using Csla.Reflection;
20using Csla.Rules;
21
22namespace Csla.Xaml
23{
30 [TemplatePart(Name = "image", Type = typeof(FrameworkElement))]
31 [TemplatePart(Name = "popup", Type = typeof(Popup))]
32 [TemplatePart(Name = "busy", Type = typeof(BusyAnimation))]
33 [TemplateVisualState(Name = "PropertyValid", GroupName = "CommonStates")]
34 [TemplateVisualState(Name = "Error", GroupName = "CommonStates")]
35 [TemplateVisualState(Name = "Warning", GroupName = "CommonStates")]
36 [TemplateVisualState(Name = "Information", GroupName = "CommonStates")]
37 public class PropertyStatus : ContentControl, INotifyPropertyChanged
38 {
39 private bool _isReadOnly = false;
40 private FrameworkElement _lastImage;
41 private Point _lastPosition;
42 private Point _popupLastPosition;
43 private Size _lastAppSize;
44 private Size _lastPopupSize;
45
52 protected bool IsReadOnly
53 {
54 get
55 {
56 return _isReadOnly;
57 }
58 set
59 {
60 _isReadOnly = value;
61 }
62 }
63
64
65#region Constructors
66
67 private bool _loading = true;
68
73 : base()
74 {
75 BrokenRules = new ObservableCollection<BrokenRule>();
76 DefaultStyleKey = typeof(PropertyStatus);
77 IsTabStop = false;
78
79 // In WPF - Loaded fires when form is loaded even if control is not visible.
80 // but will only fire once when control gets visible in Silverlight
81 Loaded += (o, e) =>
82 {
83 _loading = false;
85 };
86 // IsVisibleChanged fires when control first gets visible in WPF
87 // Does not exisit in Silverlight - see Loaded event.
88 IsVisibleChanged += (o, e) =>
89 {
90 // update status if we are not loading
91 // and control is visible
92 if (!_loading && IsVisible)
93 {
95 }
96 };
97 DataContextChanged += (o, e) =>
98 {
99 if (!_loading) SetSource(e.NewValue);
100 };
101 }
102
106 public override void OnApplyTemplate()
107 {
108 base.OnApplyTemplate();
109 UpdateState();
110 }
111
112#endregion
113
114#region Source property
115
120 public static readonly DependencyProperty PropertyProperty = DependencyProperty.Register(
121 "Property",
122 typeof(object),
123 typeof(PropertyStatus),
124 new PropertyMetadata(new object(), (o, e) =>
125 {
126 bool changed = true;
127 if (e.NewValue == null)
128 {
129 if (e.OldValue == null)
130 changed = false;
131 }
132 else if (e.NewValue.Equals(e.OldValue))
133 {
134 changed = false;
135 }
136 ((PropertyStatus)o).SetSource(changed);
137 }));
138
143 [Category("Common")]
144 public object Property
145 {
146 get { return GetValue(PropertyProperty); }
147 set { SetValue(PropertyProperty, value); }
148 }
149
150 private object _source = null;
155 protected object Source
156 {
157 get
158 {
159 return _source;
160 }
161 set
162 {
163 _source = value;
164 }
165 }
166
167 private string _bindingPath = string.Empty;
172 protected string BindingPath
173 {
174 get
175 {
176 return _bindingPath;
177 }
178 set
179 {
180 _bindingPath = value;
181 }
182 }
183
184 private string _propertyName = string.Empty;
185
192 protected string PropertyName
193 {
194 get { return _propertyName; }
195 set { _propertyName = value; }
196 }
197
201 protected virtual void SetSource(bool propertyValueChanged)
202 {
203 var binding = GetBindingExpression(PropertyProperty);
204 if (binding != null)
205 {
206 SetSource(binding.DataItem);
207 }
208 }
209
210
214 protected virtual void SetSource(object dataItem)
215 {
216 SetBindingValues();
217 var newSource = GetRealSource(dataItem, BindingPath);
218
219 if (!ReferenceEquals(Source, newSource))
220 {
221 DetachSource(Source); // detach from this Source
222 Source = newSource; // set new Source
223 AttachSource(Source); // attach to new Source
224
225 var bb = Source as BusinessBase;
226 if (bb != null)
227 {
228 IsBusy = bb.IsPropertyBusy(PropertyName);
229 }
230 UpdateState();
231 }
232 }
233
237 private void SetBindingValues()
238 {
239 var bindingPath = string.Empty;
240 var propertyName = string.Empty;
241
242 var binding = GetBindingExpression(PropertyProperty);
243 if (binding != null)
244 {
245 if (binding.ParentBinding != null && binding.ParentBinding.Path != null)
246 bindingPath = binding.ParentBinding.Path.Path;
247 else
248 bindingPath = string.Empty;
249 propertyName = (bindingPath.IndexOf('.') > 0)
250 ? bindingPath.Substring(bindingPath.LastIndexOf('.') + 1)
251 : bindingPath;
252 }
253
254 BindingPath = bindingPath;
255 PropertyName = propertyName;
256 }
257
264 protected object GetRealSource(object source, string bindingPath)
265 {
266 var firstProperty = string.Empty;
267 if (bindingPath.IndexOf('.') > 0)
268 firstProperty = bindingPath.Substring(0, bindingPath.IndexOf('.'));
269
270 var icv = source as ICollectionView;
271 if (icv != null && firstProperty != "CurrentItem")
272 source = icv.CurrentItem;
273 if (source != null && !string.IsNullOrEmpty(firstProperty))
274 {
275 var p = MethodCaller.GetProperty(source.GetType(), firstProperty);
276 return GetRealSource(
277 MethodCaller.GetPropertyValue(source, p),
278 bindingPath.Substring(bindingPath.IndexOf('.') + 1));
279 }
280 else
281 return source;
282 }
283
284 private void DetachSource(object source)
285 {
286 var p = source as INotifyPropertyChanged;
287 if (p != null)
288 p.PropertyChanged -= source_PropertyChanged;
289 INotifyBusy busy = source as INotifyBusy;
290 if (busy != null)
291 busy.BusyChanged -= source_BusyChanged;
292
293 ClearState();
294 }
295
296 private void AttachSource(object source)
297 {
298 var p = source as INotifyPropertyChanged;
299 if (p != null)
300 p.PropertyChanged += source_PropertyChanged;
301 INotifyBusy busy = source as INotifyBusy;
302 if (busy != null)
303 busy.BusyChanged += source_BusyChanged;
304
305 }
306
307 void source_PropertyChanged(object sender, PropertyChangedEventArgs e)
308 {
309 if (e.PropertyName == PropertyName || string.IsNullOrEmpty(e.PropertyName))
310 UpdateState();
311 }
312
313 void source_BusyChanged(object sender, BusyChangedEventArgs e)
314 {
315 if (e.PropertyName == PropertyName || string.IsNullOrEmpty(e.PropertyName))
316 {
317 bool busy = e.Busy;
319 if (bb != null)
320 busy = bb.IsPropertyBusy(PropertyName);
321
322 if (busy != IsBusy)
323 {
324 IsBusy = busy;
325 UpdateState();
326 }
327 }
328 }
329
330#endregion
331
332#region BrokenRules property
333
338 public static readonly DependencyProperty BrokenRulesProperty = DependencyProperty.Register(
339 "BrokenRules",
340 typeof(ObservableCollection<BrokenRule>),
341 typeof(PropertyStatus),
342 null);
343
348 [Category("Property Status")]
349 public ObservableCollection<BrokenRule> BrokenRules
350 {
351 get { return (ObservableCollection<BrokenRule>)GetValue(BrokenRulesProperty); }
352 private set { SetValue(BrokenRulesProperty, value); }
353 }
354
355#endregion
356
357#region State properties
358
359 private bool _canRead = true;
364 [Category("Property Status")]
365 public bool CanRead
366 {
367 get { return _canRead; }
368 protected set
369 {
370 if (value != _canRead)
371 {
372 _canRead = value;
373 OnPropertyChanged("CanRead");
374 }
375 }
376 }
377
378 private bool _canWrite = true;
383 [Category("Property Status")]
384 public bool CanWrite
385 {
386 get { return _canWrite; }
387 protected set
388 {
389 if (value != _canWrite)
390 {
391 _canWrite = value;
392 OnPropertyChanged("CanWrite");
393 }
394 }
395 }
396
397 private bool _isBusy = false;
402 [Category("Property Status")]
403 public bool IsBusy
404 {
405 get { return _isBusy; }
406 private set
407 {
408 if (value != _isBusy)
409 {
410 _isBusy = value;
411 OnPropertyChanged("IsBusy");
412 }
413 }
414 }
415
416 private bool _isValid = true;
421 [Category("Property Status")]
422 public bool IsValid
423 {
424 get { return _isValid; }
425 private set
426 {
427 if (value != _isValid)
428 {
429 _isValid = value;
430 OnPropertyChanged("IsValid");
431 }
432 }
433 }
434
435 private RuleSeverity _worst;
442 [Category("Property Status")]
444 {
445 get { return _worst; }
446 private set
447 {
448 if (value != _worst)
449 {
450 _worst = value;
451 OnPropertyChanged("RuleSeverity");
452 }
453 }
454 }
455
456 private string _ruleDescription = string.Empty;
461 [Category("Property Status")]
462 public string RuleDescription
463 {
464 get { return _ruleDescription; }
465 private set
466 {
467 if (value != _ruleDescription)
468 {
469 _ruleDescription = value;
470 OnPropertyChanged("RuleDescription");
471 }
472 }
473 }
474
475#endregion
476
477#region Image
478
479 private void EnablePopup(FrameworkElement image)
480 {
481 if (image != null)
482 {
483 image.MouseEnter += new MouseEventHandler(image_MouseEnter);
484 image.MouseLeave += new MouseEventHandler(image_MouseLeave);
485 }
486 }
487
488 private void DisablePopup(FrameworkElement image)
489 {
490 if (image != null)
491 {
492 image.MouseEnter -= new MouseEventHandler(image_MouseEnter);
493 image.MouseLeave -= new MouseEventHandler(image_MouseLeave);
494 }
495 }
496
497 private void image_MouseEnter(object sender, MouseEventArgs e)
498 {
499 Popup popup = (Popup)FindChild(this, "popup");
500 if (popup != null && sender is UIElement)
501 {
502 popup.Placement = PlacementMode.Mouse;
503 popup.PlacementTarget = (UIElement)sender;
504 ((ItemsControl)popup.Child).ItemsSource = BrokenRules;
505 popup.IsOpen = true;
506 }
507 }
508
509 void popup_Loaded(object sender, RoutedEventArgs e)
510 {
511 (sender as Popup).Loaded -= popup_Loaded;
512 if (((sender as Popup).Child as UIElement).DesiredSize.Height > 0)
513 {
514 _lastPopupSize = ((sender as Popup).Child as UIElement).DesiredSize;
515 }
516 if (_lastAppSize.Width < _lastPosition.X + _popupLastPosition.X + _lastPopupSize.Width)
517 {
518 (sender as Popup).HorizontalOffset = _lastAppSize.Width - _lastPosition.X - _popupLastPosition.X - _lastPopupSize.Width;
519 }
520 if (_lastAppSize.Height < _lastPosition.Y + _popupLastPosition.Y + _lastPopupSize.Height)
521 {
522 (sender as Popup).VerticalOffset = _lastAppSize.Height - _lastPosition.Y - _popupLastPosition.Y - _lastPopupSize.Height;
523 }
524 }
525
526 private void image_MouseLeave(object sender, MouseEventArgs e)
527 {
528 Popup popup = (Popup)FindChild(this, "popup");
529 popup.IsOpen = false;
530 }
531
532 void popup_MouseLeave(object sender, MouseEventArgs e)
533 {
534 Popup popup = (Popup)FindChild(this, "popup");
535 popup.IsOpen = false;
536 }
537
538#endregion
539
540#region State management
541
545 protected virtual void UpdateState()
546 {
547 if (_loading) return;
548
549 Popup popup = (Popup)FindChild(this, "popup");
550 if (popup != null)
551 popup.IsOpen = false;
552
553 if (Source == null || string.IsNullOrEmpty(PropertyName))
554 {
555 BrokenRules.Clear();
556 RuleDescription = string.Empty;
557 IsValid = true;
558 CanWrite = false;
559 CanRead = false;
560 }
561 else
562 {
564 if (iarw != null)
565 {
566 CanWrite = iarw.CanWriteProperty(PropertyName);
567 CanRead = iarw.CanReadProperty(PropertyName);
568 }
569
570 BusinessBase businessObject = Source as BusinessBase;
571 if (businessObject != null)
572 {
573 var allRules = (from r in businessObject.BrokenRulesCollection
574 where r.Property == PropertyName
575 select r).ToArray();
576
577 var removeRules = (from r in BrokenRules
578 where !allRules.Contains(r)
579 select r).ToArray();
580
581 var addRules = (from r in allRules
582 where !BrokenRules.Contains(r)
583 select r).ToArray();
584
585 foreach (var rule in removeRules)
586 BrokenRules.Remove(rule);
587 foreach (var rule in addRules)
588 BrokenRules.Add(rule);
589
590 IsValid = BrokenRules.Count == 0;
591
592 if (!IsValid)
593 {
594 BrokenRule worst = (from r in BrokenRules
595 orderby r.Severity
596 select r).FirstOrDefault();
597
598 if (worst != null)
599 {
600 RuleSeverity = worst.Severity;
602 }
603 else
604 RuleDescription = string.Empty;
605 }
606 else
607 RuleDescription = string.Empty;
608 }
609 else
610 {
611 BrokenRules.Clear();
612 RuleDescription = string.Empty;
613 IsValid = true;
614 }
615 }
616 GoToState(true);
617 }
618
622 private string _lastState;
623
628 protected virtual void ClearState()
629 {
630 _lastState = null;
631 _lastImage = null;
632 }
633
634
639 protected virtual void GoToState(bool useTransitions)
640 {
641 if (_loading) return;
642
643 BusyAnimation busy = FindChild(this, "busy") as BusyAnimation;
644 if (busy != null)
645 busy.IsRunning = IsBusy;
646
647 string newState;
648 if (IsBusy)
649 newState = "Busy";
650 else if (IsValid)
651 newState = "PropertyValid";
652 else
653 newState = RuleSeverity.ToString();
654
655 if (newState != _lastState || _lastImage == null)
656 {
657 _lastState = newState;
658 DisablePopup(_lastImage);
659 VisualStateManager.GoToState(this, newState, useTransitions);
660 if (newState != "Busy" && newState != "PropertyValid")
661 {
662 _lastImage = (FrameworkElement)FindChild(this, string.Format("{0}Image", newState.ToLower()));
663 EnablePopup(_lastImage);
664 }
665 }
666 }
667
668#endregion
669
670#region Helpers
671
678 protected DependencyObject FindChild(DependencyObject parent, string name)
679 {
680 DependencyObject found = null;
681 int count = VisualTreeHelper.GetChildrenCount(parent);
682 for (int x = 0; x < count; x++)
683 {
684 DependencyObject child = VisualTreeHelper.GetChild(parent, x);
685 string childName = child.GetValue(FrameworkElement.NameProperty) as string;
686 if (childName == name)
687 {
688 found = child;
689 break;
690 }
691 else found = FindChild(child, name);
692 }
693
694 return found;
695 }
696
697#endregion
698
699#region INotifyPropertyChanged Members
700
704 public event PropertyChangedEventHandler PropertyChanged;
705
710 protected virtual void OnPropertyChanged(string propertyName)
711 {
712 if (PropertyChanged != null)
713 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
714 }
715
716#endregion
717 }
718}
719#endif
This is the base class from which most business objects will be derived.
Definition: BusinessBase.cs:38
This is the non-generic base class from which most business objects will be derived.
virtual bool IsPropertyBusy(Csla.Core.IPropertyInfo property)
Gets a value indicating whether a specific property is busy (has a currently executing async rule).
virtual Rules.BrokenRulesCollection BrokenRulesCollection
Provides access to the readonly collection of broken business rules for this object.
Event arguments for the BusyChanged event.
string PropertyName
Property for which the Busy value has changed.
Stores details about a specific broken business rule.
Definition: BrokenRule.cs:19
string Description
Provides access to the description of the broken rule.
Definition: BrokenRule.cs:55
RuleSeverity Severity
Gets the severity of the broken rule.
Definition: BrokenRule.cs:77
bool IsRunning
Gets or sets a property controlling whether the animation is running.
Displays validation information for a business object property, and manipulates an associated UI cont...
string BindingPath
Gets or sets the binding path.
override void OnApplyTemplate()
Applies the visual template.
string PropertyName
Gets or sets the name of the property.
object Property
Gets or sets the source business property to which this control is bound.
bool IsValid
Gets a value indicating whether the property is valid.
static readonly DependencyProperty PropertyProperty
Gets or sets the source business property to which this control is bound.
static readonly DependencyProperty BrokenRulesProperty
Gets the broken rules collection from the business object.
virtual void OnPropertyChanged(string propertyName)
Raises the PropertyChanged event.
ObservableCollection< BrokenRule > BrokenRules
Gets the broken rules collection from the business object.
virtual void SetSource(object dataItem)
Sets the source binding and updates status.
virtual void GoToState(bool useTransitions)
Updates the status of the Property in UI
bool IsBusy
Gets a value indicating whether the property is busy with an asynchronous operation.
PropertyStatus()
Creates an instance of the object.
DependencyObject FindChild(DependencyObject parent, string name)
Find child dependency property.
virtual void SetSource(bool propertyValueChanged)
Sets the source binding and updates status.
object Source
Gets or sets the Source.
PropertyChangedEventHandler PropertyChanged
Event raised when a property has changed.
bool IsReadOnly
Gets or sets a value indicating whether this DependencyProperty field is read only.
bool CanWrite
Gets a value indicating whether the user is authorized to write the property.
string RuleDescription
Gets the description of the most severe broken rule for this property.
object GetRealSource(object source, string bindingPath)
Gets the real source helper method.
bool CanRead
Gets a value indicating whether the user is authorized to read the property.
virtual void UpdateState()
Updates the state on control Property.
virtual void ClearState()
Clears the state.
Interface defining an object that notifies when it is busy executing an asynchronous operation.
Definition: INotifyBusy.cs:17
BusyChangedEventHandler BusyChanged
Event raised when the object's busy status changes.
Definition: INotifyBusy.cs:22
Defines the authorization interface through which an object can indicate which properties the current...
RuleSeverity
Values for validation rule severities.
Definition: RuleSeverity.cs:16