I am adding an extended property to an existing user control, it works fine at design time and runtime with no errors. I do however get an error showing in design mode in the XAML which is:
A 'Binding' cannot be set on the 'SfdName' property of type 'ExtendedSfMaskedEdit'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
How do I clear this please?
XAML below:
<local:ExtendedSfMaskedEdit SfdName="{Binding TestTag}" />
Code below:
Public SfdNameProperty As DependencyProperty = DependencyProperty.Register("SfdName", GetType(String), GetType(ExtendedSfMaskedEdit), New UIPropertyMetadata("", AddressOf SfdNameChanged))
Private Sub SfdNameChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
End Sub
<Bindable(True)>
Public Property SfdName As String
Get
Return DirectCast(GetValue(SfdNameProperty), String)
End Get
Set
SetValue(SfdNameProperty, Value)
End Set
End Property
Related
I am trying to achieve the following in a WPF personal finance app:
In various places I want to display a user control giving details of a asset holding (usually a share, bond etc), the target asset may be changed dynamically by the user in which case the control must be refreshed. Each Asset has a unique identifier, AssetId.
I am using MVVM and I've developed a single window with a View Model that takes AssetID as a parameter (property) and retrieves the relevant details for binding to the View. This work fine. What I'd like to do is make a generic user control with the same functionality so I can basically drop that 'window' inside other windows.
So I pretty much copy-pasted the XAML from that form into a User Control, where I'm struggling is passing in the AssetId from the parent window to the child control.
Google tells me I need a dependency property and here's where I am
Public Class HoldingView
Private _AssetId As Integer
Public AssetIdProperty As DependencyProperty = DependencyProperty.Register("AssetId",
GetType(Integer),
GetType(HoldingView),
New FrameworkPropertyMetadata(New PropertyChangedCallback(AddressOf AssetIDChanged)))
Public Property AssetId As Integer
Get
Return GetValue(AssetIdProperty)
End Get
Set(value As Integer)
SetValue(AssetIdProperty, value)
End Set
End Property
Private Sub AssetIDChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim NewAssetId As Integer
NewAssetId = e.NewValue
Me.DataContext.AssetId = NewAssetId
End Sub
Public Sub New()
' This call is required by the designer.
InitializeComponent()
Me.DataContext = New HoldingViewmodel
End Sub
End Class
Called like this:
<Grid>
<local:HoldingView AssetId="{Binding AssetId}"/>
</Grid>
The code compiles and runs but when I try and load the window that has the user control, the app crashes with this message:
A 'Binding' cannot be set on the 'AssetId' property of type 'HoldingView'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
Which is not that helpful. From my Googling, you can also get this message if the syntax of the DP registration is not spot on, but it looks Ok to my inexperienced eye...
Anybody else had this?
Public AssetIdProperty As DependencyProperty
should be
Public Shared ReadOnly AssetIdProperty As DependencyProperty
Please take a look at Custom Dependency Properties.
Also remove
Me.DataContext = New HoldingViewmodel
because that will effectively break any DataContext-based Bindings like
AssetId="{Binding AssetId}"
where the source property is supposed to be owned by the object in the inherited DataContext, which usually is an object in the application's view model.
Controls should never have their own, "private" view model, but instead handle property changes in code behind. In case of UserControls, there could simply be UI elements in their XAML that would be bound to the UserConrol's own properties.
Hence
Me.DataContext.AssetId = NewAssetId
in the PropertyChangedCallback is pointless and should be removed, as well as
Private _AssetId As Integer
To summarize, it should look like this:
Public Class HoldingView
Public Shared ReadOnly AssetIdProperty As DependencyProperty =
DependencyProperty.Register(
"AssetId",
GetType(Integer),
GetType(HoldingView),
New FrameworkPropertyMetadata(
New PropertyChangedCallback(AddressOf AssetIdPropertyChanged)))
Public Property AssetId As Integer
Get
Return GetValue(AssetIdProperty)
End Get
Set(value As Integer)
SetValue(AssetIdProperty, value)
End Set
End Property
Private Shared Sub AssetIdPropertyChanged(
d As DependencyObject, e As DependencyPropertyChangedEventArgs)
CType(d, HoldingView).AssetIdChanged(e.NewValue)
End Sub
Private Sub AssetIdChanged(id As Integer)
...
End Sub
Public Sub New()
InitializeComponent()
End Sub
End Class
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))
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
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}"/>
I'm trying to get the WebBrowser control to display HTML loaded from a variable. I found this solution Displaying html from string in WPF WebBrowser control so am trying to implement my first DependencyProperty.
Here's my attempt at the implementation:
Public Shared ReadOnly HtmlProperty As DependencyProperty = DependencyProperty.RegisterAttached("Html", GetType(String), GetType(PortraitSingle), New FrameworkPropertyMetadata(OnHtmlChanged))
<AttachedPropertyBrowsableForType(GetType(WebBrowser))> _
Public Shared Function GetHtml(ByVal d As WebBrowser) As String
Return DirectCast(d.GetValue(HtmlProperty), String)
End Function
Public Shared Sub SetHtml(ByVal d As WebBrowser, ByVal value As String)
d.SetValue(HtmlProperty, value)
End Sub
Private Shared Sub OnHtmlChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim wb As WebBrowser = TryCast(d, WebBrowser)
If wb IsNot Nothing Then
wb.NavigateToString(TryCast(e.NewValue, String))
End If
End Sub
and the XAML
<WebBrowser x:Name="HTMLDescription" Grid.RowSpan="2" Margin="10" local:PortraitSingle.Html="{Binding HtmlToDisplay}"></WebBrowser>
First off, the problem I'm having is I'm getting the following errors:
Argument not specified for parameter 'e' of 'Private Shared Sub OnHtmlChanged(d As System.Windows.DependencyObject, e As System.Windows.DependencyPropertyChangedEventArgs)
and
The attachable property 'Html' was not found in type 'PortraitSingle'. (PortraitSingle is the name of my Window).
I can't work out how to get around them.
Also, I can't understand how I actually load my HTML into the WebBrowser control.
Ben
Found my problem.
Needed to add the 'AddressOf' to the DependencyProperty statement
Public Shared ReadOnly HtmlProperty As DependencyProperty = DependencyProperty.RegisterAttached("Html", GetType(String), GetType(PortraitSingle), New FrameworkPropertyMetadata(AddressOf OnHtmlChanged))