I have a Grid and want to show user control as child of the grid or content of the grid. When a button is clicked, a few user controls will be shown dependent on the cases. Please check the xaml part and code behind.
<Grid x:Name="ContentPanel" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="5,5,5,5">
</Grid>
I want to bind Grid content to the below activeUserControl object.
public class MainVM
{
public UserControl activeUserControl;
Stability stability;
Tank tank;
public MainVM()
{
stability = new Stability();
tank = new Tank();
activeUserControl = stability;
stability.Visibility = Visibility.Visible;
}
}
The problem is that you cannot directly bind to the Children
collection of a Grid
, because it is not a DependencyProperty
. You would have to implement attached properties or a behavior to do so. However, you can put a ContentControl
into your Grid
or replace it as a workaround. Then bind its Content
to the activeUserControl
property in your view model. Usually properties start with a capital letter, so I adapted it.
<Grid x:Name="ContentPanel" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,5,5,5">
<ContentControl Content="{Binding ActiveUserControl}"/>
</Grid>
Make sure that your MainVM
is set as DataContext
in any of the parent controls, otherwise this binding will not work. activeUserControl
must be a property to make it bindable. Implement INotifyPropertyChanged
in your MainVM
, so that the ContentControl
gets notified when the property changes and adapts its Content
.
// Implement "INotifyPropertyChanged" so controls get notified about property changes
public class MainVM : INotifyPropertyChanged
{
// Backing field of the "ActiveUserControl" property
private UserControl _activeUserControl;
public UserControl ActiveUserControl
{
get => _activeUserControl;
set
{
// Only set the value if it has changed
if (_activeUserControl != value)
{
_activeUserControl = value;
// Signal to the control that it needs to update the value
OnPropertyChanged(nameof(ActiveUserControl));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MainVM()
{
// ...your code.
ActiveUserControl = stability;
// ...your code.
}
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This should solve your problem, but your code is still a mix of MVVM and code-behind. Ideally, you should not have any UI control references in a view model.
Consider creating Tank
and Stability
view models using INotifyPropertyChanged
and data templates to display them, instead of UserControl
s in your view model. You can still use the ContentControl
in this case.
<ContentControl Content="{Binding activeUserControl}">
<ContentControl.Resources>
<DataTemplate DataType={x:Type TankViewModel}>
<!-- ...data template equivalent of your "Tank" user control. -->
</DataTemplate>
<DataTemplate DataType={x:Type StabilityViewModel}>
<!-- ...data template equivalent of your "Stability" user control. -->
</DataTemplate>
</ContentControl.Resources>
</ContentControl>