1

My ViewModel:

public class MainViewModel : INotifyPropertyChanged
    {
        private User selectedUser;
        private IUserRepository _userRepository;

        public List<User> Users { get; set; }
        public User SelectedUser
        {
            get { return selectedUser; }
            set
            {
                selectedUser = value;
                OnPropertyChanged("SelectedUser");
            }
        }

        public MainViewModel(IUserRepository userRepository)
        {
            _userRepository = userRepository;
            Users = GetAllUsers();
        }
    
        public List<User> GetAllUsers()
        {
            var users = _userRepository.GetAllUsers();
            return users;

        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string prop = "")
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

My View:

<Window x:Class="AppDesc.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        xmlns:vm="clr-namespace:AppDesc.ViewModels"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="FontSize" Value="14" />
        </Style>
        <Style TargetType="TextBox">
            <Setter Property="FontSize" Value="14" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="0.8*" />
        </Grid.ColumnDefinitions>

        <ListBox Grid.Column="0" ItemsSource="{Binding Users}"
                 SelectedItem="{Binding SelectedUser}" Background="#FFA68F8F">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="5">
                        <TextBlock FontSize="18" Text="{Binding Path=Name}" />
                        <TextBlock Text="{Binding Path=Login}" />
                        <TextBlock Text="{Binding Path=Password}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

        <StackPanel Grid.Column="1" DataContext="{Binding SelectedUser}" Background="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <TextBlock Text="Выбранный элемент"  />
            <TextBlock Text="ФИО" />
            <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
            <TextBlock Text="Логин" />
            <TextBox Text="{Binding Login, UpdateSourceTrigger=PropertyChanged}" />
            <TextBlock Text="Телефон" />
            <TextBox Text="{Binding TelephoneNumber, UpdateSourceTrigger=PropertyChanged}" />
        </StackPanel>
    </Grid>
</Window>

Code Behind my View:

enter image description here

I tried to assign my ViewModel to the DataContext in the Code Behind, but an error occurs because my view model has a constructor with parameters. How to correctly connect the view model and the view itself, if the constructor in the ViewModel takes parameters?

EldHasp
  • 6,079
  • 2
  • 9
  • 24
  • 1
    See [How to handle dependency injection in a WPF/MVVM application](https://stackoverflow.com/questions/25366291/how-to-handle-dependency-injection-in-a-wpf-mvvm-application) – Jackdaw Aug 01 '23 at 11:59

2 Answers2

2

The cleanest way is to inject the ViewModel into each View by a DependencyInjection-Framework.

You can use the library Wpf.Extensions.Hosting for running WPF applications on Generic Host.

To inject the ViewModel just change the View code-behind to

public class MainWindow : Window
{
    public MainWindow( MainViewModel viewModel )
    {
        InitializeComponent();
        DataContext = viewModel;
    }
}

and the main code to

// Create a builder by specifying the application and main window.
var builder = WpfApplication<App, MainWindow>.CreateBuilder(args);

// Configure dependency injection.
// Injecting MainWindowViewModel into MainWindow.
builder.Services
    .AddTransient<MainWindowViewModel>()
    .AddTransient<IUserRepository,UserRepositoryImplementingClass>();
   
var app = builder.Build();

await app.RunAsync();

Thats it.

You can find a fully example at github.com

Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
0

The cleanest way of assigning a ViewModel to a View is using the ViewModelLocator pattern.

First define the locator class which handles creation of ViewModel instances.

public class ViewModelLocator
{
    // substitute your choice of DNS container here
    [DNS_Container].Register<IUSerRepository, UserRepository>()
    [DNS_Container].Register<MainViewModel>()

   public MainViewModel MainViewModel => [DNS_Container].GetInstance<MainViewModel>();

   ...
}

Now create an instance of this locator class in App.Xaml, which makes it available as an application-wide resourse.

<Application ...>
    <Application.Resources>
        <local:ViewModelLocator x:Key="ViewModelLocator" />
        ...
    </Application.Resources>
</Application> 

You can now assign the DataContext of the View, using the ViewModelLocator resource as the source.

<Window
    x:Class="MainView"
    ...
    DataContext="{Binding Source={StaticResource ViewModelLocator}, Path=MainViewModel}"
>

    

This construct also allows live data to be supplied to the View at design-time - more details in my blog post.

Peregrine
  • 4,287
  • 3
  • 17
  • 34