Created
September 15, 2012 13:42
-
-
Save davybrion/3727959 to your computer and use it in GitHub Desktop.
code snippets for "MVP In Silverlight/WPF: Some Infrastructure Bits" post
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PropertyValidation<TBindingModel> | |
where TBindingModel : BindingModel<TBindingModel> | |
{ | |
private Func<TBindingModel, bool> validationCriteria; | |
private string errorMessage; | |
private readonly string propertyName; | |
public PropertyValidation(string propertyName) | |
{ | |
this.propertyName = propertyName; | |
} | |
public PropertyValidation<TBindingModel> WithMessage(string errorMessage) | |
{ | |
if (this.errorMessage != null) | |
{ | |
throw new InvalidOperationException("You can only set the message once."); | |
} | |
this.errorMessage = errorMessage; | |
return this; | |
} | |
public PropertyValidation<TBindingModel> When(Func<TBindingModel, bool> validationCriteria) | |
{ | |
if (this.validationCriteria != null) | |
{ | |
throw new InvalidOperationException("You can only set the validation criteria once."); | |
} | |
this.validationCriteria = validationCriteria; | |
return this; | |
} | |
public bool IsInvalid(TBindingModel presentationModel) | |
{ | |
if (validationCriteria == null) | |
{ | |
throw new InvalidOperationException("No criteria have been provided for this validation. (Use the 'When(..)' method.)"); | |
} | |
return validationCriteria(presentationModel); | |
} | |
public string GetErrorMessage() | |
{ | |
if (errorMessage == null) | |
{ | |
throw new InvalidOperationException("No error message has been set for this validation. (Use the 'WithMessage(..)' method.)"); | |
} | |
return errorMessage; | |
} | |
public string PropertyName | |
{ | |
get { return propertyName; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class BindingModel<TBindingModel> : INotifyPropertyChanged, INotifyDataErrorInfo | |
where TBindingModel : BindingModel<TBindingModel> | |
{ | |
public event PropertyChangedEventHandler PropertyChanged = delegate { }; | |
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { }; | |
private readonly List<PropertyValidation<TBindingModel>> validations = new List<PropertyValidation<TBindingModel>>(); | |
private Dictionary<string, List<string>> errorMessages = new Dictionary<string, List<string>>(); | |
protected BindingModel() | |
{ | |
PropertyChanged += (s, e) => { if (e.PropertyName != "HasErrors") ValidateProperty(e.PropertyName); }; | |
} | |
public IEnumerable GetErrors(string propertyName) | |
{ | |
if (errorMessages.ContainsKey(propertyName)) return errorMessages[propertyName]; | |
return new string[0]; | |
} | |
public bool HasErrors | |
{ | |
get { return errorMessages.Count > 0; } | |
} | |
public void ValidateAll() | |
{ | |
var propertyNamesWithValidationErrors = errorMessages.Keys; | |
ClearAllErrorMessages(); | |
validations.ForEach(PerformValidation); | |
var propertyNamesThatMightHaveChangedValidation = errorMessages.Keys.Union(propertyNamesWithValidationErrors).ToList(); | |
propertyNamesThatMightHaveChangedValidation.ForEach(NotifyErrorsChanged); | |
NotifyPropertyChanged(m => m.HasErrors); | |
} | |
public void ValidateProperty(Expression<Func<TBindingModel, object>> expression) | |
{ | |
ValidateProperty(GetPropertyName(expression)); | |
} | |
private void ValidateProperty(string propertyName) | |
{ | |
ClearErrorMessagesForProperty(propertyName); | |
validations.Where(v => v.PropertyName == propertyName).ToList().ForEach(PerformValidation); | |
NotifyErrorsChanged(propertyName); | |
NotifyPropertyChanged(m => m.HasErrors); | |
} | |
protected PropertyValidation<TBindingModel> AddValidationFor(Expression<Func<TBindingModel, object>> expression) | |
{ | |
var validation = new PropertyValidation<TBindingModel>(GetPropertyName(expression)); | |
validations.Add(validation); | |
return validation; | |
} | |
private void NotifyErrorsChanged(string propertyName) | |
{ | |
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName)); | |
} | |
private void PerformValidation(PropertyValidation<TBindingModel> validation) | |
{ | |
if (validation.IsInvalid((TBindingModel)this)) | |
{ | |
AddErrorMessageForProperty(validation.PropertyName, validation.GetErrorMessage()); | |
} | |
} | |
private void AddErrorMessageForProperty(string propertyName, string errorMessage) | |
{ | |
if (errorMessages.ContainsKey(propertyName)) | |
{ | |
errorMessages[propertyName].Add(errorMessage); | |
} | |
else | |
{ | |
errorMessages.Add(propertyName, new List<string> { errorMessage }); | |
} | |
} | |
private void ClearAllErrorMessages() | |
{ | |
errorMessages = new Dictionary<string, List<string>>(); | |
} | |
private void ClearErrorMessagesForProperty(string propertyName) | |
{ | |
errorMessages.Remove(propertyName); | |
} | |
protected void NotifyPropertyChanged(string propertyName) | |
{ | |
PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); | |
} | |
protected void NotifyPropertyChanged(Expression<Func<TBindingModel, object>> expression) | |
{ | |
NotifyPropertyChanged(GetPropertyName(expression)); | |
} | |
private static string GetPropertyName(Expression<Func<TBindingModel, object>> expression) | |
{ | |
MemberExpression memberExpression; | |
if (expression.Body is UnaryExpression) | |
{ | |
memberExpression = ((UnaryExpression)expression.Body).Operand as MemberExpression; | |
} | |
else | |
{ | |
memberExpression = expression.Body as MemberExpression; | |
} | |
if (memberExpression == null) | |
{ | |
throw new InvalidOperationException("You must specify a property!"); | |
} | |
return memberExpression.Member.Name; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AddValidationFor(m => m.Name) | |
.When(m => string.IsNullOrWhiteSpace(m.name)) | |
.WithMessage("name is a required field"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private string name; | |
public string Name | |
{ | |
get { return name; } | |
set | |
{ | |
name = value; | |
NotifyPropertyChanged(m => m.Name); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class Presenter<TView, TBindingModel> | |
where TView : IView | |
where TBindingModel : BindingModel<TBindingModel>, new() | |
{ | |
protected Presenter(TView view, IEventAggregator eventAggregator, IAsyncRequestDispatcherFactory requestDispatcherFactory) | |
{ | |
View = view; | |
EventAggregator = eventAggregator; | |
RequestDispatcherFactory = requestDispatcherFactory; | |
BindingModel = new TBindingModel(); | |
} | |
protected IEventAggregator EventAggregator { get; private set; } | |
protected IAsyncRequestDispatcherFactory RequestDispatcherFactory { get; private set; } | |
protected TView View { get; private set; } | |
public TBindingModel BindingModel { get; private set; } | |
public virtual void Initialize() {} | |
protected void PublishRemoteException(ExceptionInfo exceptionInfo) | |
{ | |
EventAggregator.Publish(new RemoteExceptionOccurredEvent(exceptionInfo)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IView : IDisposable | |
{ | |
void Hide(); | |
void Show(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class View : UserControl, IView | |
{ | |
private dynamic presenter; | |
private static readonly bool inDesignMode = | |
(null == Application.Current) || Application.Current.GetType() == typeof(Application); | |
protected TPresenter CreateAndInitializePresenter<TPresenter>() | |
{ | |
if (inDesignMode) | |
{ | |
return default(TPresenter); | |
} | |
presenter = IoC.Container.Resolve<TPresenter>(new Dictionary<string, object> { { "view", this } }); | |
presenter.Initialize(); | |
DataContext = presenter.BindingModel; | |
return presenter; | |
} | |
public void Hide() | |
{ | |
Visibility = Visibility.Collapsed; | |
} | |
public void Show() | |
{ | |
Visibility = Visibility.Visible; | |
} | |
public void Dispose() | |
{ | |
// naive implementation of Dispose, but then again, this is just a sample | |
IoC.Container.Release(presenter); | |
CleanUpChildrenOf(this); | |
} | |
private void CleanUpChildrenOf(object obj) | |
{ | |
var dependencyObject = obj as DependencyObject; | |
if (dependencyObject != null) | |
{ | |
var count = VisualTreeHelper.GetChildrenCount(dependencyObject); | |
for (int i = 0; i < count; i++) | |
{ | |
var child = VisualTreeHelper.GetChild(dependencyObject, i); | |
if (child != null) | |
{ | |
DisposeIfDisposable(child); | |
StopIfProgressBar(child); | |
CleanUpChildrenOf(child); | |
} | |
} | |
} | |
} | |
private static void DisposeIfDisposable(DependencyObject child) | |
{ | |
var disposable = child as IDisposable; | |
if (disposable != null) disposable.Dispose(); | |
} | |
private static void StopIfProgressBar(DependencyObject child) | |
{ | |
var progressBar = child as ProgressBar; | |
if (progressBar != null) progressBar.IsIndeterminate = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment