The initial problem is enough known - "Cannot animate '(0).(1)' on an immutable object instance".
There are many questions here in SO about it but all the solutions are more fixes or crutches. And most of questions are linked to concrete part of code.
Also there are few topics about this problem with possible causes:
https://wpftutorial.net/DebuggingAnimations.html
https://blogs.msdn.microsoft.com/mikehillberg/2006/09/25/tip-cannot-animate-on-an-immutable-object-instance/
I have huge corporate app where a have hundreds styles and storyboards. I can't disable them step by step and it's painstaking work to looking for problem part of code.
I look at these bug not from side of looking for in many xamls but from side of loging. I tried to research info in InvalidOperationException that is raised but there is no useful info like control place in xaml or smth else.
Also one idea is to create class inherited from Storyboard and to override methods.
But there is no methods to override.
Can someone propose how to log the internality of storyboard or other class that is responsible of animation?
At last I found sulution.
You should add classes: listener to animation, AttachedProperty and custom StoryBoard.
public static class TriggerTracing
{
static TriggerTracing()
{
// Initialise WPF Animation tracing and add a TriggerTraceListener
PresentationTraceSources.Refresh();
PresentationTraceSources.AnimationSource.Listeners.Clear();
PresentationTraceSources.AnimationSource.Listeners.Add(new TriggerTraceListener());
PresentationTraceSources.AnimationSource.Switch.Level = SourceLevels.All;
}
#region TriggerName attached property
/// <summary>
/// Gets the trigger name for the specified trigger. This will be used
/// to identify the trigger in the debug output.
/// </summary>
/// <param name="trigger">The trigger.</param>
/// <returns></returns>
public static string GetTriggerName(TriggerBase trigger)
{
return (string)trigger.GetValue(TriggerNameProperty);
}
/// <summary>
/// Sets the trigger name for the specified trigger. This will be used
/// to identify the trigger in the debug output.
/// </summary>
/// <param name="trigger">The trigger.</param>
/// <returns></returns>
public static void SetTriggerName(TriggerBase trigger, string value)
{
trigger.SetValue(TriggerNameProperty, value);
}
public static readonly DependencyProperty TriggerNameProperty =
DependencyProperty.RegisterAttached(
"TriggerName",
typeof(string),
typeof(TriggerTracing),
new UIPropertyMetadata(string.Empty));
#endregion
#region TraceEnabled attached property
/// <summary>
/// Gets a value indication whether trace is enabled for the specified trigger.
/// </summary>
/// <param name="trigger">The trigger.</param>
/// <returns></returns>
public static bool GetTraceEnabled(TriggerBase trigger)
{
return (bool)trigger.GetValue(TraceEnabledProperty);
}
/// <summary>
/// Sets a value specifying whether trace is enabled for the specified trigger
/// </summary>
/// <param name="trigger"></param>
/// <param name="value"></param>
public static void SetTraceEnabled(TriggerBase trigger, bool value)
{
trigger.SetValue(TraceEnabledProperty, value);
}
public static readonly DependencyProperty TraceEnabledProperty =
DependencyProperty.RegisterAttached(
"TraceEnabled",
typeof(bool),
typeof(TriggerTracing),
new UIPropertyMetadata(false, OnTraceEnabledChanged));
private static void OnTraceEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var triggerBase = d as EventTrigger;
if (triggerBase == null)
return;
if (!(e.NewValue is bool))
return;
if ((bool)e.NewValue)
{
// insert dummy story-boards which can later be traced using WPF animation tracing
var storyboard = new TriggerTraceStoryboard(triggerBase, TriggerTraceStoryboardType.Enter);
triggerBase.Actions.Insert(0, new BeginStoryboard() { Storyboard = storyboard });
//storyboard = new TriggerTraceStoryboard(triggerBase, TriggerTraceStoryboardType.Exit);
//triggerBase.ExitActions.Insert(0, new BeginStoryboard() { Storyboard = storyboard });
}
else
{
// remove the dummy storyboards
//foreach (TriggerActionCollection actionCollection in new[] { triggerBase.EnterActions, triggerBase.ExitActions })
foreach (TriggerActionCollection actionCollection in new[] { triggerBase.Actions })
{
foreach (TriggerAction triggerAction in actionCollection)
{
BeginStoryboard bsb = triggerAction as BeginStoryboard;
if (bsb != null && bsb.Storyboard != null && bsb.Storyboard is TriggerTraceStoryboard)
{
actionCollection.Remove(bsb);
break;
}
}
}
}
}
#endregion
private enum TriggerTraceStoryboardType
{
Enter, Exit
}
/// <summary>
/// A dummy storyboard for tracing purposes
/// </summary>
private class TriggerTraceStoryboard : Storyboard
{
public TriggerTraceStoryboardType StoryboardType { get; private set; }
public TriggerBase TriggerBase { get; private set; }
public TriggerTraceStoryboard(TriggerBase triggerBase, TriggerTraceStoryboardType storyboardType)
{
TriggerBase = triggerBase;
StoryboardType = storyboardType;
}
}
/// <summary>
/// A custom tracelistener.
/// </summary>
private class TriggerTraceListener : TraceListener
{
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
{
base.TraceEvent(eventCache, source, eventType, id, format, args);
if (format.StartsWith("Storyboard has begun;"))
{
TriggerTraceStoryboard storyboard = args[1] as TriggerTraceStoryboard;
if (storyboard != null)
{
// add a breakpoint here to see when your trigger has been
// entered or exited
// the element being acted upon
object targetElement = args[5];
// the namescope of the element being acted upon
INameScope namescope = (INameScope)args[7];
TriggerBase triggerBase = storyboard.TriggerBase;
string triggerName = GetTriggerName(storyboard.TriggerBase);
var str = "";
var element = targetElement as DependencyObject;
while (element != null)
{
str += element.ToString() + Environment.NewLine;
element = VisualTreeHelper.GetParent(element);
}
LoggingInfrastructure.DefaultLogger.Log(...);
}
}
}
public override void Write(string message)
{
}
public override void WriteLine(string message)
{
}
}
Then you could add property to xaml where you need:
<EventTrigger Ui:TriggerTracing.TriggerName="CopyTextBlockStyle PreviewMouseLeftButtonDown"
Ui:TriggerTracing.TraceEnabled="True" RoutedEvent="PreviewMouseLeftButtonDown">
Related
I have a Timer and three buttons to control it: Start, Stop, and Pause.
Each button is bound to a RelayCommand.
I have a TimerState property of type enum TimerState. (This is useful for setting various GUI elements.)
Is there a way to somehow bind the RelayCommands' CanExecute functionality to the TimerState property?
Currently, I have 3 methods that look like this:
private bool CanStartTimer()
{
return (TimerState == TimerState.Stopped || TimerState == TimerState.Paused);
}
In the TimerState setter, I call
StartTimerCmd.RaiseCanExecuteChanged();
Is there a better way bind the CanExecute state of the RelayCommands to a property like TimerState?
Thanks for any insight.
I've implemented a class to handle commands, actually it's based on DelegateCommand because i'm using PRISM but it could easily be changed to be used with RelayCommand or any other class implementing ICommand
It could have bugs, i've not yet fully tested it, however it works fine in my scenarios, here it is:
public class MyDelegateCommand<TViewModel> : DelegateCommand where TViewModel : INotifyPropertyChanged {
private List<string> _PropertiesToWatch;
public MyDelegateCommand(TViewModel viewModelInstance, Action executedMethod)
: base(executedMethod) {
}
public MyDelegateCommand(TViewModel viewModelInstance, Action executedMethod, Func<bool> canExecuteMethod)
: base(executedMethod, canExecuteMethod) {
}
/// <summary>
///
/// </summary>
/// <param name="viewModelInstance"></param>
/// <param name="executedMethod"></param>
/// <param name="selector"></param>
public MyDelegateCommand(TViewModel viewModelInstance, Action executedMethod, Func<bool> canExecuteMethod, Expression<Func<TViewModel, object>> propertiesToWatch)
: base(executedMethod, canExecuteMethod) {
_PropertiesToWatch = RegisterPropertiesWatcher(propertiesToWatch);
viewModelInstance.PropertyChanged += PropertyChangedHandler;
}
/// <summary>
/// handler that, everytime a monitored property changes, calls the RaiseCanExecuteChanged of the command
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PropertyChangedHandler(object sender, PropertyChangedEventArgs e) {
if (_PropertiesToWatch.Contains(e.PropertyName)) {
this.OnCanExecuteChanged();
}
}
/// <summary>
/// giving an expression that identify a propriety or a list of properties, return the property names obtained from the expression
/// Examples on selector usage
/// proprietà singola:
/// entity => entity.PropertyName
/// proprietà multiple
/// entity => new { entity.PropertyName1, entity.PropertyName2 }
/// </summary>
/// <param name="selector"></param>
/// <returns></returns>
protected List<string> RegisterPropertiesWatcher(Expression<Func<TViewModel, object>> selector) {
List<string> properties = new List<string>();
System.Linq.Expressions.LambdaExpression lambda = (System.Linq.Expressions.LambdaExpression)selector;
if (lambda.Body is System.Linq.Expressions.MemberExpression) {
System.Linq.Expressions.MemberExpression memberExpression = (System.Linq.Expressions.MemberExpression)(lambda.Body);
properties.Add(memberExpression.Member.Name);
}
else if (lambda.Body is System.Linq.Expressions.UnaryExpression) {
System.Linq.Expressions.UnaryExpression unaryExpression = (System.Linq.Expressions.UnaryExpression)(lambda.Body);
properties.Add(((System.Linq.Expressions.MemberExpression)(unaryExpression.Operand)).Member.Name);
}
else if (lambda.Body.NodeType == ExpressionType.New) {
NewExpression newExp = (NewExpression)lambda.Body;
foreach (var argument in newExp.Arguments) {
if (argument is System.Linq.Expressions.MemberExpression) {
System.Linq.Expressions.MemberExpression mExp = (System.Linq.Expressions.MemberExpression)argument;
properties.Add(mExp.Member.Name);
}
else {
throw new SyntaxErrorException("Syntax Error, selector has to be an expression that returns a new object containing a list of properties, e.g.: s => new { s.Property1, s.Property2 }");
}
}
}
else {
throw new SyntaxErrorException("Syntax Error, selector has to be an expression that returns a new object containing a list of properties, e.g.: s => new { s.Property1, s.Property2 }");
}
return properties;
}
}
note that my solution implies that this command has to be wired with the viewmodel that handle it and the viewmodel has to implement the INotifyPropertyChanged interface.
first two constructor are there so the command is backward compatible with DelegateCommand but the 3rd is the important one, that accepts a linq expression to specify which property to monitor
the usage is pretty simple and easy to understand, let me write it here with methods but of course you can create your handler methods. Suppose you have have a ViewModel called MyViewModel with two properties (PropertyX and PropertyY) that rise the propertychanged event, and somewhere in it you create an instance of SaveCommand, it would look like this:
this.SaveCommand = new MyDelegateCommand<MyViewModel>(this,
//execute
() => {
Console.Write("EXECUTED");
},
//can execute
() => {
Console.Write("Checking Validity");
return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5;
},
//properties to watch
(p) => new { p.PropertyX, p.PropertyY }
);
maybe i'll create an article somewhere about this, but this snippet should be clear i hope
Fabio's answer works well. Here's a parameterized version for DelegateCommand<T>. (I've tightened up the code a little, too.)
public class DepedencyCommand<TViewModel, TArg> : DelegateCommand<TArg>
where TViewModel : INotifyPropertyChanged
{
private readonly List<string> _propertiesToWatch;
public DepedencyCommand(Action<TArg> executedMethod)
: base(executedMethod) { }
public DepedencyCommand(Action<TArg> executedMethod, Func<TArg, bool> canExecuteMethod)
: base(executedMethod, canExecuteMethod) { }
public DepedencyCommand(TViewModel viewModelInstance, Action<TArg> executedMethod, Func<TArg, bool> canExecuteMethod, Expression<Func<TViewModel, object>> propertiesToWatch)
: base(executedMethod, canExecuteMethod)
{
_propertiesToWatch = _RegisterPropertiesWatcher(propertiesToWatch);
viewModelInstance.PropertyChanged += PropertyChangedHandler;
}
/// <summary>
/// handler that, everytime a monitored property changes, calls the RaiseCanExecuteChanged of the command
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
if (_propertiesToWatch.Contains(e.PropertyName))
{
this.OnCanExecuteChanged();
}
}
/// <summary>
/// giving an expression that identify a propriety or a list of properties, return the property names obtained from the expression
/// Examples on selector usage
/// proprietà singola:
/// entity => entity.PropertyName
/// proprietà multiple
/// entity => new { entity.PropertyName1, entity.PropertyName2 }
/// </summary>
/// <param name="selector"></param>
/// <returns></returns>
private static List<string> _RegisterPropertiesWatcher(Expression<Func<TViewModel, object>> selector)
{
var properties = new List<string>();
LambdaExpression lambda = selector;
if (lambda.Body is MemberExpression)
{
var memberExpression = (MemberExpression)lambda.Body;
properties.Add(memberExpression.Member.Name);
}
else if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
properties.Add(((MemberExpression)unaryExpression.Operand).Member.Name);
}
else if (lambda.Body.NodeType == ExpressionType.New)
{
var newExp = (NewExpression)lambda.Body;
foreach (var argument in newExp.Arguments)
{
if (argument is MemberExpression)
{
MemberExpression mExp = (MemberExpression)argument;
properties.Add(mExp.Member.Name);
}
else
throw new SyntaxErrorException("Syntax Error, selector has to be an expression that returns a new object containing a list of properties, e.g.: s => new { s.Property1, s.Property2 }");
}
}
else
throw new SyntaxErrorException("Syntax Error, selector has to be an expression that returns a new object containing a list of properties, e.g.: s => new { s.Property1, s.Property2 }");
return properties;
}
}
There doesn't seem to be a better solution. I know what you mean, it seems inelegant but whatever lipstick you put on it, the onus is on the objects involved in the expression to notify the command.
If your condition is based purely on other notify properties you could add your own handler to PropertyChanged, that provides a bit of abstraction.
In this case, TimerState would be a VM property. Then you can a handler to your ViewModel.PropertyChanged event. You can then inspect the property name and update your CanExecute. This is still ugly, but at least you have all the garbage in one block.
I am facing a few difficulties with EF 4, foreign keys and INotifyPropertyChanged / the partial methods exposed for scalar properties.
I hope you can help me find the right way to do this.
Image I have a Customer entity with *..1 relationship with the Country entity.
Now, I'd obviously like to be able to do:
var customer = new Customer();
customer.Country = [...]
...but I don't necessarily need the CountryKey property.
I create a Association in EF with the correct cardinality in the .edmx designer. I choose not to "add foreign key properties" in the dialog.
This leaves me with a generated class without the partial OnCountryChanging and OnCountryChanged.
Next, I try to add the foreign key properties, and I now have a OnCountryKeyChanging and OnCountryKeyChanged.
However, the generated code looks like this:
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int64 CountryKey
{
get
{
return _CountryKey;
}
set
{
OnCountryKeyChanging(value);
ReportPropertyChanging("CountryKey");
_CountryKey = StructuralObject.SetValidValue(value);
ReportPropertyChanged("CountryKey");
OnCountryKeyChanged();
}
}
private global::System.Int64 _CountryKey;
partial void OnCountryKeyChanging(global::System.Int64 value);
partial void OnCountryKeyChanged();
As you can see from the generated code, the PropertyChanged notification occurs with "CountryKey" instead of "Country". This makes data binding in WPF difficult.
My question is: how do I get around this?
Do I wrap my object in a ViewModel, listen to property changes and strip the "Key" part?
Do I modify the T4 template?
Or is there a third option I just can't see yet?
I'd greatly appreciate any suggestions here, as I am experimenting with WPF / EF without wrapping each Model property in a ViewModel.
The 'best practices' approach is to decorate your model in a viewmodel, exposing the model properties as required. You can create a generic ViewModel with some nifty work with dynamicobject, using perhaps a property mapping etc.
public class DynamicViewModel : DynamicObject, INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
private static readonly Dictionary<Type, Tuple<Dictionary<string, PropertyInfo>, Dictionary<string, string>>> typeDictionary = new Dictionary<Type, Tuple<Dictionary<string, PropertyInfo>, Dictionary<string, string>>>();
private readonly Dictionary<string, object> additionalProperties = new Dictionary<string, object>();
private readonly object underlyingObject;
public object UnderlyingObject
{
get
{
return underlyingObject;
}
}
private readonly Type type;
/// <summary>
/// constructor which takes a model for which it will be extensing its perceived properties
/// </summary>
/// <param name="underlyingObject">the underlying object</param>
public DynamicViewModel(IBindableRfqViewModel underlyingObject) : this(underlyingObject, new Dictionary<string, string>())
{
}
/// <summary>
/// constructor which takes a model for which it will be extensing its perceived properties as well as a property map
/// </summary>
/// <param name="underlyingObject">the underlying object</param>
/// <param name="propertyMap">a string/string dictionary, where the key is a property on the underlying object, and the value is the name of the dynamic property to be used as a binding target</param>
public DynamicViewModel(IBindableRfqViewModel underlyingObject, Dictionary<string, string> propertyMap)
{
this.underlyingObject = underlyingObject;
if (underlyingObject is INotifyPropertyChanged)
{
((INotifyPropertyChanged)underlyingObject).PropertyChanged += OnUnderlyingPropertyChanged;
}
type = underlyingObject.GetType();
if (typeDictionary.ContainsKey(type))
{
return;
}
lock (typeDictionary)
{
if (typeDictionary.ContainsKey(type))
{
return;
}
var forwardPropertyMap = propertyMap;
var typeProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(p => p.Name, p => p);
typeDictionary.Add(type, Tuple.Create(typeProperties,forwardPropertyMap));
}
}
private void OnUnderlyingPropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.OnPropertyChanged(e.PropertyName);
}
private bool TryGetProperty(string name, out object result)
{
try
{
var propertyData = typeDictionary[type];
var modelProperty = name;
if (propertyData.Item2.ContainsKey(name))
{
modelProperty = propertyData.Item2[name];
}
if (propertyData.Item1.ContainsKey(modelProperty))
{
result = propertyData.Item1[modelProperty].GetValue(underlyingObject, null);
return true;
}
if (additionalProperties.ContainsKey(name))
{
result = additionalProperties[name];
return true;
}
result = null;
return true;
}
catch (Exception ex)
{
result = null;
return false;
}
}
/// <summary>
/// <see cref="DynamicObject.TryGetMember" />
/// </summary>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return this.TryGetProperty(binder.Name, out result);
}
/// <summary>
/// <see cref="DynamicObject.TryGetIndex" />
/// </summary>
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
return this.TryGetProperty(indexes[0].ToString(), out result);
}
private bool TrySetProperty(string name, object value)
{
try
{
var propertyData = typeDictionary[type];
var modelProperty = name;
if (propertyData.Item2.ContainsKey(name))
{
modelProperty = propertyData.Item2[name];
}
if (propertyData.Item1.ContainsKey(modelProperty))
{
propertyData.Item1[modelProperty].SetValue(underlyingObject, value, null);
}
else
{
if (!additionalProperties.ContainsKey(name))
{
additionalProperties.Add(name, new object());
}
additionalProperties[name] = value;
}
this.OnPropertyChanged(name);
return true;
}
catch (Exception ex)
{
return false;
}
}
/// <summary>
/// <see cref="DynamicObject.TrySetMember" />
/// </summary>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
return this.TrySetProperty(binder.Name, value);
}
/// <summary>
/// <see cref="DynamicObject.TrySetIndex" />
/// </summary>
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
return indexes.Length == 0 || this.TrySetProperty(indexes[0].ToString(), value);
}
private void OnPropertyChanged(string propName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
/// <summary>
/// IDisposable implementation
/// </summary>
public void Dispose()
{
if (underlyingObject is INotifyPropertyChanged)
{
((INotifyPropertyChanged)underlyingObject).PropertyChanged -= OnUnderlyingPropertyChanged;
}
if (underlyingObject is IDisposable)
{
((IDisposable)underlyingObject).Dispose();
}
}
}
I have multiple viewModels in my application and am binding/used them in ViewModelLocator mvvm light. I have done button on one of my DailyActivities.xaml page. but when i clicked on it,it gives me error in ViewModelLocator like "Object reference not set to an instance of an object". and control comes to this line :
public static void ClearActivities()
{
_activities.Cleanup(); //Error here
_activities = null;
}
here is the code for DailyActivitiesViewModel in ViewModelLocator:
private static ActivitiesViewModel _activities;
public static ActivitiesViewModel ActivitiesStatic
{
get
{
if (_activities == null)
{
CreateActivities();
}
return _activities;
}
}
/// <summary>
/// Gets the ViewModelPropertyName property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public ActivitiesViewModel Activities
{
get
{
return ActivitiesStatic;
}
}
/// <summary>
/// Provides a deterministic way to delete the ViewModelPropertyName property.
/// </summary>
public static void ClearActivities()
{
_activities.Cleanup(); --Error here
_activities = null;
}
/// <summary>
/// Provides a deterministic way to create the ViewModelPropertyName property.
/// </summary>
public static void CreateActivities()
{
if (_activities == null)
{
_activities = new ActivitiesViewModel();
}
}
Kindly Suggest?
Thanks
You need to determine if the field exists before you can use it: It seems like if the field _activities is already null then no clean-up is required.
why not do something like
public static void ClearActivites()
{
if (null == _activities) return;
_activities.Cleanup();
_activities = null;
}
The "Silverlight Business Application" template bundled with VS2010 / Silverlight 4 uses DataAnnotations on method arguments in its domain service class, which are invoked automagically:
public CreateUserStatus CreateUser(RegistrationData user,
[Required(ErrorMessageResourceName = "ValidationErrorRequiredField", ErrorMessageResourceType = typeof(ValidationErrorResources))]
[RegularExpression("^.*[^a-zA-Z0-9].*$", ErrorMessageResourceName = "ValidationErrorBadPasswordStrength", ErrorMessageResourceType = typeof(ValidationErrorResources))]
[StringLength(50, MinimumLength = 7, ErrorMessageResourceName = "ValidationErrorBadPasswordLength", ErrorMessageResourceType = typeof(ValidationErrorResources))]
string password)
{ /* do something */ }
If I need to implement this in my POCO class methods, how do I get the framework to invoke the validations OR how do I invoke the validation on all the arguments imperatively (using Validator or otherwise?).
We have approached it like this:
We have a ValidationProperty class that takes in a RegularExpression that will be used to validate the value (you could use whatever you wanted).
ValidationProperty.cs
public class ValidationProperty
{
#region Constructors
/// <summary>
/// Constructor for property with validation
/// </summary>
/// <param name="regularExpression"></param>
/// <param name="errorMessage"></param>
public ValidationProperty(string regularExpression, string errorMessage)
{
RegularExpression = regularExpression;
ErrorMessage = errorMessage;
IsValid = true;
}
#endregion
#region Properties
/// <summary>
/// Will be true if this property is currently valid
/// </summary>
public bool IsValid { get; private set; }
/// <summary>
/// The value of the Property.
/// </summary>
public object Value
{
get { return val; }
set
{
if (this.Validate(value))//if valid, set it
{
val = value;
}
else//not valid, throw exception
{
throw new ValidationException(ErrorMessage);
}
}
}
private object val;
/// <summary>
/// Holds the regular expression that will accept a vaild value
/// </summary>
public string RegularExpression { get; private set; }
/// <summary>
/// The error message that will be thrown if invalid
/// </summary>
public string ErrorMessage { get; private set; }
#endregion
#region Private Methods
private bool Validate(object myValue)
{
if (myValue != null)//Value has been set, validate it
{
this.IsValid = Regex.Match(myValue.ToString(), this.RegularExpression).Success;
}
else//still valid if it has not been set. Invalidation of items not set needs to be handled in the layer above this one.
{
this.IsValid = true;
}
return this.IsValid;
}
#endregion
}
Here's how we would create a Validation property. Notice how the public member is a string, but privately I am using a 'ValidationProperty.'
public string TaskNumber
{
get { return taskNumber.Value.ToString(); }
set
{
taskNumber.Value = value;
OnPropertyChanged("TaskNumber");
}
}
private ValidationProperty taskNumber;
Now, whenever the value is set, the business layer will validate that it's a valid value. If it's not, it will simply throw a new ValidationException (in the ValidationProperty class). In your xaml, you will need to set NotifyOnValidationError & ValidatesOnExceptions to true.
<TextBox Text="{Binding TaskNumber, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>
With this approach, you would probably have a form for creating a new 'User' and each field would valitate each time they set it (I hope that makes sense).
This is the approach that we used to get the validation to be on the business layer. I'm not sure if this is exactly what you're looking for, but I hope it helps.
I need to be able to specify a command to run when the SelectionChanged event fires. I already know how to implement the ICommandSource interface; what I need to know is how I can just add a command to the column series to handle the SelectionChanged event.
When I inherit from the ColumnBarBaseSeries<...> base class I have to override GetAxes() and UpdateDatePoint() which I am not sure how to implement.
You can use attached behaviours to solve this problem.
Create a SelectionChangedBehaviour that wires a selectionChanged event to the element that you're attaching the behaviour to then you can binding any ICommand to that behaviour.
For more on attached behaviours -
Introduction article by Josh Smith
Overview of the concept
Another good introduction
Hope that helps
Here is some code to add an attached behavior to a ColumnSeries for a SelectionChanged Command.
public static class ColumnSeriesBehavior
{
private static DelegateCommand<object> SelectionChangedCommand;
public static DelegateCommand<object> GetSelectionChangedCommand(ColumnSeries cs)
{
return cs.GetValue(SelectionChangedCommandProperty) as DelegateCommand<object>;
}
public static void SetSelectionChangedCommand(ColumnSeries cs, DelegateCommand<object> value)
{
cs.SetValue(SelectionChangedCommandProperty, value);
}
// Using a DependencyProperty as the backing store for SelectionChangedCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectionChangedCommandProperty =
DependencyProperty.RegisterAttached("SelectionChangedCommand", typeof(DelegateCommand<object>), typeof(ColumnSeriesBehavior), new UIPropertyMetadata(null, OnSelectionChangedCommandChanged));
private static void OnSelectionChangedCommandChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
ColumnSeries item = depObj as ColumnSeries;
if (item == null)
{
return;
}
if (e.NewValue is DelegateCommand<object> == false)
{
return;
}
SelectionChangedCommand = e.NewValue as DelegateCommand<object>;
item.SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(Column_SelectionChanged);
}
private static void Column_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (SelectionChangedCommand != null)
SelectionChangedCommand.Execute(sender);
}
}
And in the XAML to attach the property:
<chartingToolkit:Chart.Series>
<chartingToolkit:ColumnSeries
IsSelectionEnabled="True"
ItemsSource="{Binding YourItemSource}"
IndependentValueBinding="{Binding YourIndValue, Path=YourIndValuePath}"
DependentValueBinding="{Binding YourDepValue, Path=YourDepValuePath}"
>
<chartingToolkit:ColumnSeries.Style>
<Style>
<!-- Attaching the SelectionChangedCommand behavior -->
<Setter Property="local:ColumnSeriesBehavior.SelectionChangedCommand"
Value="{Binding YourDelegateCommand}"/>
</Style>
</chartingToolkit:ColumnSeries.Style>
</chartingToolkit:ColumnSeries>
</chartingToolkit:Chart.Series>
Here is some code that appears to work for me to implement your own CommandColumnSeries, I stole a lot of it from the source for the ColumnSeries Sealed Class:
public class CommandColumnSeries : ColumnBarBaseSeries<ColumnDataPoint>
{
#region "ICommandSource"
[Localizability(LocalizationCategory.NeverLocalize), Category("Action"), Bindable(true)]
public ICommand Command
{
get
{
return (ICommand)base.GetValue(CommandProperty);
}
set
{
base.SetValue(CommandProperty, value);
}
}
[Bindable(true), Category("Action"), Localizability(LocalizationCategory.NeverLocalize)]
public object CommandParameter
{
get
{
return base.GetValue(CommandParameterProperty);
}
set
{
base.SetValue(CommandParameterProperty, value);
}
}
[Category("Action"), Bindable(true)]
public IInputElement CommandTarget
{
get
{
return (IInputElement)base.GetValue(CommandTargetProperty);
}
set
{
base.SetValue(CommandTargetProperty, value);
}
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(CommandColumnSeries), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandColumnSeries), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(CommandColumnSeries), new FrameworkPropertyMetadata(null));
#endregion
#region public IRangeAxis DependentRangeAxis
/// <summary>
/// Gets or sets the dependent range axis.
/// </summary>
public IRangeAxis DependentRangeAxis
{
get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; }
set { SetValue(DependentRangeAxisProperty, value); }
}
/// <summary>
/// Identifies the DependentRangeAxis dependency property.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because the base classes need to share this dependency property.")]
public static readonly DependencyProperty DependentRangeAxisProperty =
DependencyProperty.Register(
"DependentRangeAxis",
typeof(IRangeAxis),
typeof(ColumnSeries),
new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged));
/// <summary>
/// DependentRangeAxisProperty property changed handler.
/// </summary>
/// <param name="d">ColumnBarBaseSeries that changed its DependentRangeAxis.</param>
/// <param name="e">Event arguments.</param>
private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandColumnSeries source = (CommandColumnSeries)d;
IRangeAxis newValue = (IRangeAxis)e.NewValue;
source.OnDependentRangeAxisPropertyChanged(newValue);
}
/// <summary>
/// DependentRangeAxisProperty property changed handler.
/// </summary>
/// <param name="newValue">New value.</param>
private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue)
{
this.InternalDependentAxis = (IAxis)newValue;
}
#endregion public IRangeAxis DependentRangeAxis
#region public IAxis IndependentAxis
/// <summary>
/// Gets or sets the independent category axis.
/// </summary>
public IAxis IndependentAxis
{
get { return GetValue(IndependentAxisProperty) as IAxis; }
set { SetValue(IndependentAxisProperty, value); }
}
/// <summary>
/// Identifies the IndependentAxis dependency property.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because the base classes need to share this dependency property.")]
public static readonly DependencyProperty IndependentAxisProperty =
DependencyProperty.Register(
"IndependentAxis",
typeof(IAxis),
typeof(ColumnSeries),
new PropertyMetadata(null, OnIndependentAxisPropertyChanged));
/// <summary>
/// IndependentAxisProperty property changed handler.
/// </summary>
/// <param name="d">ColumnBarBaseSeries that changed its IndependentAxis.</param>
/// <param name="e">Event arguments.</param>
private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandColumnSeries source = (CommandColumnSeries)d;
IAxis newValue = (IAxis)e.NewValue;
source.OnIndependentAxisPropertyChanged(newValue);
}
/// <summary>
/// IndependentAxisProperty property changed handler.
/// </summary>
/// <param name="newValue">New value.</param>
private void OnIndependentAxisPropertyChanged(IAxis newValue)
{
this.InternalIndependentAxis = (IAxis)newValue;
}
#endregion public IAxis IndependentAxis
public CommandColumnSeries()
{
this.SelectionChanged += new SelectionChangedEventHandler(CommandColumnSeries_SelectionChanged);
}
private void CommandColumnSeries_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (Command != null)
{
RoutedCommand routedCommand = Command as RoutedCommand;
CommandParameter = e.Source;
if (routedCommand != null)
{
routedCommand.Execute(CommandParameter, CommandTarget);
}
else
{
Command.Execute(CommandParameter);
}
}
}
protected override void GetAxes(DataPoint firstDataPoint)
{
// Taken from the source of the ColumnSeries sealed class.
GetAxes(
firstDataPoint,
(axis) => axis.Orientation == AxisOrientation.X,
() => new CategoryAxis { Orientation = AxisOrientation.X },
(axis) =>
{
IRangeAxis rangeAxis = axis as IRangeAxis;
return rangeAxis != null && rangeAxis.Origin != null && axis.Orientation == AxisOrientation.Y;
},
() =>
{
IRangeAxis rangeAxis = CreateRangeAxisFromData(firstDataPoint.DependentValue);
rangeAxis.Orientation = AxisOrientation.Y;
if (rangeAxis == null || rangeAxis.Origin == null)
{
throw new InvalidOperationException("No Suitable Axes found for plotting range axis.");
}
DisplayAxis axis = rangeAxis as DisplayAxis;
if (axis != null)
{
axis.ShowGridLines = true;
}
return rangeAxis;
});
}
protected override void UpdateDataPoint(DataPoint dataPoint)
{
// This code taken from the ColumnSeries sealed class.
if (SeriesHost == null )//|| PlotArea == null)
{
return;
}
object category = dataPoint.ActualIndependentValue ?? (IndexOf<DataPoint>(this.ActiveDataPoints, dataPoint) + 1);
Range<UnitValue> coordinateRange = GetCategoryRange(category);
if (!coordinateRange.HasData)
{
return;
}
else if (coordinateRange.Maximum.Unit != Unit.Pixels || coordinateRange.Minimum.Unit != Unit.Pixels)
{
throw new InvalidOperationException("This Series Does Not Support Radial Axes");
}
double minimum = (double)coordinateRange.Minimum.Value;
double maximum = (double)coordinateRange.Maximum.Value;
double plotAreaHeight = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value.Value;
IEnumerable<CommandColumnSeries> columnSeries = SeriesHost.Series.OfType<CommandColumnSeries>().Where(series => series.ActualIndependentAxis == ActualIndependentAxis);
int numberOfSeries = columnSeries.Count();
double coordinateRangeWidth = (maximum - minimum);
double segmentWidth = coordinateRangeWidth * 0.8;
double columnWidth = segmentWidth / numberOfSeries;
int seriesIndex = IndexOf<CommandColumnSeries>(columnSeries, this);
double dataPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ToDouble(dataPoint.ActualDependentValue)).Value.Value;
double zeroPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin).Value.Value;
double offset = seriesIndex * Math.Round(columnWidth) + coordinateRangeWidth * 0.1;
double dataPointX = minimum + offset;
if (GetIsDataPointGrouped(category))
{
// Multiple DataPoints share this category; offset and overlap them appropriately
IGrouping<object, DataPoint> categoryGrouping = GetDataPointGroup(category);
int index = GroupIndexOf(categoryGrouping, dataPoint);
dataPointX += (index * (columnWidth * 0.2)) / (categoryGrouping.Count() - 1);
columnWidth *= 0.8;
Canvas.SetZIndex(dataPoint, -index);
}
if (CanGraph(dataPointY) && CanGraph(dataPointX) && CanGraph(zeroPointY))
{
double left = Math.Round(dataPointX);
double width = Math.Round(columnWidth);
double top = Math.Round(plotAreaHeight - Math.Max(dataPointY, zeroPointY) + 0.5);
double bottom = Math.Round(plotAreaHeight - Math.Min(dataPointY, zeroPointY) + 0.5);
double height = bottom - top + 1;
Canvas.SetLeft(dataPoint, left);
Canvas.SetTop(dataPoint, top);
dataPoint.Width = width;
dataPoint.Height = height;
}
}
private static int IndexOf<T>(IEnumerable<T> collection, T target)
{
int i = 0;
foreach (var obj in collection)
{
if (obj.Equals(target))
return i;
i++;
}
return -1;
}
private static int GroupIndexOf(IGrouping<object, DataPoint> group, DataPoint point)
{
int i = 0;
foreach (var pt in group)
{
if (pt == point)
return i;
i++;
}
return -1;
}
/// <summary>
/// Returns a value indicating whether this value can be graphed on a
/// linear axis.
/// </summary>
/// <param name="value">The value to evaluate.</param>
/// <returns>A value indicating whether this value can be graphed on a
/// linear axis.</returns>
private static bool CanGraph(double value)
{
return !double.IsNaN(value) && !double.IsNegativeInfinity(value) && !double.IsPositiveInfinity(value) && !double.IsInfinity(value);
}
/// <summary>
/// Converts an object into a double.
/// </summary>
/// <param name="value">The value to convert to a double.</param>
/// <returns>The converted double value.</returns>
private static double ToDouble(object value)
{
return Convert.ToDouble(value, CultureInfo.InvariantCulture);
}
}