A 'Binding' can only be set on a DependencyProperty of a DependencyObject - c#

From a custom control based on TextBox, I created a property named Items, in this way:
public class NewTextBox : TextBox
{
public ItemCollection Items { get; set; }
}
When using the custom control in XAML, I cannot bind the property because it raises exception "A 'Binding' can only be set on a DependencyProperty of a DependencyObject.".
How do I solve this exception?

As a side note, it is also worth noting that you will get these binding errors if you copy and paste between objects and forget to change the second typeof(Object) statement.
I couldn't figure out for a good hour why I was getting this error as everything appeared to be defined and correct. I'd moved my properties into a usercontrol as I wanted to go from a single set to a list. Thus:
public static readonly DependencyProperty FoldersProperty = DependencyProperty
.Register("Folders", typeof(OutlookFolders), typeof(MainWindow),
new FrameworkPropertyMetadata(new OutlookFolders()));
public OutlookFolders Folders
{
get { return GetValue(FoldersProperty) as OutlookFolders; }
set { SetValue(FoldersProperty, value); }
}
Should have become:
public static readonly DependencyProperty FoldersProperty = DependencyProperty
.Register("Folders", typeof(OutlookFolders), typeof(SavedFolderControl),
new FrameworkPropertyMetadata(new OutlookFolders()));
public OutlookFolders Folders
{
get { return GetValue(FoldersProperty) as OutlookFolders; }
set { SetValue(FoldersProperty, value); }
}
Until I did this change I kept receiving the error:
A 'Binding' cannot be set on the property 'Folders' of type 'SavedFolderControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

To solve this exception you need to change the property Items and add a DependencyProperty that will work as a "link" in XAML. The class will be:
public class AutocompleteTextBox : TextBox
{
public ItemCollection Items
{
get {
return (ItemCollection)GetValue(ItemsProperty); }
set {
SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(ItemCollection),
typeof(AutocompleteTextBox),
new PropertyMetadata(default(ItemCollection), OnItemsPropertyChanged));
private static void OnItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// AutocompleteTextBox source = d as AutocompleteTextBox;
// Do something...
}

Here's another gotcha: Ensure that the string in the first argument of DependencyProperty.Register() matches the name of the related property.
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"TheItems", // This is wrong
typeof(ItemCollection),
typeof(AutocompleteTextBox),
new PropertyMetadata(default(ItemCollection), OnItemsPropertyChanged));
I ran into this issue when I renamed my property without changing the string.

One thing I noticed, and I am not sure it is mentioned anywhere, is that the name of your DependencyProperty must match your property name
If your property name is Items, then you DependencyProperty must be ItemsProperty
In my case, as soon as I matched them the error went away

Another potential cause of this is when you provide a bad type for the default value in the metadata.
For instance:
new PropertyMetadata(default(ItemCollection), OnItemsPropertyChanged)
would throw this error if you wrote instead:
new PropertyMetadata(false, OnItemsPropertyChanged)
This can also happen if you are copying and pasting from a code source.

I had the (runtime + designtime) message:
An unhandled exception of type
'System.Windows.Markup.XamlParseException' occurred in
PresentationFramework.dll
Additional information: A 'Binding' cannot be set on the 'Property'
property of type 'Trigger'. A 'Binding' can only be set on a
DependencyProperty of a DependencyObject.
Where I was smart enough to define a Trigger on a VM property..
// incorrect.. cannot have Trigger for VM property
<Trigger Property="{Binding IsExpanded}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</Trigger>
Which should of course be a datatrigger (which uses Binding instead of Property)
<DataTrigger Binding="{Binding IsExpanded}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
Triggers are typically for Controls (Button, TextBox, FrameworkElement etc.) properties.

I had this issue due to a lack of oversight on my part. I wrote
<Button.Visibility>
<MultiBinding Converter="{StaticResource mvbConverter}">
<Binding Path="{Binding isActive}" />
<Binding Path="{Binding isCashTransaction}" />
</MultiBinding>
</Button.Visibility>
when instead i should've wrote
<Button.Visibility>
<MultiBinding Converter="{StaticResource mvbConverter}">
<Binding Path="isActive" />
<Binding Path="isCashTransaction" />
</MultiBinding>
</Button.Visibility>

Related

Dependency Property not set from ViewModel

I create a custom control "CustomAutoCompleteBox" (which inherit of AutoCompleteBox) with one dependency property "CurrentItem".
public static readonly DependencyProperty CurrentItemProperty =
DependencyProperty.Register("CurrentItem", typeof(CityEntity), typeof(CustomAutoCompleteBox),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public CityEntity CurrentItem
{
get { return (CityEntity)GetValue(CurrentItemProperty); }
set { SetValue(CurrentItemProperty, value); }
}
This custom control have also a property "InternalCurrentItem".
public CityEntity InternalCurrentItem
{
get { return _internalCurrentCity; }
set
{
if (_internalCurrentCity == value) return;
_internalCurrentCity = value;
OnPropertyChanged();
CurrentItem = value;
}
}
The DataContext is define to himself in the constructor :
public VilleAutoCompleteBox()
{
DataContext = this;
...
}
And the Style set ItemsSource and SelectedItem like this:
<Style TargetType="{x:Type infrastructure_controls:CustomAutoCompleteBox}" BasedOn="{StaticResource AutoCompleteBoxFormStyle}">
<Setter Property="ItemsSource" Value="{Binding InternalItems, Mode=OneWay}" />
<Setter Property="SelectedItem" Value="{Binding InternalCurrentItem, Mode=TwoWay}" />
...
</Style>
In summary, ItemsSource is bind to internal property "InternalItems" and SelectedItem is bind to internal property "InternalCurrentItem".
For use it, I declare this CustomAutoCompleteBox like this :
<infrastructure_usercontrols:CustomAutoCompleteBox Width="200" CurrentItem="{Binding DataContext.VmCurrentItem, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Mode=TwoWay}" />
I have bind the dependency property "CurrentItem" to the ViewModel's property "VmCurrentItem".
Everything works fine except for one thing.
When I type text in the control, the InternalCurrentItem property changes correctly. Same for the CurrentItem property in my ViewModel.
Concretely, InternalCurrentItem is correctly modified (Set). This property sets the CurrentItem dependency property, and this dependency property sets VmCurrentItem.
The opposite is not true. If I change directly the value of the VmCurrentItem property in the ViewModel, the CurrentItem property is not changed. I do not understand why.
The first case causes the following chain of events:
SelectedItem is changed
InternalCurrentItem is updated by the framework due to the binding
You manually update CurrentItem in the InternalCurrentItem setter
VmCurrentItem is updated by the framework due to the binding
In the opposite direction this is what happens:
VmCurrentItem is changed
CurrentItem is updated by the framework due to the binding
...and that's it. There's no binding and no piece of code that would update InternalCurrentItem when CurrentItem changes. So what you need to do is to register a PropertyChangedCallback for your CurrentItemProperty which will update InternalCurrentItem:
public static readonly DependencyProperty CurrentItemProperty =
DependencyProperty.Register(
"CurrentItem",
typeof(CityEntity),
typeof(CustomAutoCompleteBox),
new FrameworkPropertyMetadata
{
BindsTwoWayByDefault = true,
PropertyChangedCallback = CurrentItemPropertyChanged
});
private static void CurrentItemPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (CustomAutoCompleteBox)d;
control.InternalCurrentItem = (CityEntity)e.NewValue;
}
You need to declare the property the same way as the first:
public static readonly DependencyProperty InternalCurrentItemProperty =
DependencyProperty.Register("InternalCurrentItem", typeof(CityEntity), typeof(CustomAutoCompleteBox),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public CityEntity InternalCurrentItem
{
get{ return (CityEntity)GetValue(InternalCurrentItemProperty); }
set
{
SetValue(InternalCurrentItemProperty, value);
}
}

Setting A dependency property by this

I've created a type of Border called SelectableBorder that has an additional property called "IsSelected". I'm using this property in some triggers e.g.:
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background" Value="{DynamicResource AccentColorBrush3}" />
</MultiTrigger.Setters>
However in the code in the background there is no way for me to set IsSelected, How do I go about creating a property that can be used in xaml triggers and in code in the background?
This is the current SelectableBorder code
public class SelectableBorder : Border
{
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.RegisterAttached("IsSelected", typeof(bool), typeof(Border), new PropertyMetadata(false));
public static void SetIsSelected(UIElement element, bool value)
{
element.SetValue(IsSelectedProperty, value);
}
public static bool GetIsSelected(UIElement element)
{
return (bool)element.GetValue(IsSelectedProperty);
}
}
I am also seeing:
'SelectableBorder' initialization failed: The type initializer for 'SelectableBorder' threw an exception.
Which suggests to me that I'm not doing that well at my first attempt. Could you please guide me in the correct direction to solving these problems?
Since you're adding property to DependencyObject so you can use normal DependencyProperty instead of attached one. You can use attached if you want but then trigger should change. Also owner type of your property should be SelectableBorder instead of Border. You can also add IsSelected CLR wrapper do make it easier to set/get value in code behind.
public class SelectableBorder : Border
{
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(SelectableBorder), new PropertyMetadata(false));
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
}

Style vs inline property setting in silverlight with a custom control

I have a custom silverlight control that is pretty much a glorified text box. I have some properties in it I need to be able to set from the XAML in silverlight, so for each property I have created something like this:
public bool UseCustomTooltips
{
get { return _useCustomTooltips; }
set
{
_useCustomTooltips = value;
DoSomeOtherStuff();
}
}
public static readonly DependencyProperty UseCustomTooltipsProperty = DependencyProperty.Register("UseCustomTooltips",
typeof(bool), typeof(MyControl), new PropertyMetadata(false, PropertyChangedCallback));
In the XAML I can create the control and specify a value for that property like this:
<Controls:MyControl UseCustomTooltips="True" />
The control's state is update, my callback is hit, and all is as it should be. However, when I try to specify that state from a style instead of in each instance of the control like this:
<Style x:Key="MyControl" TargetType="Controls:MyControl">
<Setter Property="Foreground" Value="Yellow"/>
<Setter Property="UseCustomTooltips" Value="True"/>
</Style>
<Controls:MyControl Style="{StaticResource MyControl}" />
my custom properties are never set and my callback is never hit even though the inherited property Foreground takes on the value specified in the style. This leads me to assume there is something wrong with the way my dependency property is wired up but everything I've seen so far online tells me I have it correct.
Can anyone point me in the right direction?
Thanks.
That is not the correct way to register a dependency property.
public bool UseCustomTooltips
{
get { return (bool)GetValue(UseCustomTooltipsProperty); }
set { SetValue(UseCustomTooltipsProperty, value); }
}
public static readonly DependencyProperty UseCustomTooltipsProperty =
DependencyProperty.Register("UseCustomTooltips",
typeof(bool),
typeof(MyControl),
new PropertyMetadata(false, new PropertyChangedCallback(MyCallbackMethod)));
Use the propdp snippet, it really is a beautiful thing.

Make dependency object properties bindable as a static resource?

How to make an array of dependency object properties bindable for later binding as a static resource?
The code I have now, it seems that my DependencyObject bypasses the dependency property system...
I have the following class:
public class ValueMarker : DependencyObject
{
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register("Brush", typeof(Brush), typeof(ValueMarker), new FrameworkPropertyMetadata(Brushes.Aqua));
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(ValueMarker), new FrameworkPropertyMetadata(0d));
public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register("Offset", typeof(double), typeof(ValueMarker), new FrameworkPropertyMetadata(0d));
public Brush Brush
{
get { return (Brush)GetValue(BrushProperty); }
set { SetValue(BrushProperty, value); }
}
public double Offset
{
get { return (double)GetValue(OffsetProperty); }
set { SetValue(OffsetProperty, value); }
}
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
}
In the XAML, I create a resource array of these with some bindings like so:
<x:Array Type="my:ValueMarker" x:Key="plainMarks">
<my:ValueMarker Brush="Red" Offset="-5" Value="{Binding Path=...}" />
<my:ValueMarker Brush="Orange" Offset="-5" Value="{Binding Path=...}"/>
<my:ValueMarker Brush="Orange" Offset="-5" Value="{Binding Path=...}"/>
<my:ValueMarker Brush="Red" Offset="-5" Value="{Binding Path=...}" />
</x:Array>
While debugging the bindings, I've noticed that should I remove the setter for the DP, the XAML would display an error saying the property is missing. It was my understanding that XAML uses DP system to assign value thus enabling binding. In this case, if the XAML expect a 'normal' property, binding is impossible. Anyone can enlighten me on how can I make it work?
The reason you cannot bind your ValueMarkers here is because:
1.They are not in the VisualTree of your window/usercontrol.
2.They are not object of Type that can inherit DataContext even if they are not part of Visual Tree.
So in order to make your ValueMarkers bind to the properties in the View DataContext, first of all you will have to derive them from Freezable class like below:
public class ValueMarker : Freezable
{
//All your Dependency Properties comes here//
protected override Freezable CreateInstanceCore()
{
return new ValueMarker();
}
}
After doing this you can simply bind your object like below:
<my:ValueMarker x:Key="vm1" Brush="Orange" Offset="-5" Value="{Binding Path=Text1}"/>
Here Text1 is property in Windows/usercontrols DataContext
Then you can use this resource as:
<TextBox Text="{Binding Value, Source={StaticResource vm1}, StringFormat=F2}"/>
Similarly you can create resource for other ValueMarkers to use them in binding.
You will not be able to bind by creating the x:Array as simply x:Array not lies in visualtree and does not inherit DataContext hence its elements also have no access to it.
If you still want to use the collection whose element should support binding, then you will need to create your own collection class that should inherit Freezable and exposes DependancyProperty to capture the DataContext and set it on child elements also.

Behavior of DependencyProperty With Regards to Multiple FrameworkElement Instances

So I tried using DependencyProperty to solve my issues with regards to passing the local ViewModel across child Views. However a question popped in my head.
For example I need to make multiple instances of a certain FrameworkElement, say, a UserControl. That UserControl has a DependencyProperty defined. As stated in books, a dependency property instance should be static and readonly. How would the DependencyProperty work in that kind of scenario? Would it work the same way as a conventional UserControl property, or whatever object instance you pass to the DependencyProperty, it'll be passed across all instances of the said UserControl?
Yes, it will operate as a normal property. If you need a property for a specific control, that is one property for a single control, you can use just dependency property. They will be passed through all the instances of the class. But if you want the property on many controls, then should use the attached dependency property, which will be available to all members within a certain namespace. Properties, such as: Canvas.Top, DockPanel.Dock are attached DependencyProperty.
Sample of attached dependency properties:
public class MyDependencyClass : DependencyObject
{
public static readonly DependencyProperty IsSelectedProperty;
public static void SetIsSelected(DependencyObject DepObject, Boolean value)
{
DepObject.SetValue(IsSelectedProperty, value);
}
public static Boolean GetIsSelected(DependencyObject DepObject)
{
return (Boolean)DepObject.GetValue(IsSelectedProperty);
}
private static bool IsSelectedValid(object Value)
{
if (Value.GetType() == typeof(bool))
{
return true;
}
else
{
return false;
}
}
static MyDependencyClass()
{
FrameworkPropertyMetadata MetaData = new FrameworkPropertyMetadata((Boolean)false);
IsSelectedProperty = DependencyProperty.RegisterAttached("IsSelected",
typeof(Boolean),
typeof(MyDependencyClass),
MetaData,
new ValidateValueCallback(IsSelectedValid));
}
}
They also contain useful callback's like OnPropertyChangedCallback, ValidateValueCallback which can be placed in an additional logic.
These properties are also available in XAML. Add "local" namespace:
xmlns:local="clr-namespace:SampleApp"
Define for element's:
<Button Name="Button1" local:MyDependencyClass.IsSelected="True" />
<Button Name="Button2" local:MyDependencyClass.IsSelected="False" />
...
<ListBoxItem Name="Sample" local:MyDependencyClass.IsSelected="True" />
Access to property in triggers:
<Trigger Property="local:MyDependencyClass.IsSelected" Value="True">
<Setter Property="Background" Value="Green" />
</Trigger>
Work with attached dependency properties in code:
if (CurrentButtonName == MyButton.Name)
{
MyDependencyClass.SetIsSelected(CurrentButton, true);
}
else
{
MyDependencyClass.SetIsSelected(CurrentButton, false);
}
For more info see: http://msdn.microsoft.com/en-us/library/ms749011.aspx

Categories