Creating Multiple Extended Controls - wpf

I have created an Extended TextBox that Inherits a standard WPF TextBox, what I am now trying to do is create other extended control types like a TextBlock, ListBox, ComboBox etc. All controls will have the same DependencyProperties as shown below so I am trying to find a way to implement this without repeating the DependencyProperty code behind each new extended control.
Public Class ExtendedTextBox
Inherits TextBox
Public Shared MandatoryProperty As DependencyProperty = DependencyProperty.Register("Mandatory", GetType(Boolean), GetType(ExtendedTextBox))
Public Shared ReadOnly HasAnyErrorsProperty As DependencyProperty = DependencyProperty.Register("HasAnyErrors", GetType(Boolean), GetType(ExtendedTextBox))
End Class

You could define attached properties that can be set on any UIElement:
Public Class MyProperties
Public Shared ReadOnly MandatoryProperty As DependencyProperty = DependencyProperty.RegisterAttached("Mandatory", GetType(Boolean), GetType(MyProperties))
Public Shared Sub SetMandatory(ByVal element As UIElement, ByVal value As Boolean)
element.SetValue(MandatoryProperty, value)
End Sub
Public Shared Function GetMandatory(ByVal element As UIElement) As Boolean
Return CType(element.GetValue(MandatoryProperty), Boolean)
End Function
End Class
XAML:
<TextBox local:MyProperties.Mandatory="True" />
<ListBox local:MyProperties.Mandatory="False" />

Related

WPF Dependency Property - set from XAML

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

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 public property in UserControl to property on parent does not work [duplicate]

I have a WPF UserControl project named FormattedTextBox that contains a TextBox and a WPF window project in the same solution.
My user control has two dependency properties registered like this:
public static readonly DependencyProperty NumberProperty =
DependencyProperty.Register("Number",
typeof(double),
typeof(FormattedTextBox),
new FrameworkPropertyMetadata());
public static readonly DependencyProperty NumberFormatStringProperty =
DependencyProperty.Register("NumberFormatString",
typeof(string),
typeof(FormattedTextBox),
new FrameworkPropertyMetadata());
I make an instance of my usercontrol in the main window. The main window inplements INotifyPropertyChanged and has a property named MyNumber. In the XAML of the main window I try to bind to MyNumber like this:
Number="{Binding Path=MyNumber,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
The binding doesn't work - I never get into the get or set on the Number property in the user control. Can anybody help?
When a dependency property is set in XAML (or by binding or animation etc.), WPF directly accesses the underlying DependencyObject and DependencyProperty without calling the CLR wrapper. See XAML Loading and Dependency Properties,
Implications for Custom Dependency Properties.
In order to get notified about changes of the Number property, you have to register a PropertyChangedCallback:
public static readonly DependencyProperty NumberProperty =
DependencyProperty.Register("Number",
typeof(double),
typeof(FormattedTextBox),
new FrameworkPropertyMetadata(NumberPropertyChanged));
private static void NumberPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var textBox = obj as FormattedTextBox;
...
}

Hide WPF elements in Visual Studio designer

I have a WPF form which basically looks like this:
<Window ...>
<Grid>
<DockPanel>
[content shown during normal operation]
</DockPanel>
<Grid Background="#CCCC" Visibility="Hidden">
[overlay grid which is only shown during special circumstances]
</Grid>
</Grid>
</Window>
The overlay grid hides everything else (i.e. the "normal content") and is only shown under special circumstances (i.e. if the network connection goes down). This works perfectly fine when running the program.
Now, in design mode, the problem is that Visual Studio ignores the Visibility="Hidden". Usually, this makes perfect sense (after all, I want to be able to edit the hidden UI elements), but in my case it's annoying, because it prevents me from editing the stuff in the DockPanel in the designer.
So, what I'd like to do is something like that:
<Grid Background="#CCCC" Visibility="Hidden" VS.ShowInDesigner="False">
[overlay grid which is only shown during special circumstances]
</Grid>
But, alas, there is no such property, or at least none that I know of. Any ideas?
Starting from VS2012 you can just use the Blend namespace IsHidden attribute:
add if not already present xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
put d:IsHidden="true" on element you want to hide at design time only
Nice solution, I was having a similar problem and I agree that there are cases where it's needed. Here is a minor update that allows you to edit the value to turn IsHidden on and off while designing. I also applied a ScaleTransform instead of setting Width and Height to reduce screen artifacts a bit if control grips etc are displayed and to avoid conflicts if the control being hidden already has Width and Height properties set (assuming that the control doesn't already have a LayoutTransform set on it).
Public Class DesignModeTool
Public Shared ReadOnly IsHiddenProperty As DependencyProperty = DependencyProperty.RegisterAttached( _
"IsHidden", GetType(Boolean), GetType(DesignModeTool), _
New FrameworkPropertyMetadata(False, New PropertyChangedCallback(AddressOf OnIsHiddenChanged)))
Public Shared Sub SetIsHidden(ByVal element As FrameworkElement, ByVal value As Boolean)
element.SetValue(IsHiddenProperty, value)
End Sub
Public Shared Function GetIsHidden(ByVal element As FrameworkElement) As Boolean
Return DirectCast(element.GetValue(IsHiddenProperty), Boolean)
End Function
Private Shared Sub OnIsHiddenChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If System.ComponentModel.DesignerProperties.GetIsInDesignMode(d) AndAlso True.Equals(e.NewValue) Then
With DirectCast(d, FrameworkElement)
.LayoutTransform = New ScaleTransform(0.001, 0.001)
End With
ElseIf System.ComponentModel.DesignerProperties.GetIsInDesignMode(d) AndAlso False.Equals(e.NewValue) Then
With DirectCast(d, FrameworkElement)
.LayoutTransform = Nothing
End With
End If
End Sub
End Class
Nice work! I translated to C# and change the property it's changing to RenderTransform.
static class DesignModeTool
{
public static readonly DependencyProperty IsHiddenProperty =
DependencyProperty.RegisterAttached("IsHidden",
typeof(bool),
typeof(DesignModeTool),
new FrameworkPropertyMetadata(false,
new PropertyChangedCallback(OnIsHiddenChanged)));
public static void SetIsHidden(FrameworkElement element, bool value)
{
element.SetValue(IsHiddenProperty, value);
}
public static bool GetIsHidden(FrameworkElement element)
{
return (bool)element.GetValue(IsHiddenProperty);
}
private static void OnIsHiddenChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!DesignerProperties.GetIsInDesignMode(d)) return;
var element = (FrameworkElement)d;
element.RenderTransform = (bool)e.NewValue
? new ScaleTransform(0, 0)
: null;
}
}
Since there is no built-in way to do this, I decided to implement a solution myself, which was surprisingly easy to do using attached properties:
Public Class DesignModeTool
Public Shared ReadOnly IsHiddenProperty As DependencyProperty = DependencyProperty.RegisterAttached( _
"IsHidden", GetType(Boolean), GetType(DesignModeTool), _
New FrameworkPropertyMetadata(False, New PropertyChangedCallback(AddressOf OnIsHiddenChanged)))
Public Shared Sub SetIsHidden(ByVal element As UIElement, ByVal value As Boolean)
element.SetValue(IsHiddenProperty, value)
End Sub
Public Shared Function GetIsHidden(ByVal element As UIElement) As Boolean
Return DirectCast(element.GetValue(IsHiddenProperty), Boolean)
End Function
Private Shared Sub OnIsHiddenChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If System.ComponentModel.DesignerProperties.GetIsInDesignMode(d) AndAlso True.Equals(e.NewValue) Then
With DirectCast(d, FrameworkElement)
.Width = 0
.Height = 0
End With
End If
End Sub
End Class
After declaring a namespace, the feature can be used like this:
<Grid ... local:DesignModeTool.IsHidden="True">
[stuff I don't want to be shown in the designer]
</Grid>
Other than not using the designer (really, consider this) you could separate the contents of the Grid into a separate UserControl. That way, you could just update that UserControl in isolation from the visibility logic.
I ran into a similar problem recently.
I am using a Rectangle to obscure the main window during a modal dialog's execution. I have the Visibility data bound, but the Rectangle made the designer unusable. I mad the Z index a one time data bind, and a fallback value was lower than the window I wanted to obscure. When the application starts up, the Rectangle's Z index is bound to a higher value than the window.
I am on the other side... hate VS 2012 for hiding hidden WPF controls in designer. I need to see them so i have modified gregsdennis code to:
public class DesignModeTool
{
public static readonly DependencyProperty IsHiddenProperty = DependencyProperty.RegisterAttached("IsHidden", typeof(bool), typeof(DesignModeTool), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsHiddenChanged)));
public static void SetIsHidden(FrameworkElement element, bool value)
{
element.SetValue(IsHiddenProperty, value);
}
public static bool GetIsHidden(FrameworkElement element)
{
return (bool)element.GetValue(IsHiddenProperty);
}
private static void OnIsHiddenChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!DesignerProperties.GetIsInDesignMode(d)) return;
var element = (FrameworkElement)d;
element.Visibility=Visibility.Visible;
}
}
wpfClasses2:DesignModeTool.IsHidden="False" will show the control in designer mode.

Resources