Member 11949544 Ответов: 2

C# WPF & MVVM / обновление данных свойства с привязкой к объекту


Привет ребята,

На самом деле это мой первый вопрос. Я склонен находить все ответы, просматривая сеть, но еще не нашел ответа на этот вопрос...

*Я создал этот код специально для этого вопроса, посмотрите ниже, пожалуйста!*

Я пытался выяснить, есть ли какой-нибудь способ сделать что-то следующим образом:
1) у меня есть база модели представления с IPropertyChanged
2) у меня есть модель, например модель студента с 2 свойствами - ID и Name.
3) у меня есть простое представление MainWindow, которое устанавливается на MainWindowViewModel datacontext.
4) представление показывает сетку данных, ограниченную "Students" из MainWindowViewModel, которая является OC students - ObservableCollection<student>(со случайными данными, которые я вставил).
5) я настроил datagrid так, чтобы он имел SelectedItem = {Binding SelectedStudent}
6) я установил 2 текстовых поля для выбранного студента.ID и SelectedStudent.Name, и значения хорошо отображаются всякий раз, когда я выбираю студента.
7) я также вижу изменения в тексте текстового поля в пользовательском интерфейсе, но не в коде(!!!)
* Я установил UpdateSourceTrigger=on свойство changed, и связывание режим=с двусторонним движением

Проблема возникает всякий раз, когда я хочу попытаться отредактировать значения свойств, изменив текущее значение, показанное в текстовых полях.
Может быть, я не понимаю, что делаю, когда связываю таким образом, но я ставлю точку останова отладки на "get/set" моих свойств - ID и Name, и ничто, кажется, не вызывает метод "Set" (на SelectedStudent - может быть, это часть моей проблемы?).

Это работает, если я усердно работаю и создаю 2 новых свойства, таких как" Id "и" Name " В модели представления, и вместо этого привязываю их к текстовым полям... Я мог бы изменить их значение, и "набор" вызывал бы я мог видеть его на точке останова в самих свойствах - проблема начинается только тогда, когда я пытаюсь изменить дочерние свойства моего объекта...

Нужно ли мне установить stackpanel "local" datacontext(например) на что-то другое???

Просто чтобы вы знали, я работаю с PRISM, но я хочу сделать это как можно проще(если вы знаете ответ в PRISM, я хотел бы знать!).


Большое спасибо всем, кто пытается помочь!

-----------------
Код (все здесь!):
-----
ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged([CallerMemberName]string caller = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(caller));
        }
    }
}

----------------
MainWindowViewModel:
public class MainWindowViewModel : ViewModelBase
    {
        private ObservableCollection<Student> _students;
        private Student _selectedStudent = null;

        public Student SelectedStudent
        {
            get { return _selectedStudent; }
            set 
            {
                _selectedStudent = value;
                OnPropertyChanged();
            }
        }

        public ObservableCollection<Student> Students
        {
            get { return _students; }
            set
            {
                _students = value;
                OnPropertyChanged();
            }
        }

        public MainWindowViewModel()
        {
            Students = new ObservableCollection<Student>();
            Students.Add(new Student { Id = "0", Name = "Jackson" });
            Students.Add(new Student { Id = "1", Name = "Jenny" });
        }
    }

-----------------
Студент:
public class Student : ViewModelBase
{
    private string _id;
    private string _name;

    public string Id
    {
        get { return _id; }
        set { _id = value; OnPropertyChanged(); }
    }

    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged(); }
    }

}

-------------------
Файл MainWindow.язык XAML:
<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0">
        <TextBlock Text="Name: "/>
        <TextBox Text="{Binding SelectedStudent.Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
        <TextBlock Text="Id: "/>
        <TextBox Text="{Binding SelectedStudent.Id, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
    </StackPanel>

    <DataGrid Grid.Row="1" x:Name="dgStudents" ItemsSource="{Binding Students, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" SelectedItem="{Binding SelectedStudent, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" AutoGenerateColumns="True"/>
</Grid>


Что я уже пробовал:

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

2 Ответов

Рейтинг:
2

J. Calhoun

Я попробовал это сделать без имени вызывающего абонента и фактически передал строку, и это сработало для меня, еще одна работа, если вы не хотите использовать код позади, потому что я редко люблю делать такие вещи, - это привязать его к команде с помощью системы.Окна.Класс интерактивности и реализация триггера события; однако я буду искать то, что упущено. Я использовал модель с именем student с идентификатором и именем и смог привязаться к ней. Единственное, о чем я могу думать, это действительно ли вы создали экземпляр Student? В вашем конструкторе

SelectedStudent = new Student();
?
В противном случае это работает для меня.


Richard Deeming

То [CallerMemberName] атрибут на caller параметр заставляет компилятор автоматически вставлять имя вызывающего члена-в данном случае имя свойства.

Он был введен именно с этой целью.

CallerMemberNameAttribute | MSDN[^]

J. Calhoun

У меня были проблемы с этим, но когда я действительно использовал строку, она работала для меня по какой-то причине. Но спасибо Вам за ссылку, так как я буду смотреть дальше и обновил свое решение, чтобы отразить единственное, что я вижу, что отсутствует, что, возможно, может повлиять на привязку.

Рейтинг:
1

Kenneth Haugland

Что ж, они работают, как и ожидалось.

Если вы пишете что-то в SelectedTextBox и текст не обновляется, нажав Tab, когда вы закончите, он обновит значения в вашей коллекции. Но для меня он работает так, как вы хотите, без каких-либо изменений в коде. Если у вас есть проблемы, вы можете попробовать это:

Вы можете принудительно обновить привязки в TextChanged, т. е. :

<StackPanel Grid.Row="0">
    <TextBlock Text="Name: "/>
    <TextBox  x:Name="txtSelectedName" TextChanged="txtSelectedName_TextChanged" Text="{Binding SelectedStudent.Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

С кодом позади:
private void txtSelectedName_TextChanged(object sender, TextChangedEventArgs e)
{
    BindingOperations.GetBindingExpression(txtSelectedName, TextBox.TextProperty).UpdateSource();
}

Об этом должны позаботиться ваши проблемы.


Member 11949544

Привет!

Я еще не пробовал ваше второе решение, я пытаюсь понять, почему нажатие кнопки TAB несколько раз сработало, потому что фокус не на текстовом поле? Что делать, если я хочу, чтобы он обновлялся немедленно, при каждом изменении текстового поля, есть ли другой способ или просто использовать ваш совет с событием "TextChanged"?

Кстати, просто быстрый вопрос - нарушает ли это решение с написанием txtSelectedName_TextChanged в коде позади шаблона MVVM? Я не уверен. Если да, то как я могу сделать это наилучшим образом с моим собственным событием в моей модели представления?

Большое спасибо! большой ответ :).

РЕДАКТИРОВАТЬ:
----
Я действительно пытался поместить это событие в код MainWindow.xaml.cs, но оно тоже не сработало, только после нажатия tab tab... Так что никакой разницы с тем, о чем вы упомянули в первую очередь. Это сработало прямо на месте для вас?

Kenneth Haugland

Ну, это не сработало сразу. Мне пришлось построить его во второй раз, и тогда все проблемы исчезли. Это само по себе не разрушает шаблон MVVM, так как привязка обновления является универсальной. Он не знает, к чему привязано текстовое поле. Но я согласен, в этом нет необходимости. Кстати: какую версию VS вы используете?

Member 12929951

Здравствуйте, большое спасибо! Использование 'UpdateSourceTrigger=PropertyChanged' сделало волшебство.