Converting normal property to dependency property - wpf

I have a control that I am using for my new application. This control has a regular property as such.
Public Property Value() As String
Get
If AutoCompleteTextBox.SearchText Is Nothing Then
Return String.Empty
Else
Return AutoCompleteTextBox.SearchText.ToString.Trim
End If
End Get
Set(value As String)
AutoCompleteTextBox.SearchText = value
End Set
End Property
Edit:
So, after multiple tries, I am finally at this stage.
Public Shared ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(String), GetType(AutoCompleteBox))
Public Property Value() As String
Get
Return Me.GetValue(ValueProperty).ToString
End Get
Set(value As String)
Me.SetValue(ValueProperty, value)
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
This is the dependency property. This property is still not binding. No errors are shown in output window for binding.
Text="{Binding RelativeSource={RelativeSource Self}, Path=Value, Mode=TwoWay}"
This is my binding method. I have no idea what else I can do. At least if there was an error, I could have figured out something. Without any error, I am just a headless chicken here.

Please refer to the following url for all the dependency fundamentals
http://www.wpftutorial.net/dependencyproperties.html
Basically, you can get a property changed event of dependency property by providing a FrameworkPropertyMetadata.
new FrameworkPropertyMetadata( [Default Value],
OnCurrentTimePropertyChanged);
And you can get back the target control (DependencyObject) at the event handler and implement your logic over there
private static void OnCurrentTimePropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
AutoCompleteTextBox control = source as AutoCompleteTextBox;
string time = (string)e.NewValue;
// Put some update logic here...
}

Declaring a dependency property in a control is a good thing.
You could make some binding in the xaml (sorry I don't have your XAML - I imagine).
Something like :
<TextBox x:Name="AutoCompleteTextBox"
Text="{Binding RelativeSource={RelativeSource=Self},Path=Value}"/>
Regards

TextBox has a property called Text. When you access Text property it will give you text entered in TextBox. Same is your case. Now why you want to convert it into a DP ? A DP would be useful if you want o bind this DP to some other control.
Extend this control itself. Make a new control and introduce this new DP.
While a DP is used where you want to bind this property to some control. This property then gets updated from control or control gets updated from this DP depending upon binding mode set.
How to do binding :
<TextBox x:Name="UserInput" />
<uc:MyAutoCompleteTextBox ValueDP="{Binding Text, ElementName=UserInput, Mode=OneWay}" />
MyAutoCompleteTextBox is new control which extends(inherits) from your old AutoComplete control.
If you want to apply some filtering logic or anything else, you can apply it in your DP itself like this :
Get
someVariable = TryCast(Me.GetValue(ValueProperty), String)
' apply somg logic to someVariable
' use your old Value property from here
Return someVariable
End Get
There are many WPF Binding tutorials on net.
I recommend :
http://blog.scottlogic.com/2012/04/05/everything-you-wanted-to-know-about-databinding-in-wpf-silverlight-and-wp7-part-one.html

Just change your code with following code and you should be good
your code
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(String), GetType(AutoCompleteBox))
Public Property Value() As String
Get
Return TryCast(Me.GetValue(ValueProperty), String)
End Get
Set(value As String)
Me.SetValue(ValueProperty, value)
End Set
End Property
New code
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(String), GetType(AutoCompleteBox))
Public Property Value() As String
Get
If AutoCompleteTextBox.SearchText Is Nothing Then
Return String.Empty
Else
Return AutoCompleteTextBox.SearchText.ToString.Trim
End If
End Get
Set(value As String)
AutoCompleteTextBox.SearchText = value
End Set
End Property
This DP will do what your Older property was doing. But just think about your requirement there can be a better way of writing the things.
Thanks

Related

WPF: Design time support for dependency properties with default values

I have written a custom control based on a ListBox. It contains a default ItemTemplate which shows an image given to the ListBox by a custom dependency property. The control also contains a default image, which is used when the user doesn't give an image to the dependency property.
This works so far, but now I've found a little problem and I don't know how to fix that.
When I use my custom control in the XAML designer, it first shows the default image. When I set the image's dependency property to an other image, the new image is immediately shown in the XAML designer.
But when I remove the XAML attribute for the new image again, the XAML designer only shows a white rectangle instead of the default image.
I assume it's because with setting the image's dependency property to some value and then removing it I nulled the value. But even when I check for null in the CoerceCallback and give back the default image when the coerced value is null, doesn't work.
What's the best way to support fallback values for dependency properties?
TestControl.vb
Public Class TestControl
Inherits ListBox
Private Shared _defaultResources As ResourceDictionary
Shared Sub New()
_defaultResources = New ResourceDictionary
_defaultResources.Source = New Uri("...")
End Sub
Public Shared ReadOnly TestProperty As DependencyProperty = DependencyProperty.Register(NameOf(TestControl.Test),
GetType(ImageSource),
GetType(TestControl),
New FrameworkPropertyMetadata(Nothing,
AddressOf TestControl.OnTestChanged,
AddressOf TestControl.OnTestCoerce))
Public Property Test As ImageSource
Get
Return DirectCast(MyBase.GetValue(TestControl.TestProperty), ImageSource)
End Get
Set(value As ImageSource)
MyBase.SetValue(TestControl.TestProperty, value)
End Set
End Property
Private Shared Sub OnTestChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
End Sub
Private Shared Function OnTestCoerce(d As DependencyObject, value As Object) As Object
If (value Is Nothing) Then
Return TryCast(_defaultResources.Item("TestImage"), ImageSource)
End If
Return value
End Function
Public Sub New()
Me.Test = TryCast(_defaultResources.Item("TestImage"), ImageSource)
End Sub
End Class
When I use that control like this
<local:TestControl ItemsSource="{Binding Items}" />
every item shows the default image at design time. When I change the XAML to
<local:TestControl ItemsSource="{Binding Items}"
Test="{StaticResource NewImage}" />
every item shows the new item at design time. But when I remove the Test="{StaticResource NewImage}" again, it doesn't go back to the default image.
Ok, after some testing (using this technique) I have discovered the source of your issue.
First of all, you are not using PropertyMetadata to set your default value, but instead the constructor. I assume you have a good reason to do so, but this essentially means that now you are relying on the coercion callback to set the default value.
However, it is not called (the framework assumes that your "true" default value - Nothing - doesn't need to be validated) after you remove the
Test="{StaticResource TestString}" line. Only the OnTestChanged
is called. This means we can use it to restore the default value:
void OnTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is null)
{
((TestControl)d).Test = yourDefaultImage;
return;
}
//Actual "OnChanged" code
}
A clumsy solution indeed, but it works. Depending on your exact situation, you might also want to take a look at Binding's FallbackValue and TargetNullValue properties:
Test="{Binding Source={ }, FallbackValue={ }, TargetNullValue={ }}"

WPF INotifyErrorInfo Validation.Error Event Not Raising

I'm encountering a strange problem. Despite setting everything correctly, the Validation.Error doesn't get fired.
Here are the details:
<DataTemplate x:Key="dtLateComers">
<TextBox Text="{Binding ParticipantTag, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True, NotifyOnSourceUpdated=True}" Validation.Error="Validation_Error" >
</DataTemplate>
Code behind (VB.Net) to set ItemsSource of HeaderedItemsControl:
hicLateComers.ItemsSource = _LateComersViewModels
_LateComersViewModels is ObservableCollection(Of ParticipantViewModel)
Implementation of ParticipantViewMode:
Public Class ParticipantViewModel
Implements INotifyPropertyChanged, IDataErrorInfo
Private _ParticipantTag As String = ""
Public Property ParticipantTag() As String
Get
Return _ParticipantTag
End Get
Set(ByVal value As String)
_ParticipantTag = value
_ParticipantTag= _ParticipantTag.ToUpper
NotifyPropertyChanged("ParticipantTag")
End Set
End Property
Public ReadOnly Property Item(byVal columnName As String) As String Implements IDataErrorInfo.Item
Get
Dim errorString As String = String.Empty
If columnName.Equals("ParticipantTag") Then
If not ParticipantValidationManager.IsValidKeypadTag(_ParticipantTag, True) then
errorString = "Incorrect entry. Please try again."
End If
End If
Return errorString
End Get
End Property
Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
Get
Throw New NotImplementedException()
End Get
End Property
End Class
Problem
When I set ItemSource property (as mentioned above in code), Item index is called as many times as there are items in _LaterComersViewModels. Validation works and as a result I get red circle next to TextBox. However, Validation_Error never gets fired until I start typing in Textbox. Typing in TextBox changes the Property binds to it and validate it. Base on validation Validation.Error event is raised, and handled by application. Within that event handler I maintain a count of errors.
So the Question is, why Validation.Error doesn't get raised when one/more items fail on a validation rule during initial data binding? Though it does get raised once property is changed by typing into that TextBox.
Feel free to share any idea, assumption or a solution. Any type of help will be appreciated. Thanks.
Side note: I've a simple C# application which doesn't use data templating. In that application, Validation.Error event gets raised perfectly on start, and on property change. Though in that application, Model is binding to DataContext property of Grid.
Since Validation.Error is an attached event, you could hook up the event handler on the HeaderedItemsControl:
<HeaderedItemsControl x:Name="hicLateComers" ItemTemplate="{StaticResource dtLateComers}" Validation.Error="Validation_Error" />
The result should be pretty much the same since you can easily access both the TextBox and the ParticipantViewModel object in the event handler:
Private Sub Validation_Error(sender As Object, e As ValidationErrorEventArgs)
Dim textBox = CType(e.OriginalSource, TextBox)
Dim participant = CType(textBox.DataContext, ParticipantViewModel)
'...
End Sub

Dependency property not getting registered in one specific control in WPF

I have two different controls, both of them have a property called Value. I am using pretty much the same code in both cases. And yet, in one case, I get binding error saying "Binding can be done only for dependency property in dependency object.
Below is the code to implement the dependency property
Public Class Calender
Implements INotifyPropertyChanged
Public Shared ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(String), GetType(Calendar))
Public Property Value() As String
Get
If Not TryCast(Me.GetValue(ValueProperty), String) Is Nothing Then
Return TryCast(Me.GetValue(ValueProperty), String)
Else
Return String.Empty
End If
End Get
Set(value As String)
Me.SetValue(ValueProperty, value)
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
This is the exact same code I am using in another control. It works fine in that control, but in this case, it does not work. Also, when I use this Value property as
Value="{Binding MeasuringTapeCalibrationDue,Mode=TwoWay}"
it displays a squiggly line in XAML editor, saying "Value property was already registered by Calendar". I have the above code registering the value property just once.
You have to replace GetType(Calendar) by GetType(OtherControl) in the dependency property registration in the other control:
Public Class OtherControl
Public Shared ValueProperty As DependencyProperty =
DependencyProperty.Register("Value", GetType(String), GetType(OtherControl))

How to make the command property of a wpf button a dependency property of a user control

I am trying to teach myself the basics of creating user controls in wpf. To that end I have been experimenting with building a data navigation control to allow navigation through the records being retrieved by various view models. My long term plan is for a completely self contained custom control, but I'd like to master the smaller points first so to that end I'd like to know how I can make the Command, and Command Parameter properties ( as well as the Is Enabled property) of buttons that form part of my user control dependency properties of the user control itself.
I have succeeded to make the various image and image height and width properties of the various buttons dependency properties of the overall user control but thus far have not had any success with the Command, Command Parameter and is Enabled properties.
I'd welcome any suggestions anyone could proffer.
I have the following already (I set for each button in my user control):
#Region "Next Button"
Public Property ImageNext() As ImageSource
Get
Return DirectCast(GetValue(ImageNextProperty), ImageSource)
End Get
Set(value As ImageSource)
SetValue(ImageNextProperty, value)
End Set
End Property
Public Shared ReadOnly ImageNextProperty As DependencyProperty = DependencyProperty.Register("ImageNext", GetType(ImageSource), GetType(DataNavigator), New UIPropertyMetadata(Nothing))
Public Property ImageNextWidth() As Double
Get
Return CDbl(GetValue(ImageNextWidthProperty))
End Get
Set(value As Double)
SetValue(ImageNextWidthProperty, value)
End Set
End Property
Public Shared ReadOnly ImageNextWidthProperty As DependencyProperty = DependencyProperty.Register("ImageNextWidth", GetType(Double), GetType(DataNavigator), New UIPropertyMetadata(16.0))
Public Property ImageNextHeight() As Double
Get
Return CDbl(GetValue(ImageNextHeightProperty))
End Get
Set(value As Double)
SetValue(ImageNextHeightProperty, value)
End Set
End Property
Public Shared ReadOnly ImageNextHeightProperty As DependencyProperty = DependencyProperty.Register("ImageNextHeight", GetType(Double), GetType(DataNavigator), New UIPropertyMetadata(16.0))
This however has been adding properties to standard wpf buttons, now what I want to do is access properties of those buttons that already exist and bind to them (via my user control) from my viewmodels
It is the same as any other dependency property.
You declare the DP like this:
Public Shared ReadOnly ThisCommandProperty As DependencyProperty = _
DependencyProperty.Register("ThisCommand", GetType(ICommand), _
GetType(thiscontrol), Nothing)
Public Property ThisCommand As ICommand
Get
Return CType(GetValue(ThisCommandProperty), ICommand)
End Get
Set(ByVal value As ICommand)
SetValue(ThisCommandProperty, value)
End Set
End Property
and in the XAML of your user control:
<UserControl ...>
<Button Command={Binding ThisCommand} ... />
</UserControl>
You set the parameter in the same way, but with type object, and you have to cast it so the correct type in your command handler.
When you use the UserControl, it is like this:
<local:thisControl ThisCommand={Binding whateverCommandYouWantToBindTo},
ThisCommandParameter={Binding whateverParameterYouWant)>
It is really just the same as any other DP, except for the type. Of course, whateverCommandYouWantToBindTo has to be set up as an ICommand too.
People might also tell you that defining usercontrols is bad and to use templates instead, and it's probably a better approach in most cases. But if you want to learn about DPs, I say learn.
Here is an example that I have working in front of me:
Public Shared ReadOnly EditButtonCommandProperty As DependencyProperty = _
DependencyProperty.Register("EditButtonCommand", _
GetType(ICommand), GetType(PersonListControl), Nothing)
Public Property EditButtonCommand As ICommand
Get
Return CType(GetValue(EditButtonCommandProperty), ICommand)
End Get
Set(ByVal value As ICommand)
SetValue(EditButtonCommandProperty, value)
End Set
End Property
Public Shared ReadOnly EditButtonCommandParameterProperty As DependencyProperty = _
DependencyProperty.Register("EditButtonCommandParameter", GetType(Object), _
GetType(PersonListControl), Nothing);
Public Property EditButtonCommandParameter As Object
Get
Return CType(GetValue(EditButtonCommandParameterProperty), Object)
End Get
Set(ByVal value As Object)
SetValue(EditButtonCommandParameterProperty, value)
End Set
End Property
And in the UserControl XAML:
<StackPanel>
<ListBox ... />
<Button
...
Command="{Binding EditButtonCommand}"
CommandParameter="{Binding EditButtonCommandParameter}"/>
</StackPanel>
And I use this UserControl like this:
<local:PersonListControl
...
EditButtonCommand="{Binding PersonListEditCommand}"
EditButtonCommandParameter="{Binding Parents}"/>

Binding from multiple sources

My scenario:
I've got a Silverlight Application with a View, where i want to bind the textboxes to an object (two-way) and all labels to a dictionary holding the label translations.
My approach was to set the datacontext of the page to a dictionary with two items, one of them is the object and the other is the translation-dictionary.
In xaml the code looks like the following:
<TextBlock Text="{Binding [dict].[name],FallbackValue='Fallback'}" />
<TextBox Text="{Binding [obj].name,Mode=TwoWay}" />
This works initially, if I however change the object on the datacontext, the xaml is not notified about any changes and doesn't update.
I've had a working solution using a Converter for the translations, however due to the limitations on one converterparameter I didn't like the solution. In addition it wasn't possible to place a fallback-value in the textblock, which resulted in "invisible" textblocks while designing the page.
Any suggestions on how to solve this issue? It doesn't have to be using my dictionary, it would be also okay if i could set the datacontext to the object (which works) and bind the labels somehow different.
I know this will get a lot of traditional answers, but I would also like to put forward something completely original we tried (and succeeded) doing ourselves for more efficient localisation of Silverlight using Attached Properties instead of binding:
Localisation of Silverlight projects after completion
What would be the most flexible is rather than setting the DataContext for the view to a dictionary, you would be better off having the DataContext be something like a ViewModel. That is, a simple class that holds multiple properties: one for your "object" and one for your translation dictionary.
Then have the class that acts as your ViewModel implement INotifyPropertyChanged.
Create a method in your class called OnPropertyChanged that takes in a string representing your property name. In that method raise the PropertyChanged event passing in the instance of the ViewModel class and a new PropertyChangedEventArgs passing in the property name.
Back in the properties you created (object and dictionary) in the Set, after setting the value, call OnPropertyChanged passing in the string name of that property. This will notify the UI that the value of this property has changed and will essentially rebind the control to that property.
Finally, bind the Text properties of your controls on your View to the new properties you just created in your ViewModel. That should ensure that the controls on the view stay up to date.
I found a solution, but wasn't able to answer my own question (8h limit..)
I think this is just the approach Hydroslide suggested.
Create a class which holds all data and implements INotifyPropertyChanged
Public Class MyDatacontext
Implements ComponentModel.INotifyPropertyChanged
'Properties
Private _obj As Object
Private _dict As Dictionary(Of String, String)
'Events
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
'Methods
Public Property Obj As Object
Get
Return _obj
End Get
Set(ByVal value As Object)
_obj = value
'Notify the xaml about the changed Object
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Obj"))
End Set
End Property
Public Property Dict As Dictionary(Of String, String)
Get
Return _dict
End Get
Set(ByVal value As Dictionary(Of String, String))
_dict = value
'Notify the xaml about the changed translation
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Dict"))
End Set
End Property
End Class
Define a private var in your page code
Private mycontext As New MyDatacontext
In the constructor of your page, fill your "mycontext" with the desired data
mycontext.Dict = LoadDictionary()
mycontext.Obj = LoadObject()
Me.DataContext = mycontext
Change your xaml to the following
<TextBlock Text="{Binding Dict.[name],FallbackValue=MyFallback}" />
<TextBox Text="{Binding Obj.name,Mode=TwoWay}" />
Update your object/dictionary as you like using
mycontext.Obj = LoadNextObject()

Resources