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.
Related
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
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}" />
I'm really new in WPF. I tried to set a default value for a control-property in code and want to overwrite the property by data binding, when the datacontext (VM) is available. But the databinding is not working in this case.
Example:
code behind:
public partial class MyViewControl : UserControl
{
public MyViewControl()
{
InitializeComponent();
// it works if I remove this line
panelControl.Visibility = Visibility.Hidden;
}
}
xaml usercontrol:
<DockPanel Name="panelControl" Visibility="{Binding
MyViewModelProperty_IsVisible_ConvertedToVisibility}">
xaml mainwindow:
<my:MyViewControl DataContext="{Binding ElementName=lbListBox,
Path=SelectedItem}"/>
Actually the FallbackValue parameter works for this scenario, but I want to know the technical reason, why the control property cannot be bound after it was set by code?
Xaml is processed during InitializeComponent(), so this is what is happening:
InitializeComponent(); // binding is set
panelControl.Visibility = Visibility.Hidden; // binding is removed (value is set)
You can restore binding
InitializeComponent();
panelControl.Visibility = Visibility.Hidden;
BindingOperations.SetBinding(panelControl, Control.VisibilityProperty,
new Binding()
{
Path = new PropertyPath(nameof(ViewModel.MyViewModelProperty_IsVisible_ConvertedToVisibility)),
Source = viewModelInstance, // this.DataContext ?
});
And it will work after. But it's not really clear why do you want to overwrite binding in first place.
A simple way to prevent a binding being cleared when changing bound property value in code, is to use TwoWay binding mode:
<DockPanel Name="panelControl"
Visibility="{Binding MyViewModelProperty_IsVisible_ConvertedToVisibility,
Mode=TwoWay}">
I found this out the hard way, of course, lol.
Actually, using TwoWay mode does make sense if you have a reason for modifying a control's property directly (as opposed to modifying the bound property) - you would then want the bound property to reflect the change too.
By the way, instead of binding to a property of type Visibility it is better to use bind to a boolean and use a converter like BooleanToVisibilityConverter as it decouples ViewModel from View even better:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
...
<DockPanel Name="panelControl"
Visibility="{Binding MyViewModelProperty_IsVisible,
Converter={StaticResource BooleanToVisibilityConverter},
Mode=TwoWay}">
Yeah, I know this is an old question and has an accepted answer but I found no other answer offering this solution directly.
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.
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();