I am trying to understand how the RelativeSource works.
With the setup below I'd expect to see the text "I am the MainViewModel" displayed on the form, however I see an error in the debugger and no text on the MainWindow:
Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='UnderstandingBindings.ViewModels.MainViewModel', AncestorLevel='1''. BindingExpression:Path=SomeProperty; DataItem=null; target element is 'TextBlock' (Name='myText'); target property is 'Text' (type 'String')
I have a ViewModel like this:
class MainViewModel
{
public string SomeProperty { get => "I am the MainViewModel"; }
private readonly ChildViewModel _child = new ChildViewModel();
public ChildViewModel Child => _child;
}
class ChildViewModel
{
public string SomeProperty { get => "I am the ChildViewModel"; }
}
the MainWindow XAML looks like:
<Window x:Class="UnderstandingBindings.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UnderstandingBindings.Views"
xmlns:vm="clr-namespace:UnderstandingBindings.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel x:Name="pnlMain">
<TextBlock x:Name="myText" Text="{Binding SomeProperty, RelativeSource={RelativeSource AncestorType={x:Type vm:MainViewModel}}}"/>
</StackPanel>
</Window>
The data context is assigned like this:
public partial class MainWindow : Window
{
private readonly MainViewModel _viewModel = new MainViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = _viewModel.Child;
}
}
wpf looks for the Ancestors of the xaml element the binding is declared on. You can think of it as walking up the Visual Tree.
You'd use it to Bind to a property thats on that Ancestor or have to bind to a viewmodel by going through it's DataContext Property. So for example:
<TextBlock x:Name="myText" Text="{Binding DataContext.Child.SomeProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
That only works if the DataContext is set to the MainViewModel in your example tho. If you want the Binding to go the direction you expected then you need to add a reference to the Parent Viewmodel on the Child.
The declaration of a relative source within a binding does the following.
Gets or sets the binding source by specifying its location relative to the position of the binding target.
This means it sets the Source property of the binding to an element in the visual tree. This can be the current element (Self) or an ancestor (e.g. StackPanel is an ancestor of the TextBlock it contains) or a templated parent in case of control templates. This depends on the Mode that you set. A relative source allows you to specify a property path on that element in a binding, like its DataContext or Tag or any other property.
The error that you get translates to: I searched for an instance of type MainViewModel in the visual tree starting from TextBlock. Then I checked the next ancestor StackPanel, which is not a MainViewModel. Then I checked the next ancestor Window, which is also not a MainViewModel. There is no other ancestor, so I could not find anything.
You are misusing relative source here. View models are not part of the visual tree, but act as data context for elements. For your example using the data context the right way is enough. Once you set the data context on MainWindow, it is inherited in all child controls, if not specified otherwise, e.g. explicitly assigning a different data context on an element. Consequently, the TextBlock, which is a child of MainWindow will get the same data context that you assigned to the DataContext property of MainWindow.
The data context in your example is an instance of ChildViewModel, so in order to bind to its SomeProperty, you do not need a relative source binding, just a property path which is automatically resolved using the DataContext (which is set as the binding source) of the corresponding control.
<TextBlock x:Name="myText" Text="{Binding SomeProperty}"/>
This will lead to the following text: I am the ChildViewModel
If you want to bind to the SomeProperty of MainViewModel, you should set the DataContext accordingly.
public MainWindow()
{
InitializeComponent();
this.DataContext = _viewModel;
}
The binding in TextBlock is the same as above, if you want to display the SomeProperty of MainViewModel.
<TextBlock x:Name="myText" Text="{Binding SomeProperty}"/>
If you want to bind the SomeProperty of the ChildViewModel instead, you can change the path.
<TextBlock x:Name="myText" Text="{Binding Child.SomeProperty}"/>
In both example, it will lead to the following text: I am the MainViewModel
Related
I want to set the datacontext of a TextBlock at XAML.
Resharper suggested this
<TextBlock Text="{Binding name}" Margin="50" d:DataContext="{d:DesignData Class1}" />
but when I run the application for some reason TextBlock has as its datacontext the class that I specified at View's code-behind and displays a wrong text
Views's code-behind
public partial class ViewA : UserControl {
public ViewA() {
InitializeComponent();
VmB vm = new VmB();
DataContext = vm;
}
}
So i want to display Class1 name property instead of VmB name property.
d:DataContext sets the DataContext for the visual designer only. If you want to bind to your class you need to set your actual DataContext to an instance of Class1. Where you get that instance from and how you set it is up to you.
Yes you just need to drop the Design parts from your xaml, so something like this ...
<TextBlock Text="{Binding name}" Margin="50" DataContext="{Binding Class1}" />
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?
I like to bind a custom property (owner Window) to my datacontext. How to do these in xaml.
I can not access on these property because my class is window and not MyView, its Window <Window x:Class="MyNamespace.MyView"
By changing my xaml to MyView class i get some errors that my class needs a inheritance from window.
Codebehind:
DependencyProperty MyValueProperty, Property MyValue
Xaml:
Bind MyValue to my datacontext.
I want these
If i try to change the class name inside xaml:
I get these error
you can instantiate a control that is
<local:MyView xmlns:local="YourNameSpaceToMyView" this way you can use your DP
You can set your DataContext to the current instance of your window class like this:
<Window x:Class="MyNamespace.MyView"
DataContext="{Binding RelativeSource={RelativeSource self}}">
Then you can access the underlying properties easily.
To bind to your custom property, you can do something like this:
DataContext="{Binding RelativeSource={RelativeSource self}, Path=MyProperty}"
I have a private field
private static Double myValue;
in the application MainWindow class. And there (in the MainWindow class) I defined a property
public static Double MytValue
{
get { return myValue; }
}
In the structure of the MainWindow class I have a TextBox. I'm in need of binding it to the MytValue property. In XAML I write:
<TextBox Name="tbxMyValue" Grid.Row="1" Grid.Column="2" TextAlignment="Center"
Text="{Binding Path=MyValue}" Width="Auto" Margin="10,0,10,15" IsEnabled="True" />
But it has no effect. I see nothing in the TextBox while myValue variable has a value. Why? Please help me.
I like to set the DataContext in the Window section
<Window x:Class="Gabe3a.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Gabe3a"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:System="clr-namespace:System;assembly=mscorlib"
DataContext="{Binding RelativeSource={RelativeSource self}}"
Title="Gabriel Main Ver 3a01" Icon="faviconw.ico" Height="600" Width="800">
You need to set the DataContext behind the Window for that binding to work
There are two layers to an application with WPF: the UI layer and the data layer.
The Data layer for an application starts out as null, and you can set it using the DataContext property.
Whenever you do a basic binding in WPF, you are binding to the DataContext. So Text="{Binding Path=MyValue}" is actually saying "Get the MyValue property from the current DataContext".
You could simply set the DataContext in your code behind:
MyWindow.DataContext = this;
Or you can use a RelativeSource binding to tell WPF to get the MyValue property from somewhere else, such as telling it to get it from the closest Window it finds when navigating up the VisualTree:
Text="{Binding Path=MyValue, RelativeSource={
RelativeSource AncestorType={x:Type Window}}}"
I actually have an article on my blog about the DataContext that I'd recommend reading if you're new to WPF and the DataContext: What is this "DataContext" you speak of?
It's not how it works. Binding to static properties; {Binding Source={x:Static local:Application.MyValue}}
Note that your field needs to be property & public. If you want to go with your solution, you need to set DataContext as {RelativeSource Self}.
In file MainWindow.xaml.cs in constructor add this line:
DataContext = this;
after InitializeComponent();
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.