Как работает DataContext?

Рейтинг: 0Ответов: 2Опубликовано: 21.02.2023

Не понимать, почему он не выводит в TextBlock

User.cs:

class User
    {
        public string name { get; set; } = "IVAN";
        public string surname { get; set; } = "IVANOV";
    }

MainWindow.xaml:

<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBox Width="130" Height="30"/>
            <TextBox Width="130" Height="30" Margin="0,10,0,0"/>
        </StackPanel>
        <StackPanel VerticalAlignment="Center" HorizontalAlignment="Right" Background="Black" Width="130" Margin="10">
            <TextBlock Text="{Binding name}" Foreground="White"/>
            <TextBlock Text="{Binding surname}" Margin="0,10,0,0" Foreground="White"/>
        </StackPanel>
        <Button Width="130" Height="30" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="10" Content="REFRESH" Click="Button_Click"/>

MainWindow.cs:

        private User user { get; set; }
        public MainWindow()
        {
            InitializeComponent();
            //user = DataContext as User;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            DataContext = user;
        }

введите сюда описание изображения

UPDATE: Решил усложнить. Теперь нужно запихнуть данные из TextBox в User. Но очевидно не сработало, передает null в user

MainWindow.cs:

private void Button_Click(object sender, RoutedEventArgs e)
        {
            user = DataContext as User;
            DataContext = user;
        }

Ответы

▲ 2Принят

С привязками данных надо понять один нюанс. Если при использовании обработчиков событий и прямом доступе к контролам, вы из кода присваиваете значение свойств контролов, то с привязками всё наоборот, контрол сам берёт значение из привязанного свойства и кладёт туда измененное значение в случае его модификации. А из кода вы просто работаете с этим свойством.

Назначаение DataContext выглядит так. Класс User при этом должен быть public.

user = new User();
DataContext = user;

DataContext обычно назначается в конструкторе окна или обработчике события Window.Loaded. Второе предпочтительней.

Когда изменяется DataContext, View внутри себя вызывает событие DataContextChanged и обновляет содержимое всех контролов. Но это сработает только один раз.

Если ничего не дорабатывать, то будет работать только односторонняя привязка View->ViewModel. То есть при изменении руками значений текстбоксов, значения свойств вьюмодели изменятся. При этом здесь имеет значение поведение привязки данных, по умолчанию сам факт изменения произойдет по потере фокуса текстбокса, который например произойдет автоматически когда вы нажмете на кнопку.

Попробуйте что-то поменять в текстбоксе, а затем нажать кнопку с таким кодом.

MessageBox.Show($"{user.name} {user.surname}");

И вы увидите изменения, которые внесли в текстбоксы.

Но на самом деле привязка данных может быть двухсторонней. А так же привязывать можно к одному свойству несколько контролов.

Двухсторонняяя привязка реализуется с помощью интерфейса INotifyPropertyChanged для вьюмодели. Для этого существет уже написанная шаблонная примочка - реализация данного интерфейса.

public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Просто вставьте этот класс в проект, и затем унаследуйтесь от него во вьюмодели. (давайте публичные свойства называть с большой буквы, так принято в C#)

public class User : NotifyPropertyChanged
{
    private string name;
    private string surname;

    public string Name
    {
        get => name;
        set
        {
            name = value;
            OnPropertyChanged(); // сообщает контролу что свойство изменилось
        }
    }

    public string Surname
    {
        get => surname;
        set
        {
            surname = value;
            OnPropertyChanged();
        }
    }
}

Теперь можно в коде сделать так

user.Name = "Test name";

И оно моментально поменяется в интерфейсе.

Сделайте 2 контрола, чтобы понять как это работает.

<TextBox Text="{Binding Name}"/>
<TextBlock Text="{Binding Name}"/>

Попробуйте поменять значение из текстбокса и из кода.

Попробуйте поменять поведение привязки вот так.

<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>

И посмотрите что поменяется.

▲ 0

MainWindow.cs:

private User user { get; set; }= new User();