Strange BindingExpression path error with ContentPresenter - c#

In my program I have a mainWindow that contains a contentPresenter. The mainWindow has a ViewModel called MainWindowViewModel. This viewModel contains properties that are bound to items in the mainWindow.
The Content of my contentPresenter is represented by a UserControl->ViewModel->Model structure.
This is the xaml of the contentPresenter that I am working with.
MainWindow.xaml:
<ContentPresenter Content="{Binding LeftWidget}" IsEnabled="{Binding LeftWidgetEnabled}" ... />
LeftWidget and LeftWidgetEnabled are both properties located in MainWindowViewModel. However, the BindingExpression path error that I receive has to do with LeftWidgetEnabled. For some reason my program is looking for the property in the ViewModel of the contentPresenter's UserControl. This doesn't make much sense to me, because the program deals with the LeftWidget property correctly.
Both properties are located in MainWindowViewModel, so why would the program be looking elsewhere for the LeftWidgetEnabled property? How can I fix this?
Also Note: The way that I set the DataContext of my UserControls are like so...
Under <Window.Resources... in mainWindow.xaml:
<DataTemplate DataType="{x:Type project:LeftWidgetViewModel}">
<local:LeftWidgetUserControl/>
</DataTemplate>

chnage the binding path to (this assumes main window is in fact a window object):
IsEnabled={Binding DataContext.LeftWidgetEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}
does that help? If so then you need to examine the datacontext of your objects as there might be something else going on
also, does the datatype of LeftWidgetEnabled match what is expected by the IsEnabled Property, ie boolean to boolean?

if LeftWidget and LeftWidgetEnabled are in the same datacontext and if LeftWidget is working. then i would say you should check your property in your viemodel wether its really LeftWidgetEnabled.
the xaml looks good for me.
EDIT: LeftWidgetEnabled should be typeof bool not bool?

Related

Call A Method From A Control That Is Not In The DataContext

I have a button in a UserControl that I want to call a method in another class (which happens to be my main window's view model).
<ToggleButton cal:Message.Attach="[Event Click] = [Action ToggleWatch]">
The user control's DataContext is tied to a DataTemplate. This makes everything beautiful, unless I run into cases like this where implementing the ToggleWatch method in the DataTemplate class doesn't make much sense, since the DataTemplate should only contain data.
Is the best way to get around this is by setting the DataContext of this control to the MainWindowViewModel? That workaround fails when I want to bind a value from the DataTemplate to the same button, since the DataContext modification will then make it look for the value in the MainWindowViewModel.
In case I've over complicated the question, I will summarise. I have a UserControl whose DataContext is a seperate DataTemplate. I want to call a method from a button when it is clicked, but I want the method seperated from the DataTemplate. I want the method in a completely different class. What is the best way to solve this?
Here is how I'm setting the DataTemplate:
<WrapPanel>
<ItemsControl ItemsSource="{Binding Devices}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:DeviceCleanerBox/>
...
UserControl must exist as child of your MainWindow and if i am right you must have set DataContext of MainWindow to be MainWindowViewModel.
So, what you can do is declare ICommand in MainWindowViewModel and bind to button's Command using RelativeSource to search for window's DataContext:
<ToggleButton Command="{Binding DataContext.CommandName,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window}}"/>

How do I avoid binding to 2 datacontext

My application looks like the following
The black is my MainWindow, the red is a tab control and the yellow is a UserControl.
The UserControl has many Dependency Properties defined and they bind to the DataContext (Which is set in the MainWindow's code behind, using this.DataContext = this).
To bind my UserControl to the same DataContext as my MainWindow, in my UserControl xaml I have the following
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType=Window}}"
This works great, and when I interact with my UserControl, due to the two way binding, it updates the Properties of my MainWindow, which in turn updates my TabControl!
The issue is, my UserControl now has some extra functionality and as such, needs to bind to the UserControl's code behind (such as values for the GUI).
This is where I'm stuck. I can't bind from my UserControl to my code behind because I've already created a DataContext.
I know I could use the WinForms approach, and name each control with x:Name="MyControl" like
MyControl.Text = "This value";
or
MyControl.DataContext = this;
Yeuk I think!!
My question is, is this the only way to go, or can I still use binding.
First of all you don't need to manually set DataContext on UserControl. DataContext is an inheritable property so it will inherit DataContext from its parent unless you have explicitly set it.
Get rid of DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType=Window}}" from your UserControl.
And now, in case you want to bind to code behind for some controls in your UserControl, you can bind using RelativeSource or can set DataContext on control:
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl}}"
If controls can be clubbed together under one panel, set DataContext on parent panel say Grid and child controls will inherit from it:
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl}}">
..Child Controls here will inherit DataContext
</Grid>
And to answer your question:
MyControl.DataContext = this; <-- Is this possible
Yes, it is possible like i mentioned above.
You can just use another RelativeSource Binding as you have for the MainWindow properties... to access the properties defined in the UserControl, try the following XAML in your UserControl:
<TextBlock Text="{Binding UserControlProperty, RelativeSource={RelativeSource
AncestorType={x:Type YourXmlNamespacePrefix:YourUserControl}}}" />
Obviously, you'll need to update YourXmlNamespacePrefix:YourUserControl to a valid XML Namespace and control type to get this to work.
I'm not saying either that you should set the DataContext anywhere, or change any properties. This is a RelativeSource Binding... you do not need to set any DataContext to make it work. I thought you would have known that seeing as you're already using one. Just try the example out.

How to set DataContext to self

My UserControl requires binding to the ancestor (the ancestor being the MainWindow) and to itself (it's code behind).
To bind to the ancestor, I'm using
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType=Window}}">
To bind a control to the code behind (and thus using the 'local' DataContext), I'm using
<TextBlock Text ="{Binding MyUC3Property}" Name="MyName" />
and in the code behind, setting it like
this.MyName.DataContext = this;
The above works fine, where I can bind to the codebehind and to the ancestor.
Now, I still want to bind to the code behind and the ancestor but set the DataContext in the XAML only (if possible).
I've tried
<TextBlock Text ="{Binding MyUC3Property}" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}" />
and ensured the constructor does not set the DataContext (since I want it all done in the XAML) - (although even if I do set this.DataContext = this; the error persists)
and the output window tells me there is a binding error.
System.Windows.Data Error: 40 : BindingExpression path error: 'MyUC3Property' property not found on 'object' ''TextBlock' (Name='')'. BindingExpression:Path=MyUC3Property; DataItem='TextBlock' (Name=''); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
I guess I'm missing something obvious, but I can't tell what.
You should be able to bind to the user control the same way as you do to the window:
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType=UserControl}}">
What you have tried was referring to the relative source Self from the TextBox. However, in that context, Self refers to the TextBox, not to the enclosing user control.
for usercontrols you should never set the datacontext to self. check to comment from H.B. from here
i use ElementName Binding
<UserControl x:Name="uc">
<TextBlock Text="{Binding ElementName=uc, Path=MyDependencyPropertyDefinedInMyUserControl}"/>
using the usercontrol:
<Window>
<MyUserControl MyDependencyPropertyDefinedInMyUserControl="{Binding Path=MyValueForTheTextBox}"/>
i try to explain it a little bit for your textbox case (ignor my poor english btw :))
if you want create a usercontrol with a textbox and this usercontrol/textbox should show the text from differrent viewmodels in different views - then you have a problem as far as the viewmodels have different propertynames. now the dependency property in your usercontrol come into the game. you create a DP where all your viewmodels can bind to and you bind your textbox within your usercontrol just to the DP from your usercontrol.
First thing is that you should probably push your parent DataContext to the lower levels. This will give you "God" ViewMode shared between all nested screens.
Second is that you should probably use something like MVVMLights Messanger to have cleaner separation.

Binding IsEnabled to the parent ViewModel instead of the UserControl ViewModel

I developed a user control in SilverLight that contains several child controls. Textboxes, ComboBoxes and so on.
The problem is, when I include that UserControl into a parent view and set the complete control to IsEnabled=False, the child controls in that specific UserControl are still enabled.
After all I found the problem.
Adding something like that, implies that the IsEnabled Binding is located in the UserControl binding, not as expected from myself in the DataContext of the parent.
<localControls:TeamEmployeeSelector Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
IsEnabled="{Binding CanModify}" DataContext="{Binding Confidentiality}"/>
QUESTION:
But there's still the question how I can bind the IsEnabled to the ViewModel of the Parent? Because it's not very elegant to copy the CanModify Property to the ViewModel of the Child Control.
Instead of modifying a binding in some way (for example you can make it dependent on other control name as it is proposed in other answer) I would move separate the control which will be disabled and control where DataContext will be changed. For example:
<ContentControl IsEnabled="{Binding CanModify}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<localControls:TeamEmployeeSelector DataContext="{Binding Confidentiality}"/>
</ContentControl>
Here is how I would do this.
Your TeamEmployeeSelector UserControl will contain a single root level element which by default is a Grid and is given the name "LayoutRoot".
Now you can bind the IsEnabled property of all the child elements to the UserControl like this:-
<TextBox IsEnabled="{Binding Parent.IsEnabled, ElementName=LayoutRoot}" ... />
By using element-to-element binding you do not need to copy the CanModify property in to child view models.
Some might suggest that you simply add an x:Name to your UserControl element and then bind directly to it rather than going via the Parent property of the root element as I do above. That'll work ok in Silverlight 4 but not in 3 or WP7. Personally I prefer the above.
This is a scoping issue. Generally, when creating a UserControl, you want to set itself as the DataContext for its sub-elements. This is most easily accomplished in the constructor:
UserControlExample() {
InitializeComponent();
RootElement.DataContext = this;
}
Where RootElement is the name you give to this first child (usually a Grid or panel) of your UserControl.
From here you can set natural bindings for your sub-elements like so:
<TextBox x:Name="MainTextBox" IsEnabled={Binding IsEnabled} />
This works, since TextBox inherits the DataContext of the parent layout panel.
Finally, if you want to have your UserControl's IsEnabled property to be related to its parent, this is best done at the point of declaration:
<Grid>
<UserControlExample IsEnabled={Binding CanModify} />
</Grid>
This way you keep your concerns separate. The sub-controls don't care what the UserControl is reflecting. They just need to know how to enable/disable when the control's IsEnabled property flips.
sub-controls IsEnabled bound to --> (UserControlExample is DataContext)
UserControlExample.IsEnabled bound to --> (VM is DataContext)
VM.CanModify
I don't know if it's possible in Silverlight, but in WPF I would use RelativeSource.
Have a look here.
Hope this help !
<localControls:TeamEmployeeSelector Grid.Row="1" Grid.Column="0"
Grid.ColumnSpan="2" IsEnabled="{Binding ElementName=SomeElementName_With_Parent_ViewModel, Path=DataContext.CanModify}" DataContext="{Binding Confidentiality}"/>

Binding not works on DependencyProperty

I have a window which has a usercontrol in it . This usercontrol's RequestObject property bound to SearchArgumentObject property of ViewModel of the window.
This is listing from my window class
<Grid DataContext="{Binding SearchArgumentObject, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<guiLib:RegCardSearchForm x:Name="SearchParametrsUC" Grid.Row="1" RequestObject="{Binding .,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
In Usercontrol class I created dependency property:
This is listing from my userControl class
public static DependencyProperty RequestObjectProperty = DependencyProperty.Register("RequestObject", typeof(RegistrationCardSearch), typeof(RegCardSearchForm));
public RegistrationCardSearch RequestObject
{
get
{
return (RegistrationCardSearch)GetValue(RequestObjectProperty);
}
set
{
SetValue(RequestObjectProperty, value);
}
}
On the level of the usecontrol everything works fine and RequestOject property changed.
But in my window class I can't see modification of SearchArgumentObject property which was made in usercontrol.
How can I get modefied property value? I think answer to this question is very trivial but I can't find solution.
Setting the DataContext on the Grid isn't doing anything but breaking the two-way linking of your properties. Skip the extra step and bind the VM property to the control property that you want to pick up changes from instead:
<Grid>
<guiLib:RegCardSearchForm x:Name="SearchParametrsUC" Grid.Row="1"
RequestObject="{Binding SearchArgumentObject, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
The code for your Window class is setting the DataContext of the Grid to a property obtained from a binding to a property on another object's DataContext further up the tree. Do you have the Window's DataContext set elsewhere?
Let's say that the object which is supplying the SearchArgumentObject is named SearchWindowViewModel. In the code-behind of the Window, you would have the following code (in the constructor, for example):
DataContext = new SearchWindowViewModel();
Now, all the properties that SearchWindowViewModel exposes are available to the Window. To bind the SearchWindowViewModel.SearchArgumentObject to the UserControl's RequestObject property, you would have the following XAML:
<Grid>
<guiLib:RegCardSearchForm x:Name=SearchParametersUC Grid.Row=1
RequestObject={Binding SearchArgumentObject />
</Grid>
If you don't want to set the Window's DataContext, you can set the Grid's DataContext using the same type of code as I used above, and the binding in the XAML would remain the same.
Hope that helps.

Categories

Resources