Custom control ContentProperty DataBinding - wpf

I'm running in a issue while trying to use dependency properties in objects which are parts of a collection, inside acustom control, collection identified with the "ContentProperty" attribute. Ok, that's quite unclear. Here is sample of my custom control :
Here is my custom control basic definition :
[ContentProperty("SmarSearchScopes ")]
public class SmartSearchCc : Control
{
List<SmartSearchScope> SmarSearchScopes {get;set;}
(more code here)
}
Here is the basic definition of a SmartSearchScope object :
public class SmartSearchScope : DependencyObject
{
public static readonly DependencyProperty ViewProperty =DependencyProperty.Register("View", typeof (ICollectionView), typeof (SmartSearchScope),new UIPropertyMetadata(null,OnViewChanged));
public static readonly DependencyProperty FilterColumnsProperty =DependencyProperty.Register("FilterColumns", typeof (IEnumerable<ColumnBase>), typeof (SmartSearchScope),new UIPropertyMetadata(null, OnFilterColumnsChanged));
public ICollectionView View
{
get { return (ICollectionView) GetValue(ViewProperty); }
set { SetValue(ViewProperty, value); }
}
public IEnumerable<ColumnBase> FilterColumns
{
get { return (IEnumerable<ColumnBase>) GetValue(FilterColumnsProperty); }
set { SetValue(FilterColumnsProperty, value); }
}
(more code here)
}
All that for what ? Being able to pass a collection of SmartSearchScope objects via XAML like so :
<SmartSearch:SmartSearchCc HorizontalAlignment="Stretch" Grid.Row="0" >
<SmartSearch:SmartSearchScope FilterColumns="{Binding ElementName=CcyPairsConfigBlotter, Path=Columns}" View ="{Binding ElementName=CcyPairsConfigBlotter, Path=ItemsSource}"/>
<SmartSearch:SmartSearchScope FilterColumns="{Binding ElementName=ClientConfigBlotter, Path=Columns}" View ="{Binding ElementName=ClientConfigBlotter, Path=ItemsSource}"/>
</SmartSearch:SmartSearchCc>
'ClientConfigBlotter' and 'CcyPairsConfigBlotter' are just two ItemsControls which expose a 'Columns' and an 'ItemSource' d-property.
The problem here is that althought my 2 SmartSearchScope objects gets instantiated, the databinding on the "View" and "FilterColumns" d-properties is not made and I never go througth the the associated callbacks.
In addition, here is the output error message I got when creating the custom control.
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Columns; DataItem=null; target element is 'SmartSearchScope' (HashCode=56862858); target property is 'FilterColumns' (type 'IEnumerable`1')
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ItemsSource; DataItem=null; target element is 'SmartSearchScope' (HashCode=56862858); target property is 'View' (type 'ICollectionView')
This is obvious that I'm missing something but I can't find what.
I must say that, in a previous version of that control, these 2 problematic d-properties where SmartSearchCc properties and that all worked just fine.
Thanks for your help :)
--bruno

I had a similar problem here: Bindings on child dependency object of usercontrol not working
The reason the binding doesn't work is because DependencyObjects don't have a DataContext property. In my case I changed them to inherit from FrameworkElement which solved the problem.
Although as someone else has mentioned, changing the parent control to an ItemsControl could simplify things.

Ok, problem solved, I swithc inheritance of my main custom control from control to ItemsControl and inheritance of my child object to FrameWork element and that's it. no need to further modifications.
Thank you all for your suggestions !

Related

Error binding a dependency property of a user control to a property on the parent's view model

I have a user control that contains three checkboxes and three date pickers. For example, one of the date pickers on the user control looks like this (irrelevant properties like Width, etc removed for clarity)...
<telerik:RadDatePicker DisplayFormat="Long"
SelectedValue="{Binding DepositPaidDate, Mode=TwoWay}"/>
The view model for the control has a public property called PaidDate that is of type PaidDate (yup, the property and the class have the same name), the top-level Grid on the control has its DataContext set to the PaidDate property, and the individual controls in the Grid are bound to properties on this PaidDate object.
When this control is used on a window, and the window's code behind sets the PaidDate property on the control's VM explicitly, it all works fine. For example, I created a test window, whose constructor looked like this...
public PaidDateWindow(PaidDate paidDate, string windowTitle) {
InitializeComponent();
((PaidDateControlViewModel)PaidDateCtrl.DataContext).PaidDate = paidDate;
Title = windowTitle;
}
...and this worked just fine. I could show the window, and the data was displayed correctly.
The problem comes when I try to set this via a dependency property on the control. The dependency property in the user control's code behind looks like this...
public static readonly DependencyProperty PaidDateProperty = DependencyProperty.Register("PaidDate", typeof(PaidDate), typeof(PaidDateControl), new FrameworkPropertyMetadata(SetPaidDateStatic));
private static void SetPaidDateStatic(DependencyObject d, DependencyPropertyChangedEventArgs e) {
(d as PaidDateControl).SetPaidDate((PaidDate)e.NewValue);
}
private void SetPaidDate(PaidDate paidDate) {
if (DataContext != null) {
((PaidDateControlViewModel)DataContext).PaidDate = paidDate;
}
}
public PaidDate PaidDate {
get {
return (PaidDate)GetValue(PaidDateProperty);
}
set {
SetValue(PaidDateProperty, value);
}
}
As you can see, the dependency property just passes the PaidDate object through to the view model, which has the same effect as when I did this manually in the previous bit of code.
When I try to bind this dependency property to a property on the window's view model, I get a binding error. Here is the XAML in the parent window...
<vrtSystemsUserControls:PaidDateControl
PaidDate="{Binding Path=VRTSystem.PaidDate, Mode=TwoWay}" />
The parent window's VM contains a property called VrtSystem, and plenty of other controls on the window are bound to properties on that. VrtSystem also contains a property called PaidDate, and that is what I want to pass to the user control.
However, when I run this, I get the following binding error...
System.Windows.Data Error: 40 : BindingExpression path error:
'VRTSystem' property not found on 'object' ''PaidDateControlViewModel' (HashCode=18319327)'.
BindingExpression:Path=VRTSystem.PaidDate; DataItem='PaidDateControlViewModel' (HashCode=18319327);
target element is 'PaidDateControl' (Name=''); target property is 'PaidDate' (type 'PaidDate')
Now it looks to me as though WPF is passing the actual binding information through to the user control, instead of the PaidDate object, as the error says it is trying to find a VrtSystem property on the user control's VM. I have no idea why it would be doing that, as I thought the idea of the binding was to resolve the binding at the window level, and then send the results (ie the PaidDate object) in to the dependency property, where it would be sent to the VM.
I hope I've explained this clearly. Anyone able to see what's gone wrong?
Thanks for any help.
When your binding is being resolved, it is looking for the VRTSystem property on the DataContext of the control it is being applied to.
The 'DataContext' property is being inherited by child-controls so if you set a DataContext on a Window all of its children will have the same DataContext. If however one of the children itself has a different DataContext applied, all of its children will use that.
In your case, the Window has a DataContext, but so has the UserControl. So by default all bindings on the UserControl or it's chilren, will expect to find the VRTSystem property on the UserControls DataContext which is not what you want in this case.
So to explicitly target the DataContext of the Window, you have to tell the binding, by setting its RelativeSource property like this:
{Binding Path=DataContext.VRTSystem.PaidDate, Mode=TwoWay,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}

Expose an inner depedency property to the main user control

I am working in silverlight.
Made a new UserControl called TextBoxWithButton.
Now i want add a new property to my new control called TextBoxBackground.
I did this :
public partial class TextBoxWithButton : UserControl
{
public Brush TextBoxBackground
{
get{return textBox.Background;}
set{textBox.Background = value;}
}
}
This works fine, but when I try to animate this property I get an exception.
I think it's because TextBoxWithButton should be defined as a dependency property but I don't know exactly how to to this.
You need to turn this into a Dependency Property. For details on implementing a DP, see Custom Dependency Properties.
Once you have this setup as a Dependency Property, just bind your (inner) TextBox.Background to the "local" TextBoxBackground property (in xaml). You can then animate the UserControl's TextBoxBackground property as needed, and the "inner" property will change as well.

Silverlight UI element's 1-time binding to nested non-dependency property fails

I want a 1-time databind between a checkbox (IsChecked propety) in a childWindow and a nested member variable in it's DataContext object, such that only the initial IsChecked property is set by the member variable. The member variable (binding source) is not INotifyPropertyChanged or marked as a DependencyProperty- but I think that is ok b/c I only want it to be evaluated once- when it gets its initial value.
Binding (in testChildWindow.xaml):
<CheckBox Content="Show Username?" Name="cbShowUser" IsChecked="{Binding Path=User.showUser}"/>
Setting DataContext (in parent window code-behind):
testChildWindow dlgBox = new testChildWindow();
dlgBox.DataContext = (this.DataContext as IAssignDlgViewModel).AssignVM("defaultChildWindow");
dlgBox.Show();
Data Context/Member variable:
public class testChildWindowViewModel : IDlgViewUpdate
{
public User
...
}
public class User
{
public bool showUser;
public User()
{
showUser = true;
}
...
}
If I make the Vm's binding source property (showUser) a dependency property at the (non-nested) testChildWindowViewModel, then the binding works. But all other combinations seem to fail.
Why must it be a dependency (or INotifyPropertyChanged?) property for a 1-time binding?
Why can't I get it to work at a nested level?
Thanks!!!
Ah, looking at the Output window during the binding answered the question for me. The problem was that User was not a property. Changed it to an auto property and the binding works just right now.

SortDescription with custom attached property

In Xaml I can set a custom attached property using
local:TestClass.TestProperty="1"
An I can bind to a custom attached property using
{Binding Path=(Namespace:[OwnerType].[PropertyName])}
{Binding Path=(local:TestClass.TestProperty)}
But how do I specify the namespace when I need to use a custom attached property in a SortDescription?
I can bind to an attached property using
new SortDescription("(Grid.Row)", ListSortDirection.Descending)
but here I can't set a namespace anywhere...
Best Regards,
Jesper
You can't, for the same reason that {Binding Path=a:b.c} works but {Binding a:b.c} doesn't: The PropertyPath constructor has no namespace context.
Unfortunately in the case of SortDescription there isn't much you can do about it. You have to find a way to sort without using attached properties.
Normally I tell people that use of Tag is an indicator of bad coding, but in this case Tag may be your best option: You can create an object within Tag that has properties that return the actual attached properties you want.
In your PropertyChangedCallback, instantiate Tag to an instance of an inner class:
public class TestClass : DependencyObject
{
... TestProperty declaration ...
PropertyChangedCallback = (obj, e) =>
{
...
if(obj.Tag==null) obj.Tag = new PropertyProxy { Container = obj };
});
public class PropertyProxy
{
DependencyObject Container;
public SomeType TestProperty { get { return GetTestProperty(Container); } }
}
}
Now you can use the sub-property of Tag in your SortDescription:
<SortDescription PropertyName="Tag.TestProperty" />
If there is only a single property to be sorted, you can simply use the Tag for it.
The main problem with this is that using the Tag property will conflict with any other code that also tries to use the Tag. So you may want to look for some obscure DependencyProperty in the standard libraries that doesn't even apply to the objects in question and use that instead of Tag.

Dependency property in app.xaml.cs

I am new to WPF and the below question may look silly for many, please pardon me.
How can I create a dependency property in app.xaml.cs?
Actually, I tried to created it. The below code,
public static DependencyProperty TempProperty =
DependencyProperty.Register("Temp", typeof(string), typeof(App));
public string Temp
{
get { return (string)GetValue(TempProperty); }
set { SetValue(TempProperty, value); }
}
throws the below compile time errors:
The name 'GetValue' does not exist in the current context
The name 'SetValue' does not exist in the current context
Can anybody help me in this?
Thank you!
DependencyProperties can only be created on DependencyObjects, and since Application (which your App class inherits from) doesn't implement it, you can't create a DependencyProperty directly on the App class.
I assume you want this property to support binding. If this is the case, you have two options:
Implement INotifyPropertyChanged in App.xaml.cs
Create a DependencyObject derived class with your properties on it, and expose it as a standard read-only property of your App. The properties can then be successfully bound by "dotting-down" to them.
i.e if your new property is called Properties, you can bind like so:
<TextBlock Text="{Binding Properties.Temp}" />
If the property needs to be the target of a Binding, then option #2 is your best bet.
You class that contains dependency properties must inherit from DependencyObject.

Resources