C # WPF DataBinding - данные не обновляются - PullRequest
0 голосов
/ 05 июля 2018

Здесь есть несколько вопросов «WPF Databinding Not Updating», и я прочитал их все, включая один из самых популярных: Связывание данных WPF не обновляется?

Тем не менее, я заменяю целые объекты в моей ситуации, и что еще хуже, когда я пытаюсь создать воспроизводимую версию того, что я делаю, она работает в этой версии:

<Window x:Class="WpfApp2.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"
        xmlns:local="clr-namespace:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Label Content="Contact Address" />
        <TextBox x:Name="txtContactAddress" Text="{Binding Path=Contact.Address, Mode=OneWay}" />
        <Label Content="Organization Address" />
        <TextBox x:Name="txtOrgAddress" Text="{Binding Path=Organization.Address, Mode=OneWay}" />
        <Button Content="Next Contact" Click="Button_Click" />
    </StackPanel>
</Window>

Code-Behind:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public RecordWrapper CurrentRecord;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            CurrentRecord = new RecordWrapper(new ContactData());
            this.DataContext = CurrentRecord;
        }
    }

    public class RecordWrapper : INotifyPropertyChanged
    {
        private ContactData _Contact;
        public ContactData Contact
        {
            get { return _Contact;  }
            set
            {
                _Contact = value;
                OnPropertyChanged("Contact");
                Organization = _Contact.Organization;
            }
        }
        private OrganizationData _Organization;
        public OrganizationData Organization
        {
            get { return _Organization; }
            set
            {
                _Organization = value;
                OnPropertyChanged("Organization");
            }
        }

        public RecordWrapper(ContactData contactData)
        {
            Contact = contactData;
        }

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        #endregion
    }

    public class ContactData
    {
        public string Address { get; set; }
        public OrganizationData Organization { get; set; }

        public ContactData()
        {
            Address = new Random().Next(1000, 9999) + " Contact Street";
            Organization = new OrganizationData();
        }
    }

    public class OrganizationData
    {
        public string Address { get; set; }
        public OrganizationData()
        {
            Address = new Random().Next(1000, 9999) + " Org Street";
        }
    }
}

Так что этот конкретный пример работает, как и ожидалось - когда я нажимаю кнопку «Следующий контакт», он генерирует новый объект RecordWrapper, который содержит новые ContactData и новый объект OrganizationData, а затем назначает новый объект DataContext, а затем я вижу текстовые поля обновляются правильно.

Поскольку я заменяю весь объект в DataContext, я могу даже отказаться от уведомлений о свойствах и уменьшить RecordWrapper до всего:

public class RecordWrapper
{
    public ContactData Contact { get; set; }
    public OrganizationData Organization { get; set; }

    public RecordWrapper(ContactData contactData)
    {
        Contact = contactData;
        Organization = Contact.Organization;
    }
}

... и все равно прекрасно работает.

Однако мой реальный код отражает первый пример.

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

BindingOperations.SetBinding(txtContactAddress, TextBox.TextProperty, new Binding() { Source = this.RecordWrapper, Path = new PropertyPath("Contact.Address"), Mode = BindingMode.OneWay });

Когда я это делаю, я вижу, как это работает, но явно не хватает точки привязки, если мне приходится каждый раз запускать код для каждого поля для обновления привязок.

Я в замешательстве, потому что реальный код почти идентичен примеру, за исключением того, что он содержит больше полей, разных имен полей и различий, которые не имеют ничего общего с данными или связыванием. Когда я нажимаю на кнопку, это точный код:

this.Data = new DataBindings(CurrentContact.FetchFake());
this.DataContext = this.Data;

Метод FetchFake () генерирует новый объект CurrentContact с некоторыми поддельными данными, и я убедился, что после запуска первой строки this.Data.CurrentContact.Login заполняется и является доступным только для чтения свойством объекта CurrentContact ( не поле).

... и одно из текстовых полей XAML выглядит так:

<TextBox x:Name="txtAccountNumber" Grid.Column="1" Grid.Row="1"  Width="Auto" Text="{Binding Path=CurrentContact.Login, Mode=OneWay}"/>

Результат пуст. Я также пробовал варианты порядка (например, сначала установите DataContext на this.Data, затем установите свойство this.Data.CurrentContact и разрешите уведомление об изменении свойства, но все равно не повезло. Но если я запусту:

BindingOperations.SetBinding(tabAccount.txtAccountNumber, TextBox.TextProperty, new Binding() { Source = this.Data, Path = new PropertyPath("CurrentContact.Login"), Mode = BindingMode.OneWay });

... тогда я увижу значение.

Исходя из этих симптомов и того факта, что мой первый пример работает, а реальный код не работает, кто-нибудь может подумать о каком-либо месте, где мне следует искать виновных?

1 Ответ

0 голосов
/ 05 июля 2018

Ughhhhh. Догадаться. У меня была ДЕЙСТВИТЕЛЬНО старая строка кода, которая изменила DataContext элемента контейнера, который был между окном и текстовым полем, что прервало правильное связывание. Я думал, что удалил все это, но не сделал. Это ДЕЙСТВИТЕЛЬНО выдало ошибку, как предположил Эрно де Вирд, но эта ошибка была потеряна среди некоторых журналов запуска. Мы можем закрыть это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...