Here is the code:
new FrameworkPropertyMetadata( (uint) 100,...
We can set in max value here as 100, is there a way to set minimum value too? Let's say I want this default value to be between 5 and 100? I looked all over Google did not find any answers. Can anybody please suggest a solution. Thanks!
Code tried according to Reed Copsey
public static bool IsValidReading(object value)
{
uint v = (uint)value;
return (!v.Equals(0));
}
Full code:
public static readonly DependencyProperty Result =
DependencyProperty.Register(
"ResultLimit", typeof( uint ), typeof( UI ),
new FrameworkPropertyMetadata( ( uint )10, new PropertyChangedCallback( ResultChanged ) ), IsValidReading );
The value is not the max value - it's the default value.
There is no way to specify the min or max in the metadata directly. This should be handled via a Callback registered with the Dependency Property.
Related
I have the following custom datagrid column, for passing in the value of the DatePart dependency property as the ConverterParameter to the editing element's converter:
Public Class DataGridTimeColumn
Inherits DataGridTextColumn
Shared ReadOnly DatePartProperty = DependencyProperty.Register("DatePart", GetType(DateTime?), GetType(DataGridTimeColumn), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AddressOf RefreshBinding))
Property DatePart As DateTime?
Get
Return GetValue(DatePartProperty)
End Get
Set(value As DateTime?)
SetValue(DatePartProperty, value)
End Set
End Property
Private Shared Sub RefreshBinding(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim tc As DataGridTimeColumn = d
tc.Binding = tc.Binding
End Sub
Public Overrides Property Binding As BindingBase
Get
Return MyBase.Binding
End Get
Set(value As BindingBase)
Dim b As Data.Binding = value
With b
.Converter = New TimeConverter
.ConverterParameter = DatePart
End With
MyBase.Binding = b
End Set
End Property
End Class
With the following XAML:
<my:DataGridTimeColumn Header="From" Binding="{Binding FromDate}" DatePart="{Binding FromDate}" />
<my:DataGridTimeColumn Header="Until" Binding="{Binding TillDate}" DatePart="{Binding TillDate}" />
But RefreshBinding is never called (I've set a breakpoint and it's never triggered), and thus DatePart is always Nothing(null) when the ConverterParameter is set. How can I fix this?
Edit
In C#:
public class DataGridTimeColumn : DataGridTextColumn
{
static readonly DependencyProperty DatePartProperty = DependencyProperty.Register(
"DatePart", typeof(DateTime?), typeof(DataGridTimeColumn),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, RefreshBinding)
);
public DateTime? DatePart
{
get { return (DateTime?)GetValue(DatePartProperty); }
set { SetValue(DatePartProperty, value); }
}
private static void RefreshBinding(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tc = (DataGridTimeColumn)d;
tc.Binding = tc.Binding;
}
public override System.Windows.Data.BindingBase Binding
{
get { return base.Binding; }
set
{
var b = (Binding)value;
b.Converter = new TimeConverter();
b.ConverterParameter = DatePart;
base.Binding = b;
}
}
}
I might not understand what you're exactly trying to do, but this seems like a lot of work to just view/edit the DatePart. Would using a formatter and a DataGridTextColumn be easier? WPF Binding StringFormat Short Date String
First, please use debug to check your binding path whether it is ok.
Second, check your FromDate property whether is triggered.
Third, please use Snoop to monitor this DP whether is updated.
Last, i remember DataGrid has dynamic binding bug, so i am not sure above steps can solve your problem. If it is not ok , please let me know.
I would recommend Darlene approach if you only need to format the value. If you really need a converter for some magic I would recommend to set it via your binding in the XAML (I don't really know why you are trying to set it in the property definition).
Your XAML will be something like this:
<my:DataGridTimeColumn Header="From" Binding="{Binding FromDate, Converter={StaticResource myTimeConverter}} />
TimeConverter must implement IValueConverter and an instance must be defined in your window/datagrid resources. You can also define the TwoWay in that binding.
You can find a full example here.
In case you want to combine two or more pieces of data you can use a MultiBinding.
Tried to understand what you want to achieve from the comments and if I got it right you can do it using only the view model and a converter with a small workaround:
In your converter ConvertBack method, you can try to figure out if the value entered by the user has only the time part and if so set the date to something that will not be used (for example DateTime.MinValue):
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var convertedDateTime = DateTime.Parse(value.ToString());
var hasOnlyTime = value.ToString().Trim().Length <= 5; // This should be replaced with something more consistent
if (hasOnlyTime)
return new DateTime(DateTime.MinValue.Year, DateTime.MinValue.Month, DateTime.MinValue.Day, convertedDateTime.Hour, convertedDateTime.Minute, convertedDateTime.Second);
return convertedDateTime;
}
Then in the object setter for the date you can check that and use the required value, if the date part is DateTime.MinValue change it back to the previous one, if not leave it as it is:
private DateTime _fromDate;
public DateTime FromDate
{
get { return _fromDate; }
set
{
if (value.Date != DateTime.MinValue)
_fromDate = value;
else
_fromDate = new DateTime(_fromDate.Year, _fromDate.Month, _fromDate.Day, value.Hour, value.Minute, value.Second);
OnPropertyChanged("FromDate");
}
}
Tested on .NET 4.5 and works as expected.
I have a textbox in xaml here is code in .cs file:
public static readonly DependencyProperty dp =
DependencyProperty.Register(
"result", typeof( uint ), typeof( ui ),
new FrameworkPropertyMetadata( ( uint )100, new PropertyChangedCallback( ResultChanged ) ) );
private static void ResultChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e )
{
var input = ( ui )d;
var value = ( uint )e.NewValue;
}
Above code works great it is not allowing any alphabets or invalid characters to be entered in the textbox. But how can i change above code so that user will not be able to enter "0" in the textbox? So basically allow all uint except 0.
There are many ways to do this....the simplest and easiest is just to use the NumericUpDown control from the Extended WPF Toolkit....set the min/max range and you are sorted.
http://wpftoolkit.codeplex.com/wikipage?title=NumericUpDown
If you do want to do the logic all yourself then....
You could specify a "Coercion Callback" and/or a "Validate Callback" in the Register of the Dependency Property depending on your needs/requirements i.e. do you need to fix the value to a range, or show an error indicator to the user?
"Coercion" is where you adjust the value to something else e.g. in this case you might decide to adjust any value of 0, to 1 (or possibly null).
"Validation" is where the value is checked and you say if it is correct or not (by returning true of false)...if you return false...then an exception gets raised.
You use ValidatesOnException on a Binding to the DependencyProperty in order for that "error" to be propagated to the user interface in the way of display of an ErrorTemplate (the default one shows a red border around a control).
http://www.shujaat.net/2010/07/wpf-validation-validatevaluecallback.html
http://msdn.microsoft.com/en-us/library/ms745795.aspx
http://wpf.2000things.com/2010/11/11/122-validating-a-dependency-property/
http://wpf.2000things.com/2010/11/12/123-coercing-a-dependency-property/
http://wpf.2000things.com/2010/12/09/150-an-example-of-using-propertychanged-and-coercevalue-callbacks/
In this case I will show just using the Coercion Callback...as I'm presuming you don't want an invalid value to be waiting inside your TextBox. If you want the Validate one...then see the links above. (you don't need a Changed callback, so that's set to null).
public static readonly DependencyProperty dp =
DependencyProperty.Register(
"result", typeof( uint ), typeof( ui ),
new FrameworkPropertyMetadata(
( uint )100,
null,
new CoerceValueCallback( ResultCoerceValue )
)
);
private static object ResultCoerceValue
(DependencyObject depObj, object baseValue)
{
uint coercedValue = (uint)baseValue;
if ((uint)baseValue == 0)
coercedValue = 1; // might be able to set to null...but not sure on that.
return coercedValue;
}
There are also different techniques you can use like ValidationRules, possibly MaskedTextBox, and using PreviewTextInput.
http://social.msdn.microsoft.com/Forums/en/wpf/thread/990c635a-c14e-4614-b7e6-65471b0e0e26
How do I get a TextBox to only accept numeric input in WPF?
http://weblogs.asp.net/monikadyrda/archive/2009/06/24/wpf-textbox-validation.aspx
i have created a custom user control which im using on my main xaml control:
<Controls:CustomControl Width="200" Height="20"
TotalCount="{Binding TotalRecordCount}" SuccessCount="{Binding ValidationCount}" ErrorCount="{Binding ValidationErrorCount}" Margin="0,5,0,0" HorizontalAlignment="Left">
</Controls:CustomControl>
I wanted to make the private variables of my custom usercontrol being ErrorCount,SuccessCount and total count(which are of type int32) Bindable so i can bind values to them. Right now when i try to bind it to my item source it gives the following error e exception message is "Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.Int32'
Many thanks,
Michelle
You need to implement the Properties using DependencyProperty don't use private variables to hold these values. Here is an example:-
#region public int SuccessCount
public int SuccessCount
{
get { return (int)GetValue(SuccessCountProperty); }
set { SetValue(SuccessCountProperty, value); }
}
public static readonly DependencyProperty SuccessCountProperty =
DependencyProperty.Register(
"SuccessCount",
typeof(int),
typeof(CustomControl),
new PropertyMetadata(0, OnSuccessCountPropertyChanged));
private static void OnSuccessCountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomControl source = d as CustomControl;
int value = (int)e.NewValue;
// Do stuff when new value is assigned.
}
#endregion public int SuccessCount
In order for a property to be "Bindable", meaning that you can set it using DataBinding, that property needs to be a Dependency Property. For more info on Dependency Properties, please check this MSDN article.
hope this helps :)
What is the order in which attached properties are applied to an object ? I guess I should ignore this, but here my scenario:
I've got an attached property to stick the VM to the View, and then, another attached property that depend on the first one. I'm trying to see what happen if the second is set up before the first, but I can't manage to get the error! ie the first ( the model ) is always set up before the second, whatever is the order in xaml. Who is driving the order of assigment? Can I change it?
Now I'm dealing with the late assigmement by subscribing the proeprty change event:
DependencyPropertyDescriptor dd = DependencyPropertyDescriptor.FromProperty(FrameworkElement.DataContextProperty,depo.GetType());
dd.AddValueChanged(depo, (s, a) =>
{
ChangeDatacontext(s as DependencyObject);
}
and for simulate the problem I setup manually a new datacontext to the object.
Thanks,
Felix
I can't directly answer this question, because I never rely on which property is set before the other, but you can manage things with a method that both attached properties use.
here is an example from my current code:
public static readonly DependencyProperty RuleVMProperty =
DependencyProperty.RegisterAttached("RuleVM", typeof(DocumentRuleViewModel), typeof(DocumentRuleViewModel), new UIPropertyMetadata(null, RuleVMChanged));
public static void RuleVMChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var el = GetRefid(sender);
var vm = args.NewValue as DocumentRuleViewModel;
if(vm==null)
return;
vm.SetDocumentFromRefid(sender, el);
}
public static readonly DependencyProperty RefidProperty =
DependencyProperty.RegisterAttached("Refid", typeof(XmlElement), typeof(DocumentRuleViewModel), new UIPropertyMetadata(RefidChanged));
public static void RefidChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var el = args.NewValue as XmlElement;
var vm = GetRuleVM(sender);
if (vm == null)
return;
vm.SetDocumentFromRefid(sender, el);
}
private void SetDocumentFromRefid(DependencyObject sender, XmlElement element)
{
... // this is where the actual logic sits
}
so essentially you have two changed handlers and whichever triggers last executes the logic because it sees if the other property is null.
I have a GridView and want to serialize column widths across sessions. My idea of how to accomplish this is to attach a behavior to the GridViewColumns in such a way that each time the width of a column is changed the attached event handler is called and stores the new width. This already works well.
The only remaining problem:
How do I know in the event handler which GridViewColumn sent the event? I obviously need to know that in order to be able to store the width and later set the width on the correct column when restoring. Ideally I would like to use the name specified in XAML as column identifier.
Here is my code. XAML:
<GridView>
<GridViewColumn x:Name="GridColumn0"
HeaderTemplate="{StaticResource GridViewHeaderTemplate}" HeaderContainerStyle="{StaticResource GridViewHeaderStyle}"
Header="{x:Static strings:Strings.MainWindow_AppLog_Header_Severity}"
behaviors:GridViewBehaviors.PersistColumnWidth="True">
C# (please scroll down - question at bottom):
// Register the property used in XAML
public static readonly DependencyProperty PersistColumnWidthProperty =
DependencyProperty.RegisterAttached("PersistColumnWidth", typeof(bool), typeof(GridViewBehaviors),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnPersistColumnWidthChanged)));
// Provide read access to the value
public static bool GetPersistColumnWidth(DependencyObject d)
{
return (bool)d.GetValue(PersistColumnWidthProperty);
}
// Provide write access to the value (set from XAML)
public static void SetPersistColumnWidth(DependencyObject d, bool value)
{
d.SetValue(PersistColumnWidthProperty, value);
}
// This gets called once when the XAML is compiled to BAML
// Set the event handler
private static void OnPersistColumnWidthChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
GridViewColumn column = sender as GridViewColumn;
if (column == null)
return;
// Couple the UI event with a delegate
if ((bool)args.NewValue)
((INotifyPropertyChanged)column).PropertyChanged += new PropertyChangedEventHandler(PersistWidth);
else
((INotifyPropertyChanged)column).PropertyChanged -= new PropertyChangedEventHandler(PersistWidth);
}
// Deal with the events
static void PersistWidth(object sender, PropertyChangedEventArgs e)
{
GridViewColumn column = sender as GridViewColumn;
if (column == null)
return;
// We are only interested in changes of the "ActualWidth" property
if (e.PropertyName != "ActualWidth")
return;
// Ignore NaNs
if (column.ActualWidth == double.NaN)
return;
// Persist the width here
// PROBLEM:
// How to get a unique identifier for column, ideally its name set in XAML?
}
I don't think you will be able to access the x:Name, but you should be able to define your own column type that has a Name property that you can set.
So instead of adding GridViewColumns you add NamedColumn objects in your xaml.
Define a type that derives from GridViewColumn:
public class NamedColumn : GridViewColumn
{
public string ColumnName {get; set;}
}
And use it in your xaml:
<GridView>
<NamedColumn ColumnName="GridColumn0" .... blablalba more stuff here />
...
NoW you should be able to cast the sender to a NamedColumn and access its name property.
can you use column.Header.ToString()? see here for why name from XAML isn't possible: How to get x:Name value runtime
Thanks for your answers, Robert, Thomas and Rune. I like Runes answer, but I found something even easier for my situation here:
I changed the type of the attached property from bool to string and simply store the name of the column there. The relevant changes are below.
XAML:
<GridViewColumn
behaviors:GridViewBehaviors.PersistColumnWidth="MainWindow_AppLog_Column0">
C#:
public static readonly DependencyProperty PersistColumnWidthProperty =
DependencyProperty.RegisterAttached("PersistColumnWidth", typeof(string),
typeof(GridViewBehaviors), new FrameworkPropertyMetadata(string.Empty,
new PropertyChangedCallback(OnPersistColumnWidthChanged)));
static void PersistWidth(object sender, PropertyChangedEventArgs e)
{
// This now yields "MainWindow_AppLog_Column0"
string columnID = GetPersistColumnWidth(column);
I'm not sure it would work, but you can try something like that:
INameScope nameScope = NameScope.GetNameScope(column);
string name = nameScope
.Where(kvp => kvp.Value == column)
.Select(kvp => kvp.Key.ToString())
.FirstOrDefault();