I have a dependency property on a control which is a custom class.
Now is there an event that gets raised BEFORE the value is being changed?
I know that OnPropertyChanged is raised after the property has already changed.
I need some event before so that I can cancel the changing....in order to preserve the state of the control.
I cannot set back the dependency property back to its old value as that will mean that I lose state in the control.
Thanks!
If its your DependencyProperty, you can use the ValidateValueCallback to validate the incoming value and reject it, if its not as you desire.
In the following example, only values greater than 0 will be accepted:
public int Test {
get { return (int)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test", typeof(int), typeof(YourClass),
new UIPropertyMetadata(0), delegate(object v) {
return ((int)v) > 0; // Here you can check the value set to the dp
});
If your data objects implement INotifyPropertyChanging, then you can handle the PropertyChanging event which is raised before the property value changes.
INotifyPropertyChanging was introduced in .NET 3.5
You may check value of property in property declaration set section. Suppose we have CustomColor dep property:
public Color CustomColor
{
get { return GetValue(CustomColorProperty) as Color;}
set
{
//check value before setting
SetValue(CustomColorProperty, value);
}
}
Also there will be helpfull for you PropertyChangedCallback, ValidateValueCallback, CoerceValueCallback delegates.
Related
My control has a property that maps to a private variable. When the property is set, I also need to store a certain other variable. When the private variable of the property is set by my own control code, this special handling must not occur. All good.
I now need to refactor that to a DependencyProperty. I only have a change handler here, and all (also internal) accesses to that property must go through the DependencyProperty framework. There is no more private variable that I could set directly. Every change done by my own code now looks exactly the same as changes from external sources like the user of the control or DataBinding.
In my property change handler, how can I determine whether a value change came from my code or somewhere else?
This is the old code:
private DateTime selectedTime;
private int intendedDay;
public DateTime SelectedTime
{
get { return selectedTime; }
set
{
selectedTime = value;
intendedDay = selectedTime.Day;
}
}
In this code, I can set selectedTime directly, not affecting intendedDay which must only be set when SelectedTime is assigned a new value altogether from the outside, or when I see fit in my control.
The DependencyProperty only allows me to detect any changes:
public static DependencyProperty SelectedTimeProperty = DependencyProperty.Register(
"SelectedTime",
typeof(DateTime),
typeof(DateTimeTextBox),
new PropertyMetadata(SelectedTimeChanged));
public DateTime SelectedTime
{
get { return (DateTime) GetValue(SelectedTimeProperty); }
set { SetValue(SelectedTimeProperty, value); }
}
private static void SelectedTimeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
DateTimeTextBox control = (DateTimeTextBox) obj;
// This must not happen where I previously assigned the private variable:
control.intendedDay = control.SelectedTime.Day;
}
private int intendedDay;
So when I would previously assign the private variable, I can now only set the dependency property, which will also change intendedDay.
Why not wrap your dependency property in a normal property? Setting the property will still set the dependency property and setting the private one will not set the intendedDay value.
private DateTime selectedTime
{
get { return (DateTime) GetValue(SelectedTimeProperty); }
set { SetValue(SelectedTimeProperty, value); }
}
public DataTime SelectedTime
{
get { return selectedTime; }
set
{
selectedTime = value;
intendedDay = selectedTime.Day;
}
}
Like Clemens explained in his comment, I added a private variable that indicates whether I am currently updating that property from my own code. I set it to true whenever I want to suppress external value change handling, and false afterwards. So I can distinguish whether it was an internal or external value change.
Here is my problem. I recently created a custom control, which works pretty well.
But i have a problem when i use it, i have a little problem :
In my control, i made a property named Value, defined like this :
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(1000));
public int Value
{
get
{
return (int)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
this.ValueText.Text = value.ToString();
}
}
When I do a databinding to this value, the binding works, but the default value is set to 1000, so it first print 1000. But actually, the property bound to Value isn't equal to 1000.
I would like to print in ValueText.Text the value of the bound property when the Value property is created.
Edit : Question is simple, how can I remove that default value and directly print the bound property ?
You should be able to setup a PropertyChanged event in your DependancyProperties metadata to update ValueText when Value changes.
somthing like this:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown),
new PropertyMetadata(1000, (sender, e) => (sender as NumericUpDown).ValueText.Text = e.NewValue.ToString()));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
The property setter will not get called as things change via WPF's data binding, so this technique will not work.
The default, initial value will always be 1000, but data binding may override it. You will need to add a Callback to appropriately notify you when the dependency property value is changed.
For details, see the Dependency Property Callbacks page to see how to implement a property changed callback correctly. This is the appropriate place to set your other (ValueText) property.
I'm having a custom Control that has a dependency property
public static readonly DependencyProperty SelectedUserCodeProperty = DependencyProperty.Register(
"SelectedUserCode",
typeof(decimal),
typeof(SystemUsersControl),
new PropertyMetadata(SelectedUserCodeChanged));
public decimal SelectedUserCode
{
get
{
return (decimal)this.GetValue(SelectedUserCodeProperty);
}
set
{
this.SetValue(SelectedUserCodeProperty, value);
RaisePropertyChanged("SelectedUserCode");
}
}
This control is inside another usercontrol that I'm attempting to get the dependency property above in its viewmodel
this xaml is inside the parent control
<SystemUsers:SystemUsersControl Name="ctrlSystemUsersControl" SelectedUserCode="{Binding SelectedSystemUserCode, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,2,0,0"/>
but nothing is bound to the parent control viewmodel
I don't know what's the problem, it's my first time dealing with dependency properties, I'm considering making the two controls in one :( unless I got any help :)
Don't worry,
SelectedSystemUserCode must be a property . If its a property you will see initial value ,but what will fully support binding for your class is ,implementation of INotifyPropertyChanged. This basic interface will be a messenger for us.
1)When you implement INotifyPropertyChanged,the below event will be added to your class.
public event PropertyChangedEventHandler PropertyChanged;
2)Then create a firing method
public void FirePropertyChanged(string prop)
{
if(PropertyChanged!=null)
{
PropertyChanged(prop);
}
}
3) Register this event for not getting null reference.
in constructor this.PropertyChanged(s,a)=>{ //may do nothing };
4) //You may use Lazy < T > instead of this.
public decimal SelectedSystemUserCode
{
get{
if(_selectedSystemUserCode==null)
{
_selectedSystemUserCode=default(decimal);
}
return _selectedSystemUserCode;
}
set
{
_selectedSystemUserCode=value;
FirePropertyChanged("SelectedSystemUserCode");
//This will be messanger for our binding
}
}
In addition,
As I remember is the default value so you may give a decimal value for that,SelectedUserCodeChanged is callback method its ok also.
//new PropertyMetadata(SelectedUserCodeChanged)
new PropertyMetadata(0) or null
Hope helps.
This is the part where is not working. My dependency property has a default value which is Entradas.Entero, and that value must be run this line:
Grid.SetColumnSpan(button0, 3);
And it should refresh it in my user control design, however there's no changes in it.
public partial class TableroUserControl : UserControl
{
public enum Entradas
{
Entero, Decimal
}
public Entradas Entrada
{
get { return (Entradas)GetValue(EntradaProperty); }
set { SetValue(EntradaProperty, value); }
}
static void textChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
Button button0 = ((TableroUserControl)property).button0;
switch ((Entradas)args.NewValue)
{
case Entradas.Entero:
Grid.SetColumnSpan(button0, 3);
break;
case Entradas.Decimal:
Grid.SetColumnSpan(button0, 2);
break;
}
}
public static readonly DependencyProperty EntradaProperty =
DependencyProperty.Register("Entrada", typeof(Entradas), typeof(TableroUserControl), new PropertyMetadata(Entradas.Entero, new PropertyChangedCallback(textChangedCallBack)));
public TableroUserControl()
{
InitializeComponent();
}
}
You might also consider doing this with a value converter. You should be able to bind the Grid.ColumnSpan attached property of button0 to the Entrada property of your user control. Then use a value converter to convert it to an integer. This way you don't have to deal with callbacks and state/timing issues.
Your dependency property is initialized to Entero. So unless the value is changed to Decimal and then again changed back to Entero you wont hit the property changed callback code.
Make sure that the colspan setter code is hit.
I may be doing this all wrong... so hang with me
I am making a user control with a property which the user can bind to. The in setter for the property, I bind the PropertyChanged listener to the property so I can react to changes to its state. The code behind for this user control looks like this:
public static readonly DependencyProperty NodeProperty =
DependencyProperty.Register("Node", typeof(MockRequirementWrapper), typeof(RecNode2));
public MockRequirementWrapper Node
{
get
{
return (MockRequirementWrapper)GetValue(NodeProperty);
}
set
{
if(Node != null)
Node.PropertyChanged -= Update;
SetValue(NodeProperty, value);
Node.PropertyChanged += new PropertyChangedEventHandler(Update);
OnPropertyChanged(this, "Node");
}
}
then, in another user control, I bind to this property a node I've created elsewhere like this:
<local:RecNode2 Node="{Binding}"/>
What I am finding is the recnode exists and is bound to a node... but if I put a breakpoint in the setter, it never gets called. Am I misunderstanding how the binding works? How do I add my listener when the node changes?
The framework will always call GetValue and SetValue directly, the property is just for convenience and sould never contain logic besides those calls.
If you want to do something on changes register a PropertyChangedCallback in the Metadata when registering the DependencyProperty.
Taken from http://msdn.microsoft.com/en-us/library/ms753358.aspx:
public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
"AquariumGraphic",
typeof(Uri),
typeof(AquariumObject),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnUriChanged)
)
);
public Uri AquariumGraphic
{
get { return (Uri)GetValue(AquariumGraphicProperty); }
set { SetValue(AquariumGraphicProperty, value); }
}