I have two datepickers, and these datepickers accepts 01/01/0001 (They fetch data From, and To dates), the problem is, I have the following error:"SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM."
Is there any way to attach a behavior that when, if the user writes a date below 01/01/1753 it overwrites it as 01/01/1753?
You could add a CalendarDateRange to the BlackoutDates property that disables any date before 1753-01-01:
<DatePicker>
<DatePicker.BlackoutDates>
<CalendarDateRange Start="0001-01-01" End="1752-12-31" />
</DatePicker.BlackoutDates>
</DatePicker>
You can wrap this in an attached behaviour if you want to:
public class Behavior
{
public static readonly DependencyProperty DisableDateRangeProperty = DependencyProperty.RegisterAttached(
"DisableDateRange", typeof(bool), typeof(Behavior), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnChanged)));
public static void SetDisableDateRangeProperty(DatePicker element, bool value) =>
element.SetValue(DisableDateRangeProperty, value);
public static bool GetDisableDateRangeProperty(DatePicker element) =>
(bool)element.GetValue(DisableDateRangeProperty);
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DatePicker dp = (DatePicker)d;
dp.BlackoutDates.Add(new CalendarDateRange(new DateTime(0001, 1, 1), new DateTime(1752, 12, 31)));
}
}
Usage:
<DatePicker local:Behavior.DisableDateRangeProperty="True" />
Related
I have the following custom control:
<abc:MyControl MyProperty="{Binding FieldInMyModel, Mode=TwoWay}">
And in my custom control I have
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(double), typeof(MyControl),
new PropertyMetadata(0.0, OnMyPropertyChanged));
public double MyProperty
{
get { return (double)GetValue(MyPropertyProperty ); }
set { SetValue(MyPropertyProperty , value); }
}
private static void OnMyPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (d is MyControl mc)
{
// here I check if the new value is valid for my scope.
// if it is not I update it here to be valid
var v = (double)e.NewValue;
if (!mc.IsValidValue(v))
{
v = mc.MakeValidValue(v);
mc.MyProperty = v;
}
}
}
In my model I change the value of FieldInMyModel to be not valid for my scope. OnMyPropertyChanged is called, and after making a valid value from received invalid value I expect that the FieldInMyModel will be updated to have the new value of MyProperty but actually nothing happens. Any thought?
Setting MyProperty from code will break your TwoWay Binding.
Instead you can use SetCurrentValue method.
When I set the value of IsClosed during runtime, OnIsClosedChanged() is called fine.
However, the Designer sets the value of the property but does not call the OnIsClosedChanged().
public static DependencyProperty IsClosedProperty = DependencyProperty.Register("IsClosed", typeof(bool), typeof(GroupBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
public bool IsClosed {
get {
return (bool)this.GetValue(IsClosedProperty);
}
set {
if ((bool)this.GetValue(IsClosedProperty) == value)
return;
this.SetValue(IsClosedProperty, value);
OnIsClosedChanged();
}
}
private void OnIsClosedChanged() {
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
Obviously IsClosed is not modified by the Designer and only IsClosedProperty receives the xaml change.
My question is: How can I run IsClosed after the value has been modified in the Designer. Or at least add some logic to the non-runtime changes.
You would have to register a PropertyChangedCallback with property metadata.
The reason is that dependency properties set in XAML or by bindings or some other source do not invoke the CLR wrapper (the setter method). The reason is explained in the XAML Loading and Dependency Properties article on MSDN:
For implementation reasons, it is computationally less expensive to
identify a property as a dependency property and access the property
system SetValue method to set it, rather than using the property
wrapper and its setter.
...
Because the current WPF implementation of the XAML processor behavior
for property setting bypasses the wrappers entirely, you should not
put any additional logic into the set definitions of the wrapper for
your custom dependency property. If you put such logic in the set
definition, then the logic will not be executed when the property is
set in XAML rather than in code.
Your code should look like this:
public static readonly DependencyProperty IsClosedProperty =
DependencyProperty.Register(
"IsClosed", typeof(bool), typeof(GroupBox),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender,
(o, e) => ((GroupBox)o).OnIsClosedChanged()));
public bool IsClosed
{
get { return (bool)GetValue(IsClosedProperty); }
set { SetValue(IsClosedProperty, value); }
}
private void OnIsClosedChanged()
{
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
Found the answer myself now. ValidateValueCallback comes really close! (as Alex K has pointed out) But it is a static method and I don't get any reference to the instance which has been changed. The key is to use a PropertyChangedCallback in FrameworkPropertyMetadata which is also an argument passed to the Property.Register method.
See:
public static DependencyProperty IsClosedProperty = DependencyProperty.Register("IsClosed", typeof(bool), typeof(GroupBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnIsClosedChangedPCC)));
public bool IsClosed {
get {
return (bool)this.GetValue(IsClosedProperty);
}
set {
this.SetValue(IsClosedProperty, value);
OnIsClosedChanged();
}
}
private static void OnIsClosedChangedPCC(DependencyObject d, DependencyPropertyChangedEventArgs e) {
GroupBox current = (GroupBox)d;
current.IsClosed = current.IsClosed;
}
private void OnIsClosedChanged() {
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
That does now re-set the IsClosedValue which triggers the OnIsClosedChanged to run.
Thank's for your help guys!
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 created a DependencyProperty for UserControl that should be in the range of -2 .. 2
When rotating the mouse scroll wheel in the properties window.
The property value changes by one. And I want to value changed by 0.1
How do I set a step change in DependencyProperty?
I work with properties in XAML editor.
public double Value
{
get { return (double)GetValue(BarValueProperty); }
set { SetValue(BarValueProperty, value); }
}
public static readonly DependencyProperty BarValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(MeterBar), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
When adding the FrameworkPropertyMetadata options into the definition of a DependencyProperty, there is an option to provide a CoerceValueCallback handler. You can change the incomming values in this handler. For complete details, please see the Dependency Property Callbacks and Validation page on MSDN. From the linked page:
public static readonly DependencyProperty CurrentReadingProperty =
DependencyProperty.Register(
"CurrentReading",
typeof(double),
typeof(Gauge),
new FrameworkPropertyMetadata(
Double.NaN,
FrameworkPropertyMetadataOptions.AffectsMeasure,
new PropertyChangedCallback(OnCurrentReadingChanged),
new CoerceValueCallback(CoerceCurrentReading)
),
new ValidateValueCallback(IsValidReading)
);
public double CurrentReading
{
get { return (double)GetValue(CurrentReadingProperty); }
set { SetValue(CurrentReadingProperty, value); }
}
...
private static object CoerceCurrentReading(DependencyObject d, object value)
{
// Do whatever calculation to update your value you need to here
Gauge g = (Gauge)d;
double current = (double)value;
if (current
<g.MinReading) current = g.MinReading;
if (current >g.MaxReading) current = g.MaxReading;
return current;
}
I have two Dependency Property, Value and MinVal.
I want the default value of "Value" will depend on "MinVal".
The "MinVal" set by xaml only one time.
How can I do that?
Here is the code:
public int Value
{
get { return (int)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(int), typeof(NumericHexUpDown), new UIPropertyMetadata(0, ValueChanged));
private static void ValueChanged(object sender, DependencyPropertyChangedEventArgs e)
{
}
public int MinVal
{
get { return (int)GetValue(MinValProperty); }
set { SetValue(MinValProperty, value); }
}
// Using a DependencyProperty as the backing store for MinVal. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MinValProperty =
DependencyProperty.Register("MinVal", typeof(int), typeof(NumericHexUpDown), new UIPropertyMetadata(0, MinValueChanged));
private static void MinValueChanged(object sender, DependencyPropertyChangedEventArgs e)
{
}
Basically you add coerce methods to your dependency properties. Your default values are in the metadata. But you want the values to react to each other before they are displayed after the XAML is loaded, and coercion does just that.
private static void OnMinValChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((NumericHexUpDown)d).CoerceValue(ValueProperty);
}
private static void OnValueChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((NumericHexUpDown)d).CoerceValue(MinValProperty);
}
private static object CoerceMinVal(DependencyObject d,
object value)
{
double min = ((NumericHexUpDown)d).MinVal;
return value;
}
private static object CoerceValue(DependencyObject d,
object value)
{
double min = ((NumericHexUpDown)d).MinVal;
double val = (double)value;
if (val < min) return min;
return value;
}
The metadata constructor looks like this
public static readonly DependencyProperty MinValProperty = DependencyProperty.Register(
"MinVal", typeof(int), typeof(NumericHexUpDown),
new FrameworkPropertyMetadata(
0,
new PropertyChangedCallback(OnMinimumChanged),
new CoerceValueCallback(CoerceMinimum)
),
);
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(int), typeof(NumericHexUpDown),
new FrameworkPropertyMetadata(
0,
new PropertyChangedCallback(OnValueChanged),
new CoerceValueCallback(CoerceValue)
),
);
References
http://drwpf.com/blog/2010/05/05/value-coercion-for-the-masses/
http://msdn.microsoft.com/en-us/library/ms745795.aspx