I want to have access to parent DataContext in resource to use it in binding. Here is sample:
<Window x:Class="WpfApplication44.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication44"
x:Name="MyWindows"
Title="MainWindow"
Width="525"
Height="350"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<l:SomeResource x:Key="SomeResource">
<l:SomeResource.Context>
<!--
DataContext is set to windows object.
I want to bind to window`s title property
-->
<Binding Path="Title" />
</l:SomeResource.Context>
</l:SomeResource>
</Window.Resources>
<StackPanel>
<Label>
<StaticResource ResourceKey="SomeResource" />
</Label>
<!-- UPD -->
<TextBlock Text="{Binding Source={StaticResource SomeResource}, Path=Context}" />
</StackPanel>
But I get:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Title; DataItem=null; target element is 'SomeResource' (HashCode=25557385); target property is 'Context' (type 'Object')
SomeResource is derived from DependencyObject and contains only one dependency property Context of type object.
It looks like resources don't have access to parent's DataContext property and it is not set event if resource is of type FrameworkElement. I've tried to use ElementName, RelativeSource in my binding but with no luck.
All I need is to set parent's DataContext to resources. I'm using MVVM so any MVVM solutions are preferable.
UPD
Link to project is here
Oh it looks like a DataProxy than modify your SomeResource to Freezable like this:
public class SomeResource : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new SomeResource();
}
public static readonly DependencyProperty ContextProperty = DependencyProperty.Register("Context", typeof(object), typeof(SomeResource), new PropertyMetadata(default(object)));
public object Context
{
get { return (object)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
}
Related
I have inherited a class, MyModernWindow from Window, and added a property and dependency property called MyTitleLinks. The type is MyLinkCollection : ObservableCollection<MyLink>. In XAML, I'm trying to define the MyTitleLinks, and bind the MyLink.Command property to a property in my Window's ViewModel.
I have tried numerous ways to bind, including FindAncestor and ElementName, and I am constantly unsuccessful.
If using {Binding AboutCommand} or {Binding DataContext.AboutCommand, ElementName=mainWindow}, I get this error in the Output:
Cannot find governing FrameworkElement or FrameworkContentElement for target
element. BindingExpression:Path=AboutCommand; DataItem=null; target
element is 'MylLink' (HashCode=30245787); target property is 'Command'
(type 'ICommand')
If using {Binding DataContext.AboutCommand, RelativeSource={RelativeSource AncestorType={x:Type local:MyModernWindow}}},
Cannot find source for binding with reference 'RelativeSource
FindAncestor,
AncestorType='My.Namespace.MyModernWindow',
AncestorLevel='1''. BindingExpression:Path=DataContext.AboutCommand;
DataItem=null; target element is 'MyLink' (HashCode=35075009); target
property is 'Command' (type 'ICommand')
MainWindow.xaml
<local:MyModernWindow x:Class="My.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:My.Controls"
IsTitleVisible="True"
Style="{StaticResource MyModernWindow}"
Title="My Window"
WindowStartupLocation="CenterScreen">
<local:MyModernWindow.MyTitleLinks>
<local:MyLink DisplayName="Support" Source="https://www.google.com/support/" />
<local:MyLink DisplayName="About" Command="{Binding AboutCommand}" />
</local:MyModernWindow.MyTitleLinks>
</local:MyModernWindow>
MainWindow.xaml.cs
public partial class MainWindow : MyModernWindow
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
MyLinkCollection Class
public class MyLinkCollection : ObservableCollection<MyLink>
{
}
MyLink Class
public class MyLink : DependencyObject
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(nameof(Command), typeof(ICommand), typeof(MyLink));
public static readonly DependencyProperty DisplayNameProperty = DependencyProperty.Register(nameof(DisplayName), typeof(string), typeof(MyLink));
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source), typeof(Uri), typeof(MyLink));
public Uri Source
{
get { return (Uri)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public string DisplayName
{
get { return (string)GetValue(DisplayNameProperty); }
set { SetValue(DisplayNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public MyLink()
{
SetCurrentValue(VisibilityProperty, Visibility.Visible);
}
}
ViewModel
public class MainWindowViewModel
{
public ICommand AboutCommand { get; private set; }
public MainWindowViewModel()
{
this.AboutCommand = new RelayCommand(OpenAboutWindow);
}
private void OpenAboutWindow(object o)
{
ModernDialog.ShowMessage("About Screen", "About", MessageBoxButton.OK);
}
}
What am I missing?
With the help of this blog post, I figured it out. Since MyLink and MyLinkCollection aren't in the visual tree, I used a "Proxy Element" to give a context.
I gave my Window a name, created a FrameworkElement, then created a hidden ContentControl. That's all I needed.
Here's the working XAML:
<local:MyModernWindow x:Class="My.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:My.Controls"
x:Name="Window"
IsTitleVisible="True"
Style="{StaticResource MyModernWindow}"
Title="My Window"
WindowStartupLocation="CenterScreen">
<local:MyModernWindow.Resources>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding DataContext, ElementName=Window}" />
</local:MyModernWindow.Resources>
<ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"/>
<local:MyModernWindow.MyTitleLinks>
<local:MyLink DisplayName="Support" Source="{Binding DataContext.SupportSource, Source={StaticResource ProxyElement}}" />
<local:MyLink DisplayName="About" Command="{Binding DataContext.AboutCommand, Source={StaticResource ProxyElement}}" />
</local:MyModernWindow.MyTitleLinks>
</local:MyModernWindow>
The reason for the problem is that the DataContext is not inherited from the collection nor from the MyLink item.
To have WPF automatically managing the inheritance for you without the need of a proxy element you need to add "Freezable" at each step of your tree as follows:
public class MyLinkCollection : FreezableCollection<MyLink>
{
}
and
public class MyLink : Freezable
{
// class body
}
Xaml Behaviors Wpf(a Microsoft released project) uses the same approach to propagate the DataContext inside a Xaml defined collection without the need of additional proxies
I often have the following scenario:
I have a custom UserControl:
<UserControl x:Class="BindingBindingBindingTest.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBox Text="{Binding MyUserControlValue}"></TextBox>
</Grid>
</UserControl>
with its code behind:
public partial class MyUserControl
{
public MyUserControl()
{
InitializeComponent();
}
public int MyUserControlValue
{
get { return (int)GetValue(MyUserControlValueProperty); }
set { SetValue(MyUserControlValueProperty, value); }
}
public static readonly DependencyProperty MyUserControlValueProperty =
DependencyProperty.Register("MyUserControlValue", typeof(int), typeof(MyUserControl), new PropertyMetadata(0));
}
This usercontrol I'm using in a other control or window:
<Window x:Class="BindingBindingBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:BindingBindingBindingTest">
<Grid>
<controls:MyUserControl MyUserControlValue="{Binding MainWindowValue}" />
</Grid>
</Window>
In its code behind there is a property which should be passed to the usercontrol. This property is initialized in its constructor:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
MainWindowValue = 34;
DataContext = this;
}
public int MainWindowValue { get; set; }
}
Running the application, the usercontrol binding works well.
But the binding defined in the MainWindow.xaml doesnt work. The error messing is following:
BindingExpression path error: 'MainWindowValue' property not found on 'object' ''MyUserControl' (Name='')'. BindingExpression:Path=MainWindowValue; DataItem='MyUserControl' (Name=''); target element is 'MyUserControl' (Name=''); target property is 'MyUserControlValue' (type 'Int32')
As described in the error message, the property MainWindowValue is not fount on MyUserControl.
And here is my question: Why the property is expected on MyUserControl? In my opinion, the binding is made to the MainWindowValue of the MainWindow because the DataContext is set to its own instance (DataContext = this).
Of course it's simple to fix this issue by defining the source of the binding. But I'm interesting about the reason of this behavior.
Any idea about this behavior?
Thanks in advance
The property is expected on MyUserControl because MyUserControl.xaml contains the following:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
This points the DataContext of the MyUserControl instance back to itself. This happens as part of the call to the MyUserControl() constructor, which happens before the MyUserControlValue setter is evaluated in MainWindow:
<controls:MyUserControl MyUserControlValue="{Binding MainWindowValue}" />
By the time this setter is evaluated, the DataContext of the MyUserControl has been set to itself. When the binding engine looks for the MainWindowValue property, it looks on the MyUserControl, because that's the data context. The binding evaluates against the data context of the target object, not the data context of the "parent" object.
A simple workaround would be to give the window a name, and use ElementName to make the binding use the window as its binding root:
<Window x:Class="BindingBindingBindingTest.MainWindow"
x:Name="Root"
...>
<Grid>
<controls:MyUserControl
MyUserControlValue="{Binding ElementName=Root, Path=MainWindowValue}" />
</Grid>
</Window>
Also, as a matter of style, I would recommend against initializing control properties in both the constructor and the Xaml. When possible, you should stick to initializing values in one place (preferably the Xaml). If you want to specify a default value, then specify it in the DependencyProperty metadata when you Register() the property. If the value you're specifying is specific to the control instance, then set the value where you declare the instance.
I have a user WPF UserControl which is just a grid with an Image in it and I'm bidning the Image to a ImageSource Dependency Property named Source.
<UserControl x:Class="ImageOnlyClient.MyImage"
x:Name="MyImageControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Name="MainGrid">
<Border Name="MyImageBorder" BorderThickness="2" BorderBrush="Orange">
<Image Name="MyImage" VerticalAlignment="Top" Opacity="1"
RenderOptions.BitmapScalingMode="NearestNeighbor"
Source="{Binding Path=Source, Mode=OneWay}" />
</Border>
</Grid>
In the UserControl codebehind my class is defined as follows:
public partial class MyImage : UserControl
{
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
#region Source DependencyProperty
public static readonly DependencyProperty SourceProperty;
private static void SourceProperty_PropertyChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
{
//To be called whenever the DP is changed.
System.Diagnostics.Debug.WriteLine("SourceProperty changed is fired");
}
private static bool SourceProperty_Validate(object Value)
{
//Custom validation block which takes in the value of DP
//Returns true / false based on success / failure of the validation
//MessageBox.Show(string.Format("DataValidation is Fired : Value {0}", Value));
return true;
}
private static object SourceProperty_CoerceValue(DependencyObject dobj, object Value)
{
//called whenever dependency property value is reevaluated. The return value is the
//latest value set to the dependency property
//MessageBox.Show(string.Format("CoerceValue is fired : Value {0}", Value));
return Value;
}
#endregion
static MyImage()
{
SourceProperty = DependencyProperty.Register("Source", typeof(ImageSource), typeof(MyImage),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(SourceProperty_PropertyChanged),
new CoerceValueCallback(SourceProperty_CoerceValue),
false, UpdateSourceTrigger.PropertyChanged),
new ValidateValueCallback(SourceProperty_Validate));
}
public MyImage()
{
InitializeComponent();
}
}
In a Window I try to use the Image as follows and Bind it's source property to a WritableBitmap (MyClient.ImageMgr.ImageSource) which I can successfully bind to a regular Image control.
<local:MyImage x:Name="imgPrimaryImage" Height="768" Width="1024" Grid.Column="1" Grid.RowSpan="2"
Source="{Binding Path=MyClient.ImageMgr.ImageSource}" />
Any help on what's going on here would be greatly appreciated. I'm getting the following binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'Source' property not found on 'object' ''ImageOnly' (Name='')'. BindingExpression:Path=Source; DataItem='ImageOnly' (Name=''); target element is 'Image' (Name='MyImage'); target property is 'Source' (type 'ImageSource')
You're attempting to bind the Image's "Source" to a property on the parent UserControl, but if you don't specify a source (I mean a binding source ... the terminology here is confusing), then the runtime will look for the property on the default data context. I would infer from the error message that a class of type "ImageOnly" is the inherited data context in your user control.
You probably just want to specify a relative source, like this:
<Image ...
Source="{Binding
RelativeSource={RelativeSource AncestorType=UserControl},
Path=Source,
Mode=OneWay}"
/>
I finally got it to work #McGarnagle's suggestion worked, but in the mean time I had added a DataContext=this in the UserControl's constructor which was messing up the DataContext of the UserControl
Im starting with WPF, sorry if i cant explain well, and i have hours trying to solve how to bind a collection to a custom grid is named PagingDataGrid.
The PagingDataGrid is in CustomerSearchControl binding GridItems to ItemsSource, when i excecute SearchCommand GridItems gets updated but nothing else changes.
I get the following error:
System.Windows.Data Error: 40 : BindingExpression path error: 'GridItems' property not found on 'object' ''PagingDataGridViewModel' (HashCode=54151655)'. BindingExpression:Path=GridItems; DataItem='PagingDataGridViewModel' (HashCode=54151655); target element is 'PagingDataGrid' (Name='Me'); target property is 'ItemsSource' (type 'IEnumerable')
CustomerSearchControl:
<UserControl x:Class="Namespace.CustomerSearchControl"
... >
<Control.DataContext>
<Binding Path="CustomerSearchViewModel" ... />
</Control.DataContext>
<DockPanel LastChildFill="True">
<GroupBox Header="Registros">
<controls:PagingDataGrid ItemsSource="{Binding GridItems}" Height="300" />
</GroupBox>
</DockPanel>
</UserControl>
public class CustomerSearchViewModel : ViewModelBase
{
public ObservableCollection<GridItem> GridItems{ get; set; }
public ICommand SearchCommand { get; set; }
public CustomerSearchViewModel()
{
GridItems = new ObservableCollection<GridItem>();
SearchCommand = new RelayCommand(SearchEntities, () => true);
}
}
PagingDataGrid:
<UserControl x:Class="Namespace.PagingDataGrid" x:Name="Me"
... >
<UserControl.DataContext>
<Binding Path="PagingDataGridViewModel" ... />
</UserControl.DataContext>
<Grid>
...
<xcdg:DataGridControl
ItemsSource="{Binding ElementName=Me, Path=ItemsSource}" Grid.Row="0"/>
</Grid>
</UserControl>
public partial class PagingDataGrid : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty
= DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(PagingDataGrid),
new PropertyMetadata(default(IEnumerable)));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
}
You need to declare instance of CustomerSearchViewModel in XAML and bind to DataContext.
This is how to do it:
<UserControl.DataContext>
<local:CustomerSearchViewModel/>
</UserControl.DataContext>
Make sure to declare namespace local at root i.e. at UserControl:
xmlns:local="clr-namespace:WpfApplication" <-- Replace WpfApplication with
actual namespace of your ViewModel.
Not needed since getting instance from ServiceLocator.
And for binding to GridItems you need to bind explicitly to CustomerSearchControl DataContext using RelativeSource. This is needed because you have explicitly set DataContext on PagingDataGrid to PagingDataGridViewModel. So, it will search for GridItems property in PagingDataGridViewModel instead of CustomerSearchViewModel.
<controls:PagingDataGrid ItemsSource="{Binding DataContext.GridItems,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl}}"/>
Or you can give x:Name to CustomerSearchControl and bind using ElementName.
never set the datacontext of your usercontrol to self.
so simply remove
<UserControl.DataContext>
<Binding Path="PagingDataGridViewModel" ... />
</UserControl.DataContext>
EDIT:
if you not remove this, then your
<UserControl x:Class="Namespace.PagingDataGrid" x:Name="Me">
<UserControl.DataContext>
<Binding Path="PagingDataGridViewModel" ... />
</UserControl.DataContext>
<Grid>
<xcdg:DataGridControl ItemsSource="{Binding ElementName=Me, Path=ItemsSource}" Grid.Row="0"/>
</Grid>
</UserControl>
datacontext for your usercontrol is PagingDataGridViewModel and if PagingDataGridViewModel dont have a property ItemsSource you get an error. you never get the GridItems from your CustomerSearchViewModel that you want.
In my main window, I try to bind to a bool, but it's looking in my custom control's DataContext instead. If I don't assign DataContext in the user control, then the main window's bindings works, but (obviously) this brakes the bindings in the user control.
Here's the error:
System.Windows.Data Error: 40 : BindingExpression path error: 'MyControlVisible' property not found on 'object' ''MyUserControlModel' (HashCode=1453241)'. BindingExpression:Path=MyControlVisible; DataItem='MyUserControlModel' (HashCode=1453241); target element is 'MyUserControl' (Name='_myUserControl'); target property is 'Visibility' (type 'Visibility')
I need binding to work on both controls, but I don't want the user control's DataContext to supersede the window's.
Here's the code:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:Sandbox.Controls" Title="Sandbox">
<DockPanel LastChildFill="True">
<DockPanel.Resources>
<BooleanToVisibilityConverter x:Key="boolToVis" />
</DockPanel.Resources>
<Grid>
<Controls:MyUserControl x:Name="_myUserControl" Visibility="{Binding MyControlVisible, Converter={StaticResource boolToVis}}"/>
</Grid>
</DockPanel>
</Window>
namespace Sandbox
{
public partial class MainWindow
{
private MainWindowModel model;
public MainWindow()
{
InitializeComponent();
DataContext = model = new MainWindowModel();
_myUserControl.Initialize(model.MyUControlModel);
}
}
}
using System.ComponentModel;
using Sandbox.Controls;
namespace Sandbox
{
public class MainWindowModel : BaseModel
{
public MyUserControlModel MyUControlModel { get; set; }
public bool MyControlVisible { get; set; }
public MainWindowModel()
{
MyUControlModel = new MyUserControlModel();
MyControlVisible = false;
OnChange("");
}
}
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnChange(string s)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(s));
}
}
}
}
<UserControl x:Class="Sandbox.Controls.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<Grid>
<TextBlock Text="{Binding MyBoundText}"/>
</Grid>
</UserControl>
namespace Sandbox.Controls
{
public partial class MyUserControl
{
public MyUserControl()
{
InitializeComponent();
}
public void Initialize(MyUserControlModel context)
{
DataContext = context;
}
}
}
namespace Sandbox.Controls
{
public class MyUserControlModel : BaseModel
{
public string MyBoundText { get; set; }
public MyUserControlModel()
{
MyBoundText = "Hello World!";
OnChange("");
}
}
}
That is one of the many reasons you should never set the DataContext directly from the UserControl itself.
When you do so, you can no longer use any other DataContext with it because the UserControl's DataContext is hardcoded in.
In the case of your binding, normally the DataContext would be inherited so the Visibility binding could find the property MyControlVisible on the current DataContext, however because you hardcoded the DataContext in your UserControl's constructor, that property is not found.
You could specify a different binding source in your binding, such as
<Controls:MyUserControl Visibility="{Binding
RelativeSource={RelativeSource AncestorType={x:Type Window}},
Path=DataContext.MyControlVisible,
Converter={StaticResource boolToVis}}" ... />
However that's just a workaround for the problem for this specific case, and in my view is not a permanent solution. A better solution is to simply not hardcode the DataContext in your UserControl
There are a few different ways you can do depending on your UserControl's purpose and how your application is designed.
You could create a DependencyProperty on your UserControl to pass in the value, and bind to that.
<Controls:MyUserControl UcModel="{Binding MyUControlModelProperty}" ... />
and
<UserControl x:Class="Sandbox.Controls.MyUserControl"
ElementName=MyUserControl...>
<Grid DataContext="{Binding UCModel, ElementName=MyUserControl}">
<TextBlock Text="{Binding MyBoundText}"/>
</Grid>
</UserControl>
Or you could build your UserControl with the expectation that a specific property will get passed to it in the DataContext. This is normally what I do, in combination with DataTemplates.
<Controls:MyUserControl DataContext="{Binding MyUControlModelProperty}" ... />
and
<UserControl x:Class="Sandbox.Controls.MyUserControl"...>
<Grid>
<TextBlock Text="{Binding MyBoundText}"/>
</Grid>
</UserControl>
As I said above, I like to use DataTemplates to display my UserControls that expect a specific type of Model for their DataContext, so typically my XAML for the main window would look something like this:
<DataTemplate DataType="{x:Type local:MyUControlModel}">
<Controls:MyUserControl />
</DataTemplate>
<ContentPresenter Content="{Binding MyUControlModelProperty}" ... />