I have the object MyObject Object
in the class MainClass.xaml
.
I want to pass this object to a ViewModel
class of a nested user control called SubSubUserControl
through a SubUserControl
.
Here is the code in MainClass.xaml
:
<UserControl x:Class="Test.SubUserControl"
d:DataContext="{d:DesignInstance d:Type=local:SubUserControlViewModel}">
<Grid>
<local:SubSubUserControl Object="{Binding Object, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SubUserControl}}}"/>
</Grid>
</UserControl>
In SubUserControl
I have a classical DependencyProperty
in order to pass Object
to it.
public static readonly DependencyProperty ObjectProperty = DependencyProperty.Register(nameof(Object), typeof(Object), typeof(SubUserControl));
public Object Object
{
get => (Object)GetValue(ObjectProperty);
set => SetValue(ObjectProperty, value);
}
protected override void OnInitialized(EventArgs e)
{
InitializeComponent();
DataContext = new SubUserControlViewModel(Object);
base.OnInitialized(e);
}
SubUserControl
is only a "bridge", the object Object
is needed in SubSubUserControl
.
I pass to it with a binding (changing Datacontex beacause the standard DataContext for SubUserControl
is his ViewModel).
<local:SubSubUserControl Object="{Binding Object, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
and SubSubUserControl
has the classical DependencyProperty
.
SubSubUserControl
has a ViewModel as DataContext and I'd like to pass that object to it.
I try with
public static readonly DependencyProperty ObjectProperty = DependencyProperty.Register(nameof(Object), typeof(Object), typeof(SubSubUserControl));
public Object Object
{
get => (Object)GetValue(ObjectProperty);
set => SetValue(ObjectProperty, value);
}
protected override void OnInitialized(EventArgs e)
{
InitializeComponent();
DataContext = new SubSubUserControlViewModel(Object);
base.OnInitialized(e);
}
but Object
is null
.
The same code in SubUserControl
works.
Is there a way to pass an object using binding to a ViewModel class that belongs to a nested UserControl?
I saw a lot of similar questions but noone works with my specific case...
EDIT
The class Object
is instanced code-behind in MainClass.xaml.cs
. In order to pass it I set the DataContext of MainClass
to Self
.
public MyObject Object { get; set; } = new MyObject();
Here is how I set DataContext in MainClass.xaml
<Window x:Class="Test.MainWindow"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<local:SubUserControl Object="{Binding Object}"/>
</Grid>
</Window>
Setting the DataContext to the entire Window
avoid me to write something like ElementName
or Relative Source
.
P.s. Resharper do not warn me about any DataContext, each instance is seen correctly.
I don't know the method OnInitialized
, but from the documentation :
Raises the Initialized event.
And the event Initialized :
Whether you choose to handle Loaded or Initialized depends on your requirements. If you do not need to read element properties, intend to reset properties, and do not need any layout information, Initialized might be the better event to act upon. If you need all properties of the element to be available, and you will be setting properties that are likely to reset the layout, Loaded might be the better event to act upon.
I think the binding work, but you try to read the value in OnInitialized
that is called before the binding is resolved.
As suggested in the documentation, maybe you can use the event Loaded like :
public partial class SubSubUserControl : UserControl
{
public SubSubUserControl()
{
InitializeComponent();
Loaded += SubSubControl_Loaded;
}
private void SubSubControl_Loaded(object sender, RoutedEventArgs e)
{
DataContext = new SubSubViewModel(TargetFoo);
}
}
A alternative is to update the data context when the dependency property is modified :
public partial class SubSubUserControl : UserControl
{
public static readonly DependencyProperty ObjectProperty = DependencyProperty.Register(
"Object", typeof(MyObject),
typeof(SubSubUserControl),
new PropertyMetadata(OnObjectChanged)
);
private static void OnObjectChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if(sender is SubSubControl ssc)
{
ssc.DataContext = new SubSubViewModel((MyObject)e.NewValue);
}
}
}
EDIT from exchanges in comments.
It looks like you have a binding problem too. I suspect that a other element of type UserControl encapsulate the SubSubUserControl, like :
MainClass
SubUserControl
OtherUserControl
SubSubUserControl
In this case, the binding will use OtherUserControl as source (and not the expected SubUserControl
).
In this case, you can specify in the binding the source's type is SubUserControl
to avoid a other element of type UserControl is selected :
<local:SubSubUserControl Object="{Binding BridgeFoo, RelativeSource={RelativeSource AncestorType={x:Type local:SubUserControl}}}" />
A alternative is to use Binding.ElementName
instead of Binding.RelativeSource
:
<UserControl x:Class="ProjectNamespace.SubUserControl"
...
xmlns:local="clr-namespace:ProjectNamespace"
Name="Sub">
<local:SubSubUserControl Object="{Binding Object, ElementName=Sub}" />
</UserControl>
I prefer this, because I never remember the RelativeSource syntax.