9using System.Collections.Generic;
10using System.ComponentModel;
11using System.Windows.Forms;
23 [ProvideProperty(
"ActionType", typeof (ToolStripButton))]
24 [ProvideProperty(
"PostSaveAction", typeof (ToolStripButton))]
25 [ProvideProperty(
"RebindAfterSave", typeof (ToolStripButton))]
26 [ProvideProperty(
"DisableWhenClean", typeof (ToolStripButton))]
27 [ProvideProperty(
"DisableWhenUseless", typeof (ToolStripButton))]
28 [ProvideProperty(
"CommandName", typeof (ToolStripButton))]
39 _container = container;
45 #region Member variables
47 private Dictionary<ToolStripButton, CslaActionExtenderProperties> _sources =
48 new Dictionary<ToolStripButton, CslaActionExtenderProperties>();
50 private object _dataSource =
null;
51 private bool _autoShowBrokenRules =
true;
52 private bool _warnIfCloseOnDirty =
true;
54 private bool _warnOnCancel =
false;
57 private IContainer _container =
null;
59 private bool _closeForm =
false;
63 #region IExtenderProvider implementation
65 bool IExtenderProvider.CanExtend(
object extendee)
67 return extendee is ToolStripButton;
72 #region Public properties
78 [Description(
"Gets or sets the data source to which this button is bound for action purposes.")]
79 [AttributeProvider(typeof (IListSource))]
82 get {
return _dataSource; }
87 if (value is BindingSource)
99 [Category(
"Behavior")]
100 [Description(
"If True, then the broken rules will be displayed in a message box, should the object be invalid.")]
105 get {
return _autoShowBrokenRules; }
106 set { _autoShowBrokenRules = value; }
113 [Category(
"Behavior")]
114 [Description(
"If True, then the control (when set to Close mode) will warn the user if the object is currently dirty.")]
119 get {
return _warnIfCloseOnDirty; }
120 set { _warnIfCloseOnDirty = value; }
127 [Category(
"Behavior")]
128 [Description(
"Gets or sets the confirmation message that will display if a Close button is pressed and the object is dirty.")]
130 [DefaultValue(
"Object is currently in a dirty changed.")]
134 get {
return _dirtyWarningMessage; }
135 set { _dirtyWarningMessage = value; }
142 [Category(
"Behavior")]
143 [Description(
"If True, then the Cancel button will warn when pressed and the object is dirty.")]
145 [DefaultValue(
false)]
148 get {
return _warnOnCancel; }
149 set { _warnOnCancel = value; }
156 [Category(
"Behavior")]
157 [Description(
"If the WarnOnCancel property is set to True, this is the message to be displayed.")]
159 [DefaultValue(
"Are you sure you want to revert to the previous values?")]
163 get {
return _warnOnCancelMessage; }
164 set { _warnOnCancelMessage = value; }
171 [Category(
"Behavior")]
172 [Description(
"When a button with a Validate ActionType is pressed when the object is valid, this is the message to be displayed.")]
174 [DefaultValue(
"Object is valid.")]
178 get {
return _objectIsValidMessage; }
179 set { _objectIsValidMessage = value; }
184 #region Property accessor methods
194 [Description(
"Gets or sets the action type for this button.")]
199 if (_sources.ContainsKey(ctl))
200 return _sources[ctl].ActionType;
202 return CslaActionExtenderProperties.ActionTypeDefault;
211 [Description(
"Gets or sets the action type for this button.")]
216 if (_sources.ContainsKey(ctl))
217 _sources[ctl].ActionType = value;
220 CslaActionExtenderProperties props =
new CslaActionExtenderProperties();
221 props.ActionType = value;
222 _sources.Add(ctl, props);
228 #region PostSaveAction
236 [Description(
"Gets or sets the action performed after a save (if ActionType is set to Save).")]
241 if (_sources.ContainsKey(ctl))
242 return _sources[ctl].PostSaveAction;
244 return CslaActionExtenderProperties.PostSaveActionDefault;
253 [Description(
"Gets or sets the action performed after a save (if ActionType is set to Save).")]
258 if (_sources.ContainsKey(ctl))
259 _sources[ctl].PostSaveAction = value;
262 CslaActionExtenderProperties props =
new CslaActionExtenderProperties();
263 props.PostSaveAction = value;
264 _sources.Add(ctl, props);
270 #region RebindAfterSave
277 [Description(
"Determines if the binding source will rebind after business object saves.")]
282 if (_sources.ContainsKey(ctl))
283 return _sources[ctl].RebindAfterSave;
285 return CslaActionExtenderProperties.RebindAfterSaveDefault;
294 [Description(
"Determines if the binding source will rebind after business object saves.")]
299 if (_sources.ContainsKey(ctl))
300 _sources[ctl].RebindAfterSave = value;
303 CslaActionExtenderProperties props =
new CslaActionExtenderProperties();
304 props.RebindAfterSave = value;
305 _sources.Add(ctl, props);
311 #region DisableWhenClean
318 [Description(
"If True, then the dirtiness of the underlying business object will cause this button to be enabled or disabled.")]
320 [DefaultValue(
false)]
321 [Obsolete(
"Use instead DisableWhenUseless")]
325 if (_sources.ContainsKey(ctl))
326 return _sources[ctl].DisableWhenClean;
328 return CslaActionExtenderProperties.DisableWhenCleanDefault;
337 [Description(
"If True, then the dirtiness of the underlying business object will cause this button to be enabled or disabled.")]
340 [Obsolete(
"Use instead DisableWhenUseless")]
344 if (_sources.ContainsKey(ctl))
345 _sources[ctl].DisableWhenClean = value;
348 CslaActionExtenderProperties props =
new CslaActionExtenderProperties();
349 props.DisableWhenClean = value;
350 _sources.Add(ctl, props);
356 #region DisableWhenUseless
363 [Description(
"If True, then the status of the underlying business object will cause this button to be enabled or disabled.")]
365 [DefaultValue(
false)]
368 if (_sources.ContainsKey(ctl))
369 return _sources[ctl].DisableWhenUseless;
371 return CslaActionExtenderProperties.DisableWhenUselessDefault;
380 [Description(
"If True, then the status of the underlying business object will cause this button to be enabled or disabled.")]
385 if (_sources.ContainsKey(ctl))
386 _sources[ctl].DisableWhenUseless = value;
389 CslaActionExtenderProperties props =
new CslaActionExtenderProperties();
390 props.DisableWhenUseless = value;
391 _sources.Add(ctl, props);
404 [Description(
"Gets or sets the name of this command control for unique identification purposes.")]
409 if (_sources.ContainsKey(ctl))
410 return _sources[ctl].CommandName;
412 return CslaActionExtenderProperties.CommandNameDefault;
421 [Description(
"Gets or sets the name of this command control for unique identification purposes.")]
426 if (_sources.ContainsKey(ctl))
427 _sources[ctl].CommandName = value;
430 CslaActionExtenderProperties props =
new CslaActionExtenderProperties();
431 props.CommandName = value;
432 _sources.Add(ctl, props);
440 #region Event declarations
446 [Description(
"Event fires just before the attempted action.")]
447 public event EventHandler<CslaActionCancelEventArgs>
Clicking;
453 [Description(
"Event fires after a successful action. When button is set to Save, this event will only fire upon a successful save. If button is set to Close, this event will never fire.")]
454 public event EventHandler<CslaActionEventArgs>
Clicked;
460 [Description(
"Event fires upon encountering any exception during an action.")]
467 [Description(
"Event fires upon a successful save when the PostSaveAction property is set to AndNew.")]
468 public event EventHandler<CslaActionEventArgs>
SetForNew;
474 [Description(
"Event fires when the object is in an invalid state. Note that this event will work in conjunction with the InvalidateOnWarnings and InvalidateOnInformation properties.")]
481 [Description(
"Event fires if there are any broken rules at all, despite severity.")]
488 [Description(
"Fires just before a save action is performed.")]
495 [Description(
"Fires immediately after the underlying object successfully saves.")]
500 #region OnEvent methods
584 #region Public methods
592 InitializeControls(
true);
594 BindingSource rootSource = _dataSource as BindingSource;
596 if (rootSource !=
null)
598 AddEventHooks(objectToBind);
601 _bindingSourceTree = BindingSourceHelper.InitializeBindingSourceTree(_container, rootSource);
602 _bindingSourceTree.
Bind(objectToBind);
605 private void AddEventHooks(
ISavable objectToBind)
608 RemoveEventHooks(objectToBind);
610 INotifyPropertyChanged propChangedObjParent = objectToBind as INotifyPropertyChanged;
611 if (propChangedObjParent !=
null)
613 propChangedObjParent.PropertyChanged += propChangedObj_PropertyChanged;
617 if (propChangedObjChild !=
null)
619 propChangedObjChild.
ChildChanged += propChangedObj_ChildChanged;
623 private void RemoveEventHooks(
ISavable objectToBind)
625 INotifyPropertyChanged propChangedObjParent = objectToBind as INotifyPropertyChanged;
626 if (propChangedObjParent !=
null)
628 propChangedObjParent.PropertyChanged -= propChangedObj_PropertyChanged;
632 if (propChangedObjChild !=
null)
634 propChangedObjChild.
ChildChanged -= propChangedObj_ChildChanged;
643 private void propChangedObj_PropertyChanged(
object sender, PropertyChangedEventArgs e)
650 #region Protected methods
657 protected void OnClick(
object sender, EventArgs e)
659 ToolStripButton ctl = (ToolStripButton) sender;
660 CslaActionExtenderProperties props = _sources[ctl];
665 bool raiseClicked =
true;
672 BindingSource source =
null;
674 var sourceObjectError =
false;
675 if (_dataSource !=
null)
677 source = _dataSource as BindingSource;
681 savableObject = source.DataSource as
ISavable;
687 sourceObjectError =
true;
690 if (savableObject ==
null || trackableObject ==
null)
693 sourceObjectError =
true;
697 if (!sourceObjectError)
699 DialogResult diagResult;
701 switch (props.ActionType)
704 raiseClicked = ExecuteSaveAction(savableObject, trackableObject, props);
710 diagResult = DialogResult.Yes;
711 if (_warnOnCancel && trackableObject.
IsDirty)
712 diagResult = MessageBox.Show(
714 MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
716 if (diagResult == DialogResult.Yes)
717 _bindingSourceTree.
Cancel(savableObject);
724 diagResult = DialogResult.Yes;
727 if (_warnIfCloseOnDirty)
728 diagResult = MessageBox.Show(
733 if (diagResult == DialogResult.Yes)
735 _bindingSourceTree.
Close();
749 string brokenRules =
string.Empty;
752 var lambdaBrokenRule = brokenRule;
754 PropertyInfoManager.GetRegisteredProperties(businessObject.GetType()).Find(
755 c => c.Name == lambdaBrokenRule.Property).FriendlyName;
756 brokenRules +=
string.Format(
"{0}: {1}{2}", friendlyName, brokenRule, Environment.NewLine);
759 MessageBoxButtons.OK, MessageBoxIcon.Error);
764 MessageBoxButtons.OK, MessageBoxIcon.Information);
779 if (props.RebindAfterSave)
783 _bindingSourceTree.ResetBindings(
false);
784 InitializeControls(
true);
790 InitializeControls(
true);
812 #region Private methods
814 private bool ExecuteSaveAction(
ISavable savableObject,
ITrackStatus trackableObject, CslaActionExtenderProperties props)
817 bool okToContinue =
true;
820 bool savableObjectIsBusinessBase = savableObject is
BusinessBase;
821 if (savableObjectIsBusinessBase)
824 if (savableObjectIsBusinessBase)
833 _autoShowBrokenRules);
837 okToContinue = !argsHasBrokenRules.Cancel;
845 if (savableObjectIsBusinessBase)
847 if (_autoShowBrokenRules && !businessObject.
IsValid)
849 string brokenRules =
string.Empty;
852 var lambdaBrokenRule = brokenRule;
854 PropertyInfoManager.GetRegisteredProperties(businessObject.GetType()).Find(
855 c => c.Name == lambdaBrokenRule.Property).FriendlyName;
856 brokenRules +=
string.Format(
"{0}: {1}{2}", friendlyName, brokenRule, Environment.NewLine);
859 MessageBoxButtons.OK, MessageBoxIcon.Error);
865 CslaActionCancelEventArgs savingArgs =
new CslaActionCancelEventArgs(
false, props.CommandName);
868 if (!savingArgs.Cancel)
870 _bindingSourceTree.
Apply();
874 objectToSave = ((ICloneable)savableObject).Clone() as
ISavable;
876 objectToSave = savableObject;
878 if (objectToSave !=
null)
882 RemoveEventHooks(savableObject);
887 switch (props.PostSaveAction)
891 if (props.RebindAfterSave)
893 _bindingSourceTree.
Bind(savableObject);
894 AddEventHooks(savableObject);
905 OnSetForNew(
new CslaActionEventArgs(props.CommandName));
906 AddEventHooks(savableObject);
912 _bindingSourceTree.
Bind(objectToSave);
913 AddEventHooks(objectToSave);
914 OnErrorEncountered(
new ErrorEncounteredEventArgs(props.CommandName,
new ObjectSaveException(ex)));
925 _bindingSourceTree.SetEvents(
true);
944 private void ResetControls()
946 InitializeControls(
false);
949 private void InitializeControls(
bool initialEnabling)
952 List<ToolStripButton> extendedControls =
new List<ToolStripButton>();
953 foreach (KeyValuePair<ToolStripButton, CslaActionExtenderProperties> pair
in _sources)
957 ToolStripButton ctl = pair.Key;
960 if (pair.Value.DisableWhenUseless || pair.Value.DisableWhenClean)
961 ChangeEnabled(ctl, !(pair.Value.DisableWhenUseless || pair.Value.DisableWhenClean));
965 InitializeControl(ctl, pair);
966 extendedControls.Add(ctl);
971 private void InitializeControl(ToolStripButton ctl, KeyValuePair<ToolStripButton, CslaActionExtenderProperties> pair)
973 if (pair.Value.DisableWhenUseless || (pair.Value.DisableWhenClean && !ctl.Enabled))
975 ISavable businessObject = GetBusinessObject();
976 if (businessObject !=
null)
979 if (trackableObject !=
null)
981 if (pair.Value.ActionType ==
CslaFormAction.Cancel || pair.Value.DisableWhenClean)
991 private void ChangeEnabled(ToolStripButton ctl,
bool newEnabled)
994 if (ctl.Enabled != newEnabled)
995 ctl.Enabled = newEnabled;
998 private void CloseForm()
1000 if (_sources.Count > 0)
1002 Dictionary<ToolStripButton, CslaActionExtenderProperties>.Enumerator enumerator = _sources.GetEnumerator();
1003 if (enumerator.MoveNext())
1005 ToolStripButton ctl = enumerator.Current.Key;
1006 Form frm = GetParentForm(ctl);
1013 private Form GetParentForm(ToolStripButton thisToolStripButton)
1015 return GetParentForm(thisToolStripButton.GetCurrentParent());
1018 private Form GetParentForm(Control thisControl)
1022 if (thisControl.Parent is Form)
1023 frm = (Form) thisControl.Parent;
1025 frm = GetParentForm(thisControl.Parent);
1030 private ISavable GetBusinessObject()
1033 BindingSource source = _dataSource as BindingSource;
1035 businessObject = source.DataSource as
ISavable;
1037 return businessObject;
This is the base class from which most business objects will be derived.
virtual bool IsValid
Returns true if the object and its child objects are currently valid, false if the object or any of i...
BrokenRulesCollection GetBrokenRules()
Gets the broken rules for this object
Contains event data about the changed child object.
A strongly-typed resource class, for looking up localized strings, etc.
static string ActionExtenderInvalidBindingSourceCast
Looks up a localized string similar to DataSource does not cast to a BindingSource.
static string ActionExtenderErrorCaption
Looks up a localized string similar to Error.
static string ActionExtenderSourceMustBeBindingSource
Looks up a localized string similar to DataSource must be a BindingSource control instance.
static string ActionExtenderWarnOnCancelMessagePropertyDefault
Looks up a localized string similar to Are you sure you want to revert to the previous values?...
static string ActionExtenderCloseConfirmation
Looks up a localized string similar to Are you sure you want to close?.
static string ActionExtenderObjectIsValidMessagePropertyDefault
Looks up a localized string similar to Object is valid.
static string Warning
Looks up a localized string similar to Warning.
static string ActionExtenderInformationCaption
Looks up a localized string similar to Information.
static string ActionExtenderDirtyWarningMessagePropertyDefault
Looks up a localized string similar to Object is currently in a dirty changed.
static string ActionExtenderInvalidBusinessObjectBaseCast
Looks up a localized string similar to The underlying data source does not cast to a CSLA BusinessBas...
Maintains a reference to a BindingSource object on the form.
void Close()
Disconnects from the BindingSource object.
void Apply()
Applies changes to the business object.
void Cancel(object businessObject)
Cancels changes to the business object.
void Bind(object objectToBind)
Binds a business object to the BindingSource.
Event args providing information about a canceled action.
Event args for an action.
Event args indicating an error.
Event args object containing information about a broken rule.
bool AutoShowBrokenRules
Gets a value indicating whether to show broken rules.
Implemented by classes that notify when a child object has changed.
EventHandler< ChildChangedEventArgs > ChildChanged
Event indictating that a child object has changed.
Specifies that the object can save itself.
object Save()
Saves the object to the database.
Defines the common properties required objects that track their own status.
bool IsDeleted
Returns true if this object is marked for deletion.
bool IsNew
Returns true if this is a new object, false if it is a pre-existing object.
bool IsValid
Returns true if the object and its child objects are currently valid, false if the object or any of i...
bool IsDirty
Returns true if this object's data, or any of its fields or child objects data, has been changed.
CslaFormAction
The possible form actions.
PostSaveActionType
The possible actions for post save.