WPF Binding on DependencyObject error - c#

I want to pass an object as a parameter to a Converter. Since I can't do that using a ConverterParameter, I have used a DependencyProperty instead.
Here is the code -
public class FilteredColumnConverter : DependencyObject, IValueConverter
{
public DataGridFilter FilterObject
{
get { return (DataGridFilter) GetValue(FilterObjProperty); }
set { SetValue(FilterObjProperty, value); }
}
public static readonly DependencyProperty FilterObjProperty =
DependencyProperty.Register( "FilterObj",
typeof(DataGridFilter),
typeof(FilteredColumnConverter),
new PropertyMetadata(null));
// With implementations for Convert and ConvertBack
}
My XAML:
<UserControl.Resources>
<helpers:FilteredColumnConverter x:Key="filteredColumnConverter"
FilterObject="{Binding myFilterObj}"/>
</UserControl.Resources>
I'm getting the following error :
A Binding cannot be set on the FilterObject property of type FilteredColumnConverter. A Binding can only be set on a DependencyProperty of a DependencyObject.
What seems to be the problem? I have a FilterObject is a dependency property and I have followed the naming conventions as well.

try register DP with name FilterObject not FilterObj
DependencyProperty.Register("FilterObject", ...

Related

How to define a `DependencyProperty` for a collection type in UWP?

I'm trying to create a DependencyObject which is created from Xaml.
It has a DependencyProperty of type List<object> defined like so:
public List<object> Map
{
get { return (List<object>)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
// Using a DependencyProperty as the backing store for Map. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MapProperty =
DependencyProperty.Register("Map", typeof(List<object>), typeof(MyConverter), new PropertyMetadata(null));
Xaml:
<MyConverter x:Key="myConverter">
<MyConverter.Map>
<TextPair First="App.Blank" Second ="App.BlankViewModel"/>
</MyConverter.Map>
</MyConverter>
I keep receiving Cannot add instance of type 'UwpApp.Xaml.TextPair' to a collection of type 'System.Collections.Generic.List<Object>.
What can cause this error? Thank you.
You defined the type of your DependencyProperty with typeof(List<object>). This means the property requires exactly this type. Since List<object> is not
a TextPair we need to be more generic. Instead of using a special generic list type just use IList as type and add new List<object>() as default value. This should solve your problem.
public IList Map
{
get { return (IList)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
public static readonly DependencyProperty MapProperty =
DependencyProperty.Register("Map", typeof(IList),
typeof(MyConverter), new PropertyMetadata(new List<object>()));
EDIT:
Looks like UWP behaves a bit different than WPF. To run this code in UWP you need to use the generic IList<object> instead of IList as property type.
Using the method shown by Fruchtzwerg you will get an unintentional singleton: one instance of List shared between all instances of MyCoverter.
See Collection-Type DependencyProperties in the Microsoft Docs.
To avoid this, you need to set the value separately, like this
public class MyConverter
{
public IList Map
{
get { return (IList)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
public static readonly DependencyProperty MapProperty =
DependencyProperty.Register("Map", typeof(IList),
typeof(MyConverter), new PropertyMetadata(null));
public MyConverter
{
// SetValue causes DependencyProperty precedence issue so bindings
// will not work.
// WPF supports SetCurrentValue() which solves this but UWP does not
this.SetValue(MapProperty, new List<object>());
}
}
However, this will cause another problem due to DependencyProperty Value Precedence: you will no longer be able to bind to MapProperty as you have used SetValue which has a higher precedence.
So the best & correct solution is to use a CreateDefaultValueCallback. You can do this as follows:
public static readonly DependencyProperty MapProperty =
DependencyProperty.Register("Map", typeof(IList),
typeof(MyConverter), PropertyMetadata.Create(
new CreateDefaultValueCallback(() =>
{
return new List<object>();
}
}));

Data-Binding with Custom Dependency-Property of UserControl is not working

I have a UserControl with 2 custom DependencyPropertys (ColumnsCount, RowsCount):
public partial class CabinetGrid : UserControl
{
public static readonly DependencyProperty ColumnsCountProperty =
DependencyProperty.Register("ColumnsCount", typeof (int), typeof (CabinetGrid));
public static readonly DependencyProperty RowsCountProperty =
DependencyProperty.Register("RowsCount", typeof (int), typeof (CabinetGrid));
public int ColumnsCount
{
get { return (int) GetValue(ColumnsCountProperty); }
set { SetValue(ColumnsCountProperty, value); }
}
public int RowsCount
{
get { return (int) GetValue(RowsCountProperty); }
set { SetValue(RowsCountProperty, value); }
}
}
And here's the DataBinding:
<view:CabinetGrid Grid.Column="1" Grid.Row="2" x:Name="GridRack" ColumnsCount="{Binding SelectedRoom.ColumnCount}" />
whereas the window's DataContext has a property SelectedRoom which invokes PropertyChanged-Event.
Thru debugging, I got to know that the DataContext of the UserControl is set properly.
However, when SelectedRoom has changed (=> I selected another item in a list), the DependencyProperty ColumnsCount of my UserControl is not updated.
I am very frustrated, as I already spent an entire day debugging through this unexpected shit, using tools like XAMLSpy and WpfSpoon.
Please, help.
EDIT:
Clemens already pointed out, that a breakpoint in the CLR-Property wrapping the DependencyProperty (ColumnsCount) is not fired. This is a major issue, since I have to call some methods on the change. I'm trying to use the PropertyChangedCallback, but am currently experiencing some errors.
In order to get notified about value changes of a dependency property, you should specify a PropertyChangedCallback in the PropertyMetadata when you register the property.
public static readonly DependencyProperty ColumnsCountProperty =
DependencyProperty.Register(
"ColumnsCount", typeof(int), typeof(CabinetGrid),
new PropertyMetadata(OnColumnsCountPropertyChanged));
private static void OnColumnsCountPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var cabinetGrid = (CabinetGrid)obj;
// do something with the CabinetGrid instance
}

unable to use two way Binding with my control

I am new to binding. I have binded slider value to my control's property and my controls property get changed when I change the slider value.
Now, when I need to change the slider value by changing my property value, it does not work..
I modified the xaml from some internet source, but still not get the expected output.
can anyone help me out...
<Grid>
<cc:MyControl Name="mycntrl" ZoomPercentage="{Binding ElementName=slider,Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></cc:MyControl>
<Slider Name="slider" Margin="20,20,20,400" Minimum="100" Maximum="400"></Slider>
</Grid>
Updated:
My code behind for my ZoomPercentage dependency property is below
public double ZoomPercentage
{
get
{
return (double)GetValue(ZoomPercentageProperty);
}
set
{
SetValue(ZoomPercentageProperty, value);
}
}
My dependency registration
public static readonly DependencyProperty ZoomPercentageProperty = DependencyProperty.Register("ZoomPercentage", typeof(double), typeof(MyControl), new FrameworkPropertyMetadata(ZoomPercentagePropertyChanged));
public static void ZoomPercentagePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (args.OldValue != null)
{
if ((double)args.NewValue != (double)args.OldValue)
{
MyControl mycontrol = obj as MyControl;
mycontrol .ZoomTo((int)((double)args.NewValue));
}
}
}
Your ZoomPercentage property should be implemented as a Dependencyproperty
Something like this
public class MyControl:UserControl
{
public MyControl() : base() { }
public double ZoomPercentage
{
get { return (double)this.GetValue(ZoomPercentageProperty); }
set { this.SetValue(ZoomPercentageProperty, value); }
}
public static readonly DependencyProperty ZoomPercentageProperty = DependencyProperty.Register(
"ZoomPercentage", typeof(double), typeof(MyControl:),new PropertyMetadata(0));
}
read more here
If you want a data bound control in the UI to update after changes made in code then you have to do one of two things. One option is to correctly implement the INotifyPropertyChanged interface in the class that you declared your Value property.
The other is to declare your Value property as a DependencyProperty, although you should only really do this in the code behind of your Window or UserControl and opt for the first method if you are using a view model. The purpose of these two methods is for you to 'plug in' to WPF notification framework, so that your UI control will update. Please read the linked pages for more information.

AttachedProperty not propagating to children?

Trying to create my own custom AttachedProperty for a WPF DependencyObject failed to actually do what I wanted it to do, and I am a bit worried that I (again) did not understand a WPF concept fully.
I made a very simple test class to show where my problem lies. From the MSDN Documentation, I copied
public class TestBox : TextBox
{
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
"IsBubbleSource",
typeof(Boolean),
typeof(TestBox)
);
public static void SetIsBubbleSource(UIElement element, Boolean value)
{
element.SetValue(IsBubbleSourceProperty, value);
}
public static Boolean GetIsBubbleSource(UIElement element)
{
return (Boolean)element.GetValue(IsBubbleSourceProperty);
}
public Boolean IsBubbleSource
{
get
{
return (Boolean)GetValue(IsBubbleSourceProperty);
}
set
{
SetValue(IsBubbleSourceProperty, value);
}
}
}
Now, placing my new and funky TextBox into a Grid like this
<Grid vbs:TestBox.IsBubbleSource="true">
<vbs:TestBox x:Name="Test" Text="Test" >
</vbs:TestBox>
</Grid>
I expected every child that does not set the IsBubbleSource property itself to "inherit" it from its parent grid. It does not do this; a MessageBox.Show(Test.IsBubbleSource.ToString()) shows "false". The attached property is set to true. I checked this using an OnPropertyChanged event handler. Did I miss something?
Thanks!
By default, attached properties are not inherited. You have to specify it when you define the property:
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
"IsBubbleSource",
typeof(Boolean),
typeof(TestBox),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits)
);

Binding-Target never gets set in custom objects declared in *.resources; binding evaluation fails

I have a xaml view like this:
<Tv:TvPanel
xmlns:Tv="...">
<Tv:TvPanel.Resources>
<Tv:Parameters x:Key="parameterList">
<Tv:ParameterDefinition ParameterName="MyParam" InitialValue="123abc">
<Tv:ValueBinding Value="{Binding ElementName=myTextBox, Path=Text}"/>
</Tv:ParameterDefinition>
</Tv:Parameters>
<Tv:TvXmlDataProvider x:Key="dataProvider"
Source="http://server/myXml.xml"
Parameters="{StaticResource parameterList}"/>
</Tv:TvPanel.Resources>
<Tv:TvPage DataContext="{StaticResource dataProvider}" >
<Tv:TvText x:Name="myTextBox"
Tv:TvPage.Left="360" Tv:TvPage.Top="10"
Height="30" Width="200"/>
<Tv:TvButton Tv:TvPage.Left="560" Tv:TvPage.Top="10"
Content="Search"/>
/*SOME MORE CODE */
all the controls extending some standard wpf controls (canvas, label, ...).
The classes Parameters, ParameterDefinition & ValueBinding look like this:
[ContentProperty("ParameterDefinitions")]
public class Parameters
{
private readonly List<ParameterDefinition> parameterDefinitions = new List<ParameterDefinition>();
public List<ParameterDefinition> ParameterDefinitions { get { return parameterDefinitions; } }
}
[ContentProperty("ValueBindings")]
public class ParameterDefinition : DependencyObject
{
public static DependencyProperty ParameterNameProperty = DependencyProperty.Register("ParameterName", typeof (string), typeof (ParameterDefinition), new PropertyMetadata(""));
public string ParameterName
{
get { return (string) this.GetValue(ParameterNameProperty); }
set{ this.SetValue(ParameterNameProperty, value);}
}
public static DependencyProperty InitialValueProperty = DependencyProperty.Register("InitialValue", typeof(object), typeof(ParameterDefinition), new PropertyMetadata(new object()));
public object InitialValue
{
get { return this.GetValue(InitialValueProperty); }
set { this.SetValue(InitialValueProperty, value); }
}
private readonly List<ValueBinding> valueBindings = new List<ValueBinding>();
public List<ValueBinding> ValueBindings { get { return this.valueBindings; } }
}
public class ValueBinding : DependencyObject
{
public static DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(ValueBinding), new PropertyMetadata(""));
public string Value
{
get { return (string)this.GetValue(ValueProperty); }
set { this.SetValue(ValueProperty, value); }
}
}
The problem is that the databinding
Value="{Binding ElementName=myTextBox, Path=Text}"
not works in ValueBinding (Value is never set, has always the default value (and actions are performed to change the value)). The StaticResource binding in the dataprovider works perfectly, and element-to-element binding under the UIElements also works perfectly.
It compiles, and it seems there's no problem with the scope of elementname in the resourcedictionary.
What did i miss?
Edit1:
I forgot to mention the output of the VS-Console:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Text; DataItem=null; target element is 'ValueBinding' (HashCode=14342221); target property is 'Value' (type 'String')
In the TvPanel the DataContext is set to this.
I tried to derive from Freezable, instead of DependencyObject ==> same Problem.
I also tried to bind this way:
<Tv:ValueBinding>
<Tv:ValueBinding.Value>
<Binding ElementName="SearchText" Path="Text"/>
</Tv:ValueBinding.Value>
</Tv:ValueBinding>
but no difference.
I think that your class should derive from Freezable, otherwise there's no inheritance context: the DataContext is not propagated and the bindings don't work. See this article for more information.

Categories