I use custom validation rule to validate my data. But I can't access/determine the property value.
here is my code
public class MandatoryRule: ValidationRule
{
public MandatoryRule()
{
ValidationStep = System.Windows.Controls.ValidationStep.UpdatedValue;
}
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
BindingExpression exp = value as BindingExpression;
if (value == null)
return new ValidationResult(true, null);
return new ValidationResult(true, null);
}
}
I need to set the ValidationStep to UpdatedValue (for further business logic)
Then comes the problem: I don't know what's the property value? Because:
It is a generic validator, can't bound to a specific model
The value in parameter of Validate method is a BindingExpression
So how can I read the real value?
Thanks
At last, I come up with this idea.
Create a class DummyObject : DependencyObject.
Create a public static DependencyProperty DummyProperty.
Then create a new databinding, copy the source, binding path, element name, converter, etc from the (value as BindingExpression).ParentBinding.
Set the new databinding target to the dummyobject.
Then use the binding to UpdateTarget()
And now you can access the value from the dummyproperty.
Had the same issue and came accross this question, Gary's answer seems to be the way to go, but it lacked the source code. So here's my interpretation.
public class BindingExpressionEvaluator : DependencyObject
{
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("ValueProperty", typeof(object),
typeof(BindingExpressionEvaluator), new UIPropertyMetadata(null));
public static object Evaluate(BindingExpression expression)
{
var evaluator = new BindingExpressionEvaluator();
var binding = new Binding(expression.ParentBinding.Path.Path);
binding.Source = expression.DataItem;
BindingOperations.SetBinding(evaluator, BindingExpressionEvaluator.ValueProperty, binding);
var value = evaluator.Value;
BindingOperations.ClearBinding(evaluator, BindingExpressionEvaluator.ValueProperty);
return value;
}
}
Related
I'd like to get value from Binding in my code and use it as a string. How Can I do that?
Binding b = new Binding("MyProperty")
{
Source = myobject
};
//[...]
string value = b //HOW TO GET VALUE FROM b ?
BTW:
I would like the Converter attached to Binding to be called when retrieving this value.
I found that the solution could be an auxiliary class with DependencyProperty.
Auxiliary class
public class TestClass : FrameworkElement
{
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(string), typeof(TestClass), new PropertyMetadata(""));
}
Binding conversion to String
Binding b = new Binding("MyProperty") { Source = myobject };
TestClass tc = new TestClass { DataContext = b };
BindingOperations.SetBinding(tc, TestClass.MyPropertyProperty, b);
string txt = tc.MyProperty;
Advantages:
You can use Binding and MultiBinding
You can use Converters
Disadvantages:
Each time we create a class that inherits from the FrameworkElement, which means that we do unnecessary operations.
I have the following code in my sample.
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(string), typeof(MainWindow), new PropertyMetadata("Hello"));
private TestClass1 test;
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Binding binding = new Binding
{
Path = new PropertyPath("MyProperty"),
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
test.SetValueBinding(binding);
test.DataContext = this;
Console.WriteLine(test.Value);
}
public class TestClass1 : FrameworkElement
{
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(TestClass1), new PropertyMetadata(null));
public void SetValueBinding(Binding binding)
{
this.SetBinding(ValueProperty, binding);
}
}
After binding is set, if i access test.Value, it returns null for the first time. After that,if i access it (from another method) it returns correct value "Hello".
But i do not know first time why binding is not working and null value is returned? any suggestion?
Thanks in advance.
It's not that you can't get the value for the first time. It's that you can't get it so fast. When binding is set at the time the DataContext is NULL, the source is resolved in a deferred way, perhaps later by the UI thread. You ask for value just after the binding is set, too early to get valid results.
You should switch lines in your code to:
test.DataContext = this;
test.SetValueBinding(binding);
This way the binding gets the value immediately from DataContext in the first instruction.
I'm probably searching for this the wrong way, but:
is there any way to get the resulting value of a binding through code?
Probably something glaring obvious, but I just can't find it.
You just need to call the ProvideValue method of the binding. The hard part is that you need to pass a valid IServiceProvider to the method... EDIT: actually, that's not true... ProvideValue returns a BindingExpression, not the value of the bound property.
You can use the following trick:
class DummyDO : DependencyObject
{
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(DummyDO), new UIPropertyMetadata(null));
}
public object EvalBinding(Binding b)
{
DummyDO d = new DummyDO();
BindingOperations.SetBinding(d, DummyDO.ValueProperty, b);
return d.Value;
}
...
Binding b = new Binding("Foo.Bar.Baz") { Source = dataContext };
object value = EvalBinding(b);
Not very elegant, but it works...
I'm currently struggling with one of the bindings I'm trying to add to my WPF project.
In the app I have a model with a bool property that cannot be used for databinding. Behind that property is a .NET remoting object that does some validation and writes the new value into the DB.
The requirement ist that the property should be displayed as checkbox, and as the user changes the value the new value should be immediatly provided to the .NET remoting object.
My approach so far:
I've created in my ViewModel with a DependencyProperty that is bound to my checkbox.
In the propertychanged handler of the DP, I'm writting the value to the property of the remoting object.
The problems I have with this approach:
if the validation within the .net remoting object raises an exception, this exception is swallowed. In addition the checkbox state and what's in the DB is not in sync. I tried to reset the value of the DP in case of an exception, but the checkbox doesn't reflect that.
What makes the situation even worse is the fact, that this WPF controls is integrated into an existing WinForms app.
So I would like to have the same behavior for these exceptions as I have implemented in my Application.ThreadException handler.
any ideas how to approach this?
The problem is that I heard only solutions for .NET 4.0 so far, but I'm working with 3.5SP1.
tia
Martin
Short demo code:
class TestVM : DependencyObject
{
private Model _m;
public TestVM()
{
_m = new Model();
}
public bool Value
{
get { return (bool)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof(bool),
typeof(TestVM),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
((sender, e) => ((TestVM)sender).Apply(e))));
private bool _suppress = false;
private void Apply(DependencyPropertyChangedEventArgs e)
{
if (_suppress) return;
try
{
_m.Value = this.Value;
}
catch
{
_suppress = true;
this.Value = _m.Value;
this.OnPropertyChanged(e);
}
finally
{
_suppress = false;
}
}
}
You don't need to use a DependencyObject as your ViewModel. You just need to implement INotifyPropertyChanged to get data binding support:
class TestVM
: INotifyPropertyChanged
{
private Model _m;
public TestVM()
{
_m = new Model();
}
public bool Value
{
get { return _m.Value; }
set
{
_m.Value = this.Value;
OnPropertyChanged("Value");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Note that if you expect the setter to throw exceptions, you may want to use an ExceptionValidationRule on the binding in your view.
Update: It sounds like your problem is that the Binding won't respond to PropertyChanged events within the call to set the source. One way to get around this is to use an asynchronous binding by setting IsAsync=True in the XAML for your binding. WPF will process the PropertyChanged event after it has finished updating the source value and won't think it is a reentrant call.
You can also get around this by using a Converter and turning off updates on PropertyChanged by doing UpdateSourceTrigger=LostFocus, but I don't think you would want that behavior.
I found a solution for my problem. I'm now deriving my own binding class that does the job.
public class ExceptionBinding : Binding
{
public ExceptionBinding(string name)
: base(name)
{
Construct();
}
public ExceptionBinding()
: base()
{
Construct();
}
private void Construct()
{
this.ValidatesOnExceptions = true;
this.UpdateSourceExceptionFilter = new UpdateSourceExceptionFilterCallback(OnException);
}
private object OnException(object bindExpression, Exception exception)
{
// ... custom error display ...
var exp = (BindingExpressionBase)bindExpression;
exp.UpdateTarget();
return null; // null needed to avoid display of the default error template
}
}
I have a problem with the following scenario (code cut for brevity). Basically the Setter of my User Control Property isn't being called when the dependency property is set and I need to get around this.
I have the following code in my View.xaml
<Filter:Filter x:Name="ProductFilter" PrimaryItemSource="{Binding CarrierProducts}" />
In the View.xaml.cs
public ProductPricing()
{
InitializeComponent();
ViewModel.Filter.ProductPricing vm = new ViewModel.Filter.ProductPricing();
this.DataContext = vm;
}
In my ViewModel I expose a property
public ObservableCollection<Model.FilterItem> _carrierProducts;
public ObservableCollection<Model.FilterItem> CarrierProducts
{
get
{
return _carrierProducts;
}
set
{
if (_carrierProducts != value)
{
_carrierProducts = value;
RaisePropertyChanged("CarrierProducts");
}
}
}
Finally the Filter User control is defined like so.
public static readonly DependencyProperty PrimaryItemSourceProperty =
DependencyProperty.Register("PrimaryItemSource", typeof(ObservableCollection<Model.FilterItem>), typeof(Filter), new PropertyMetadata(null));
public ObservableCollection<Model.FilterItem> PrimaryItemSource
{
get
{
return (ObservableCollection<Model.FilterItem>)GetValue(PrimaryItemSourceProperty);
}
set
{
SetValue(PrimaryItemSourceProperty, value);
ComboBox combo = _filters.ElementAt(0);
FilterSourceChange(combo, value);
}
}
For some reason the PrimaryItemSource property is set but the Setter doesn't get called. Do I have to add a PropertyChange event to the PropertyMetadata object to handle this as that seems like a lot of code for something simple.
This is how a Dependency property that requires additional code to be run on set should be written:-
public ObservableCollection<Model.FilterItem> PrimaryItemSource
{
get { return (ObservableCollection<Model.FilterItem>)GetValue(PrimaryItemSourceProperty); }
set { SetValue(PrimaryItemSourceProperty , value); }
}
public static readonly DependencyProperty PrimaryItemSourceProperty =
DependencyProperty.Register(
"PrimaryItemSource",
typeof(ObservableCollection<Model.FilterItem>),
typeof(Filter), new PropertyMetadata(null, OnPrimaryItemSourceChanged));
private static void OnPrimaryItemSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Filter filter = (Filter)d;
var oldValue = (ObservableCollection<Model.FilterItem>)e.OldValue;
var newValue = (ObservableCollection<Model.FilterItem>)e.NewValue;
filter.OnPrimaryItemSourceChanged(oldValue, newValue);
}
protected virtual void OnPrimaryItemSourceChanged(
ObservableCollection<Model.FilterItem> oldValue,
ObservableCollection<Model.FilterItem> newValue)
{
ComboBox combo = _filters.ElementAt(0);
FilterSourceChange(combo, newValue);
}
You use place a static DependencyPropertyChanged handler in the class that will cast down the dependency object to the correct type and then call an instance method to alert that instance of the change.
This change handler will get called whenever the underlying dependency property is changed be that via the SetValue call in the property Set method or by binding or any other means.
Yes, always use the callback if you need additional logic for the setter. This is a must in Silverlight and WPF.
As far as I know, the Setter would only be called when actually used from code. When you do Binding, things happen using the DependencyProperty framework.
You should also wrap your ComboBox combo = ... code into a this.Dispatcher.BeginInvoke(() => ... );, because that ensures the visual tree is initialized.
The Last parameter of the DependencyProperty.Register() method takes a PropertyMetaData where you're passing null. One of the overloads of the constructor takes a PropertyChangedCallback. Use this overload to define a callback function that will be called when your property is modified.
static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Filter filter = d as Filter;
ComboBox combo = filter._filters.ElementAt(0);
filter.FilterSourceChange(combo, filter.PrimaryItemSource);
}