WPF MVVM binding error - wpf

I'm using the MVVM pattern. In my ViewModel I have a public ObservableCollection:
public ObservableCollection<SettingsTemplateHistoryItemViewModel> HistoryItemCollection;
public SettingsTemplateViewModel()
{
this.HistoryItemCollection = new ObservableCollection<SettingsTemplateHistoryItemViewModel>();
}
In my View I have an ItemsControl with its ItemsSource property bound to the ObservableCollection in the ViewModel.
<ItemsControl ItemsSource="{Binding HistoryItemCollection}"/>
I'm getting the following error:
BindingExpression path error: 'HistoryItemCollection' property not found on 'object' ''SettingsTemplateViewModel' (HashCode=48413709)'. BindingExpression:Path=HistoryItemCollection; DataItem='SettingsTemplateViewModel' (HashCode=48413709); target element is 'ItemsControl' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
I'm baffled. I am 100% certain the property exists in the View's DataContext (i.e. the ViewModel). I've copied and pasted the property name into the View, so the binding path must be correct. The View and the ViewModel are wired up via implicit/typed DataTemplates. What am I missing?

like the binding error says, your datacontext for your itemscontrol does not contain a public property called HistoryItemCollection. an easy way to check the datacontext at runtime is using Snoop
EDIT: you have to use a property instead of a field.
public ObservableCollection<SettingsTemplateHistoryItemViewModel> HistoryItemCollection {get;set;}

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}}}

Binding a 'IsReadOnly' Dependency Property

I have tried to create a Dependency Property 'IsReadOnly' to automatically set all TextBoxes in my form to ReadOnly following certain events.
The property is set up in the code behind to my window with the textboxes and looks like:
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly",
typeof(bool),
typeof(MainWindow),
new PropertyMetadata());
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
Xaml code for textboxes is similar to this:
<TextBox Text="{numBind:NumericFormatBinding Path=BudgetStatement.OpExpTotalByFunction}"
IsReadOnly="{Binding Path=IsReadOnly,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window},
Mode=TwoWay}"
Name="txtOpExpByProgram" />
but it doesn't work. I can still edit values in the textbox. I'm getting the following output error:
System.Windows.Data Error: 40 : BindingExpression path error: 'IsReadOnly' property not found on 'object' ''ListCollectionView' (HashCode=54963679)'. BindingExpression:Path=IsReadOnly; DataItem='ListCollectionView' (HashCode=54963679); target element is 'TextBox' (Name=''); target property is 'IsReadOnly' (type 'Boolean')
I don't know enough wpf to correctly understand this error, but it seems to have something to do with the ListCollectionView - but I haven't tried to attach the property to a ListCollectionView so I'm stuck.
Googling suggests it might be due to the DataContext and dependency properties needing special treatment (http://stackoverflow.com/questions/8497841/dependency-property-and-binding-error), or maybe the PropertyMetaData should be a Framework (or UI)PropertyMetaData.
Can anyone point me in the right direction to find out what isn't working?
tia
alex
ps: the numbind thing just sets the stringformat in all the text boxes
After reading the comment, Change the owner type from MainWindow to BudgetMainWindow.
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly",
typeof(bool),
typeof(BudgetMainWindow),
new PropertyMetadata());

Custom control ContentProperty DataBinding

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 !

What is the DataContext of a DependencyObject in Silverlight4?

I have read that SL4 introduces the ability to data bind properties on objects that derive from DependencyObjects, where previously data-binding only worked on FrameworkElements or FrameworkContentElements.
However, I am not clear on how the binding source is determined when binding properties of DependencyObjects.
In the case of FrameworkElements, the element's DataContext property is the source object ('walking up the tree' to find a DataContext, if the DataContext isn't set directly).
In the case of DependencyObjects, I would guess that the DataContext used is the DataContext of the 'containing' FrameworkElement in the XAML file. But what is the mechanism for determining this containing object?
In my particular case, I am trying to bind the property of a DependencyObject that lives in an ObservableCollection that is a property of a FrameworkElement. Unfortunately attempting to bind the property on the DependencyObject fails, as the databinding system appears to be using the DependencyObject itself as its own DataContext. It complains (in the output window) that the type does not have a property with the name specified in the binding expression. Binding a dependency property of a FrameworkElement in the same UserControl with the same binding expression is successful.
Have you tried stating the Source or ElementName property when defining the Binding?
(e.g: {Binding Source={StaticResource theFrameworkElement} Path=theObservableCollection[0]}
or {Binding ElementName=theFrameworkElement Path=theObservableCollection[0]}

Synchronize DataGrid and DataForm in Silverlight 3

I've been banging my head against the wall for a couple of days on this and it's time to ask for help.
I've got a DataGrid and DataForm on the same UserControl. I'm using an MVVM approach so there is a single ViewModel for the UserControl. That ViewModel has a couple of properties that are relevant to this discussion:
public ObservableCollection<VehicleViewModel> Vehicles { get; private set; }
public VehicleViewModel SelectedVehicle
{
get { return selectedVehicle; }
private set
{
selectedVehicle = value;
OnPropertyChanged( "SelectedVehicle" );
}
}
In the XAML I've got the DataGrid and DataForm defined as follows:
<data:DataGrid SelectionMode="Single"
ItemsSource="{Binding Vehicles}"
SelectedItem="{Binding SelectedVehicle, Mode=TwoWay}"
AutoGenerateColumns="False"
IsReadOnly="True">
<dataFormToolkit:DataForm CurrentItem="{Binding SelectedVehicle}" />
So as the SelectedItem changes on the DataGrid it should push that change back to the ViewModel and when the ViewModel raises the OnPropertyChanged the DataForm should refresh itself with the information for the newly-selected VehicleViewModel. However, the setter for SelectedVehicle is never being called and in the Output window of VS I'm seeing the following error:
System.Windows.Data Error: ConvertBack cannot convert value 'xxxx.ViewModel.VehicleViewModel' (type 'xxxx.ViewModel.VehicleViewModel'). BindingExpression: Path='SelectedVehicle' DataItem='xxxx.ViewModel.MainViewModel' (HashCode=31664161); target element is 'System.Windows.Controls.DataGrid' (Name=''); target property is 'SelectedItem' (type 'System.Object').. System.MethodAccessException: xxxx.ViewModel.MainViewModel.set_SelectedVehicle(xxxx.ViewModel.VehicleViewModel)
It sounds like it's having a problem converting from a VehicleViewModel to an object (or back again), but I'm confused as to why that would be (or even if I'm on the right track with that assumption). Each row/item in the DataGrid should be a VehicleViewModel (because the ItemsSource is bound to an ObservableCollection of that type), so when the SelectedItem changes it should be dealing with an instance of VehicleViewModel.
Any insight would be appreciated.
Your setter on the public VehicleViewModel SelectedVehicle is private so the DataGrid cannot modify it using the TwoWay mode. Your setter will need to be public.

Resources