Search code examples
c#wpfbindinggriduser-controls

How to bind grid content to a UserControl


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;

    }
}

Solution

  • 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 UserControls 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>