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?

1

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.