I have a simple problem that I just cant find a good solution for.
I have a textbox bound to a double property value. The user can enter values into the textbox, but I only want to allow values between 0 and 100. I would like to show a red box around the textbox if an invalid value is entered while the textbox still has focus (UpdateSourceTrigger="PropertyChanged"). Should the user click away from the textbox, I want to clamp the value using a value converter on UpdateSourceTrigger="LostFocus".
Its easy to do either the validation rule or the converter, but I can not combine them as I want the validation to trigger on UpdateSourceTrigger="PropertyChanged" and the converter should trigger on UpdateSourceTrigger="LostFocus". Unfortunately I can only choose either one or the other when setting up the binding on my TextBox.Text.
Any good ideas about how I could implement this functionality?
Thank you
/Peter
That's an interesting question. I'm not sure I have a complete solution, but I'd like to throw out a couple ideas.
What do you think of creating a new class that derives from TextBox? It could have two dependency properties, MinValue and MaxValue. Then it could override OnLostFocus. (Disclaimer: I haven't tested the following code.)
public class NumericTextBox : TextBox
{
public static readonly DependencyProperty MinValueProperty =
DependencyProperty.Register("MinValue", typeof(double), typeof(NumericTextBox), new UIPropertyMetadata(Double.MinValue));
public static readonly DependencyProperty MaxValueProperty =
DependencyProperty.Register("MaxValue", typeof(double), typeof(NumericTextBox), new UIPropertyMetadata(Double.MaxValue));
public double MinValue
{
get { return (double)GetValue(MinValueProperty); }
set { SetValue(MinValueProperty, value); }
}
public double MaxValue
{
get { return (double)GetValue(MaxValueProperty); }
set { SetValue(MaxValueProperty, value); }
}
protected override void OnLostFocus(System.Windows.RoutedEventArgs e)
{
base.OnLostFocus(e);
double value = 0;
// Parse text.
if (Double.TryParse(this.Text, out value))
{
// Make sure the value is within the acceptable range.
value = Math.Max(value, this.MinValue);
value = Math.Min(value, this.MaxValue);
}
// Set the text.
this.Text = value.ToString();
}
}
This would eliminate the need for a converter, and your binding can use UpdateSourceTrigger=PropertyChanged to support your validation rule.
My suggestion admittedly has its drawbacks.
This approach would require you to have validation-related code in two places, and they'd need to match. (Maybe you could override OnTextChanged too, and set the red border there instead.)
This approach requires you to put rules in the view layer rather than in business objects, which you may or may not find acceptable.
Related
I'm very new to WPF and while studying (particularly creating a user control), I stumbled upon this thing called "DependencyProperty".
I understand how it works in code but why and when do we need it when I can just create a property and expose it for public use.
Example:
XAML:
<UserControl.....>
<StackPanel Orientation="Vertical">
<TextBlock x:Name="label" Text="Hello"/>
<TextBlock x:Name="text" Text="World!" />
</StackPanel>
</UserControl>
CS file:
public partial SampleUserCtrl : UserControl
{
public string LabelText { get { return this.label.Text; } set { this.label.Text = value; } }
public string TextBoxText { get { return this.text.Text; } set { this.text.Text = value; } }
}
DependecyProperty in WPF has different uses.
Advantages compared to normal .NET property
Reduced memory footprint It's a huge dissipation to store a field for
each property when you think that over 90% of the properties of a UI
control typically stay at its initial values. Dependency properties
solve these problems by only store modified properties in the
instance. The default values are stored once within the dependency
property.
Value inheritance When you access a dependency property the value is
resolved by using a value resolution strategy. If no local value is
set, the dependency property navigates up the logical tree until it
finds a value. When you set the FontSize on the root element it
applies to all textblocks below except you override the value.
Change notification Dependency properties have a built-in change
notification mechanism. By registering a callback in the property
metadata you get notified, when the value of the property has been
changed. This is also used by the databinding.
As you dig deeper to WPF you will also stumble upon DataBinding and object-oriented design patterns like MVVM or MVPVM. Both patterns rely on DataBinding which is achieved through using of Dependency Properties. You cannot perform data binding if it is not a dependency property.
Basically data binding through dependency properties allow the user to update the view when updating a value through code.
Ex: In Windows Forms in order to update a label text you assign its value on the code behind like this:
lbl1.Text = "foo";
In data binding where as you bind in the XAML (View)
<label Text = "{Binding foo}"></label>
and update the values in your code behind:
foo = "foo";
I am not an expert myself so sorry if I might sound confusing.
Dependency property has many benefits over normal property.
A dependency property value can be set by referencing a resource
It can reference a value through data binding
It can be animated. When an animation is
applied and is running, the animated value operates at a higher
precedence than any value (such as a local value) that the property
otherwise has.
You can learn more about it from msdn.
The main advantage of DP property you can find anything in DP like your Control info and you current DataContext info.
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(MyUserControl), new PropertyMetadata(null, (s, e) => OnChangedValue(s, e)));
private static void OnChangedValue(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
If I create a custom control like this:
public class MyControl : ContentControl
{
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(ObservableCollection<object>),
typeof(MyControl),
new PropertyMetadata(null));
public MyControl()
{
// Setup a default value to empty collection
// so users of MyControl can call MyControl.Items.Add()
Items = new ObservableCollection<object>();
}
public ObservableCollection<object> Items
{
get { return (ObservableCollection<object>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
}
And then allow the user to bind to it in Xaml like this:
<DataTemplate>
<MyControl Items="{Binding ItemsOnViewModel}"/>
</DataTemplate>
Then the binding never works! This is due to the Dependency Property Precedence, which puts CLR Set values above Template bindings!
So, I understand why this isn't working, but I wonder if there is a solution. Is it possible to provide a default value of ItemsProperty to new ObservableCollection for lazy consumers of MyControl that just want to add Items programmatically, while allowing MVVM power-users of My Control to bind to the same property via a DataTemplate?
This is for Silverlight & WPF. DynamicResource setter in a style seemed like a solution but that won't work for Silverlight :(
Update:
I can confirm SetCurrentValue(ItemsProperty, new ObservableCollection<object>()); does exactly what I want - in WPF. It writes the default value, but it can be overridden by template-bindings. Can anyone suggest a Silverlight equivalent? Easier said than done! :s
Another Update:
Apparently you can simulate SetCurrentValue in .NET3.5 using value coercion, and you can simulate value coercion in Silverlight using these techniques. Perhaps there is a (long-winded) workaround here.
SetCurrentValue workaround for .NET3.5 using Value Coercion
Value Coercion workaround for Silverlight
Can't you just specify the default property of the dependency property:
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
"Items",
typeof(ObservableCollection<object>),
typeof(CaseDetailControl),
new PropertyMetadata(new ObservableCollection<object>()));
or am I missing what you are after?
Edit:
ah... in that case how about checking for null on the getter?:
public ObservableCollection<object> Items
{
get
{
if ((ObservableCollection<object>)GetValue(ItemsProperty) == null)
{
this.SetValue(ItemsProperty, new ObservableCollection<object>());
}
return (ObservableCollection<object>)GetValue(ItemsProperty);
}
set
{
this.SetValue(ItemsProperty, value);
}
}
When ObservableCollection properties misbehave, I try throwing out assignments to that property. I find that the references don't translate right and bindings get lost, somehow. As a result, I avoid actually setting ObservableCollection properties (preferring, instead, to clear the existing property and add elements to it). This becomes really sloppy with a DependencyProperty because you're going to call your getter multiple times in your setter. You might want to consider using INotifyPropertyChanged instead. Anyway, here's what it'd look like:
EDIT: Blatantly stole the getter from SteveL's answer. I reworked it a touch so that you only have a single call to GetValue, is all. Good work around.
public ObservableCollection<object> Items
{
get
{
ObservableCollection<object> coll = (ObservableCollection<object>)GetValue(ItemsProperty);
if (coll == null)
{
coll = new ObservableCollection<object>();
this.SetValue(ItemsProperty, coll);
}
return coll;
}
set
{
ObservableCollection<object> coll = Items;
coll.Clear();
foreach(var item in value)
coll.Add(item);
}
}
Note that this is depending on your default to set correctly. That means changing the static ItemsProperty default to be a new ObservableCollection of the correct type (i.e. new PropertyMetadata(new ObservableCollection()). You'll also have to remove that setter in the constructor. And note, I've no idea if that'll actually work. If not, you'll want to move to using INotifyPropertyChanged for sure...
I am new to WPF so after reading for a while I deduce that my problem needs to be handled with this pattern: DependencyProperty.
I want my ToggleButton to have another boolean property.
My problem is where should I assign this property, and how? Inside the object that is bound to the ToggleButton?
Let's say I have a class cell (which is bound to this Button) that when clicked I want that from this point on, it would hold new face with trigger on.
My new property will be:
bool wasClick
Can someone explain to me how I should write it and tell me more about this new concept?
EDIT:
The main topic is where should I define it so I want it asoocited to a Button but where should I write the code. Lets say I have a class that is bound to a Button. Should I write:
public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register(
... "IsSpinning", typeof(Boolean),
in this class or should I write it in my view model? If so, where and how?
As the name implies (kind of poorly), a dependency property is a property whose value can depend on something else. Generally, this means a property whose value gets determined automatically (and dynamically) by the WPF framework under certain conditions. The most common conditions are:
The property has a default value, or inherits its value from an ancestor in the visual tree. In this case, the property's value is determined without it ever being set.
The property is the target of data binding.
The property's value is set by an animation.
Not all properties whose value gets set by the WPF framework need to be dependency properties. Any CLR property with a public getter and setter can be the source of a two-way data binding.
In your case, it sounds like you don't really need a dependency property, not if you're using a view model. You could just do this (assuming that you've implemented property-change notification in your class):
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; } }
set
{
if (value == _IsChecked)
{
return;
}
_IsChecked = value;
WasChecked = WasChecked || value;
OnPropertyChanged("IsChecked");
}
}
private bool _WasChecked;
public bool WasChecked
{
get { return _WasChecked; }
private set
{
if (value == _WasChecked)
{
return;
}
_WasChecked = value;
OnPropertyChanged("WasChecked");
}
}
I'm a starter at WPF, now i would like to make a WPF userControl library which include a Rating bar userControl. All the steps of creating the rating Bar has been done, however i would like to add a property RatingValue:
public static readonly DependencyProperty RatingValueProperty =
DependencyProperty.Register("RatingValue", typeof(int), typeof(RatingControl),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(RatingValueChanged)));
public int RatingValue
{
get { return (int)GetValue(RatingValueProperty); }
set
{
SetValue(RatingValueProperty, value);
}
}
private static void RatingValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
//... change the rating value
}
that the user of my UserControl can modify by a value from 0 to 5 that are shown in a dropdown list (combo box) in the Properties windows (as some exist properties of Usercontrols like Visibility, windows style, background ...)
How can i do?
Thank you very much in advance,
Viet
Create a class derived from TypeConverter.
Override GetStandardValues and GetStandardValuesSupported (and optionally GetStandardValuesExclusive).
From GetStandardValues, return a collection containing the values you want to appear in the combo box.
Apply TypeConverterAttribute to the RatingValue property, specifying the type of your type converter.
Alternatively, depending on the semantics of RatingValue, you might consider making it an enum. This feels a bit weird because the values are numeric -- but it would have the advantage of constraining the values at a type level, and it would automatically give you a combo box with no need for you to implement a type converter.
I am learning WPF and am trying to create my first UserControl. My UserControl consists of
StackPanel
StackPanel contains a Label and TextBox
I am trying to create two Dependency Properties
Text for the Label
Orientation for the StackPanel - The orientation will affect the position of the Label and TextBox effectively
I have successfully created a Text dependency property and bind it to my UserControls . But when I created the Orientation property, I seem to get following error in get property
The as operator must be used with a reference type or nullable type ('System.Windows.Controls.Orientation' is a non-nullable value type)
public static DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(System.Windows.Controls.Orientation), typeof(MyControl), new PropertyMetadata((System.Windows.Controls.Orientation)(Orientation.Horizontal)));
public Orientation Orientation
{
get { return GetValue(OrientationProperty) as System.Windows.Controls.Orientation; }
set { SetValue(OrientationProperty, value); }
}
Appreciate your help.
Edit:
I changed the code as below and it seem to work as expected. But is this the correct way to solve the problem?
public Orientation Orientation
{
get
{
Orientation? o = GetValue(OrientationProperty) as System.Windows.Controls.Orientation?;
if (o.HasValue)
{
return (System.Windows.Controls.Orientation)o.Value;
}
else
{
return Orientation.Horizontal;
}
}
set { SetValue(OrientationProperty, value); }
}
The error message says it all. The as operator can only be used with a Type that is nullable (reference type, or Nullable<T>), because it will return either the value cast, or null.
What you're trying to use it on is an enumeration.
Just use a regular cast:
get { return (System.Windows.Controls.Orientation) GetValue(OrientationProperty); }
Reasons why:
You define a default value in your DependencyProperty.Register call, eliminating any default null value
Your DependencyProperty is typeof(Orientation), which doesn't allow for nulls
Your class's property definition is Orientation, which doesn't allow for nulls
Any attempt to set an invalid value via direct calls to SetValue(OrientationProperty, null) will receive an exception, so your property getter won't ever see a null value even by a naughty user of it.