Search code examples
wpfuser-controlsdbcontext

DataContext of usercontrol in WPF


I'm new to WPF and I'm trying to start a little project with a maximum of good practice. I'm using MVVM and dependency injection. I have a concern which seems to be easy to understand but i can't find an answer (at this step, DataContext is not very clear for me). The UserControlView of type UserControl contains just a button for testing.

This is the app class :

public App()
    {
        IServiceCollection services = new ServiceCollection();
        services.AddSingleton<MainWindow>();
        services.AddSingleton<UserControlViewModel>();
        services.AddSingleton<UserControlView>();

        _serviceProvider = services.BuildServiceProvider();
    }

The user control is included in the Main windows like that :

<Grid>
    <views:UserControlView/>
</Grid>

Now, in the OnStartup overrided method :

protected override void OnStartup(StartupEventArgs e)
    {
        MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
        MainWindow.DataContext = _serviceProvider.GetRequiredService<PaymentMeansViewModel>();
        MainWindow.Show();
    }

Like that it works, my button is correctly binded to the command.

But what is strange for me is that I have to set the 'UserControlViewModel' as the DataContext of the Main Window. Isn'it possible to bind it to the 'UserControlView', something like :

 protected override void OnStartup(StartupEventArgs e)
    {
        MainWindow = _serviceProvider.GetRequiredService<MainWindow>();

        UserControlView testUC = _serviceProvider.GetRequiredService<UserControlView>();
        testUC.DataContext = _serviceProvider.GetRequiredService<UserControlViewModel>();
        MainWindow.Show();
    }

Thanks for help.


Solution

  • Finally I did it. I think (I hope I'm right) that I understood.

    First of all, let's begin with the basic. A view must have a viewmodel to bind the properties. A usercontrol is a kind of view "encapsulated" in a view. Therefore a usercontrol must have its own viewmodel and the view must have its own viewmodel.

    The datacontext of the MainWindow is set in the app onstartup method :

    MainWindow = new MainWindow()
    {
        DataContext = new MainWindowViewModel()
    };
    

    MainWindow must implement INotifyPropertyChanged. All view models must implement this interface. We can create a base class which will be derived in the view models :

    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
        protected void OnPropertyChanged(string? propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    The DataContext of the usercontrol must be explicit in the xaml of the MainWindow:

    <Grid>
        <views:UserControlView DataContext="{Binding CurrentViewModel}"/>
    </Grid>
    

    "CurrentViewModel" is a DataContext, then it's a ViewModel, and as it is binded, it must be a property of the MainViewModel.

     public class MainWindowViewModel : ViewModelBase
    {
        public ViewModelBase CurrentViewModel { get; }
        public MainWindowViewModel()
        {
            CurrentViewModel=new UserControlViewModel();
        }
    }
    

    Hope it can help.