WPF用户界面未更新?

时间:2020-10-06 13:55:59

标签: c# wpf debugging data-binding

经历:

WPF binding not updating the view https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?redirectedfrom=MSDN&view=netcore-3.1

WPF DataContext updated but UI not updated

在以下情况下,我仍然看不到为什么UI不更新(我的最佳猜测是要更新的Grid的DataContext没有更新),这让我很困惑:

AppliedJobsModel.cs (已根据一些答案的建议实施了IPropertyChange):

public class AppliedJobsModel { }
public class AppliedJob : INotifyPropertyChanged
{
    private string appliedDate;
    private string url;
    private string company;
    private string description;
    private string contact;
    private string stack;
    private string response;
    private string interviewDate;

    public AppliedJob(string[] entries)
    {
        appliedDate = entries[Consts.APPLIED_DATE_INDEX];
        url = entries[Consts.URL_INDEX];
        company = entries[Consts.COMPANY_INDEX];
        description = entries[Consts.DESCRIPTION_INDEX];
        contact = entries[Consts.CONTACT_INDEX];
        stack = entries[Consts.STACK_INDEX];
        response = entries[Consts.RESPONSE_INDEX];
        interviewDate = entries[Consts.INTERVIEWDATE_INDEX];
    }

    public string AppliedDate
    {
        get {
            return appliedDate;
        }

        set {
            if (appliedDate != value)
            {
                appliedDate = value;
                RaisePropertyChanged("AppliedDate");
            }
        }
    }

    public string Url
    {
        get
        {
            return url;
        }

        set
        {
            if (url != value)
            {
                url = value;
                RaisePropertyChanged("Url");
            }
        }
    }

    public string Company
    {
        get
        {
            return company;
        }

        set
        {
            if (company != value)
            {
                company = value;
                RaisePropertyChanged("Company");
            }
        }
    }

    public string Description
    {
        get
        {
            return description;
        }

        set
        {
            if (description != value)
            {
                description = value;
                RaisePropertyChanged("Description");
            }
        }
    }

    public string Contact
    {
        get
        {
            return contact;
        }

        set
        {
            if (contact != value)
            {
                contact = value;
                RaisePropertyChanged("Contact");
            }
        }
    }

    public string Stack
    {
        get
        {
            return stack;
        }

        set
        {
            if (stack != value)
            {
                stack = value;
                RaisePropertyChanged("Stack");
            }
        }
    }

    public string Response
    {
        get
        {
            return response;
        }

        set
        {
            if (response != value)
            {
                response = value;
                RaisePropertyChanged("Response");
            }
        }
    }

    public string InterviewDate
    {
        get
        {
            return interviewDate;
        }

        set
        {
            if (interviewDate != value)
            {
                interviewDate = value;
                RaisePropertyChanged("InterviewDate");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}

AppliedJobsViewModel.cs (具有一个可观察的集合,当单击按钮时(在dbg中),该集合可以正确更新):

class AppliedJobsViewModel
{
    private TexParser texParser;
   

    public AppliedJobsViewModel() {
        // TODO:
        // -- do nothing here 
    }

    public ObservableCollection<AppliedJob> AppliedJobsCollection
    {
        get;
        set;
    }

    private ICommand _openTexClick;
    public ICommand OpenTexClick
    {
        get
        {
            return _openTexClick ?? (_openTexClick = new CommandHandler(() => ReadAndParseTexFile(), () => CanExecute));
        }
    }
    public bool CanExecute
    {
        get
        {
            // check if executing is allowed, i.e., validate, check if a process is running, etc. 
            return true;
        }
    }

    public async Task ReadAndParseTexFile()
    {
        if (texParser == null)
        {
            texParser = new TexParser();
        }
        // Read file asynchronously here
        await Task.Run(() => ReadFileAndUpdateUI());            
    }

    private void ReadFileAndUpdateUI()
    {
        texParser.ReadTexFile();
        string[][] appliedJobsArray = texParser.getCleanTable();
        // Use this: 
        // https://rachel53461.wordpress.com/2011/09/17/wpf-grids-rowcolumn-count-properties/
        // Update collection here
        List<AppliedJob> appliedJobsList = createAppliedJobsListFromTable(appliedJobsArray);
        AppliedJobsCollection = new ObservableCollection<AppliedJob>(appliedJobsList);
    }

    private List<AppliedJob> createAppliedJobsListFromTable(string[][] table)
    {
        List<AppliedJob> jobsList = new List<AppliedJob>();

        for (int i = 0; i < table.Length; i++)
        {
            jobsList.Add(new AppliedJob(table[i]));
        }

        return jobsList;
    }
}

AppliedJobsView.xaml

<UserControl x:Class="JobTracker.Views.AppliedJobsView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:JobTracker.Views"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">
<Grid Name="appliedJobsGrid" Grid.Row="1" Grid.Column="1" Background="#50000000" Margin="10,10,10,10">
    <ItemsControl ItemsSource = "{Binding Path = AppliedJobsCollection}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation = "Horizontal">
                    <TextBox Text = "{Binding Path = AppliedDate, Mode = TwoWay}" Width = "100" />

                    <TextBox Text = "{Binding Path = Url, Mode = TwoWay}"  Width = "100" />

                    <TextBox Text = "{Binding Path = Company, Mode = TwoWay}"  Width = "100" />

                    <TextBox Text = "{Binding Path = Description, Mode = TwoWay}"  Width = "100" />

                    <TextBox Text = "{Binding Path = Contact, Mode = TwoWay}"  Width = "100" />

                    <TextBox Text = "{Binding Path = Stack, Mode = TwoWay}"  Width = "100" />

                    <TextBox Text = "{Binding Path = Response, Mode = TwoWay}"  Width = "100" />

                    <TextBox Text = "{Binding Path = InterviewDate, Mode = TwoWay}"  Width = "100" />
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

TrackerHome.xaml (主页/使用用户控件):

<Grid Grid.Row="1" Grid.Column="1">
    <views:AppliedJobsView x:Name = "AppliedJobsControl" Loaded = "AppliedJobsViewControl_Loaded" />
</Grid>

TrackerHome.cs

    public TrackerHome()
    {
        InitializeComponent();
        // Set data context here (https://stackoverflow.com/questions/12422945/how-to-bind-wpf-button-to-a-command-in-viewmodelbase)
        // https://stackoverflow.com/questions/33929513/populate-a-datagrid-using-viewmodel-via-a-database
        if (appliedJobsViewModel == null)
        {
            appliedJobsViewModel = new AppliedJobsViewModel();                
        }
        this.DataContext = appliedJobsViewModel;
        //AppliedJobControl.DataContext = appliedJobsViewModel;
    }

    private void AppliedJobsViewControl_Loaded(object sender, RoutedEventArgs e)
    {
        if (appliedJobsViewModel == null)
        {
            appliedJobsViewModel = new AppliedJobsViewModel();                
        }
        AppliedJobsControl.DataContext = appliedJobsViewModel;
    }

1 个答案:

答案 0 :(得分:1)

您要在此处设置属性的新值:

AppliedJobsCollection = new ObservableCollection<AppliedJob>(appliedJobsList);

但这是一个简单的自动属性,无需通知。

使其具有完整属性(视图模型需要实现INotifyPropertyChange

ObservableCollection<AppliedJob> _appliedJobsCollection =
    new ObservableCollection<AppliedJob>(); // empty initially
public ObservableCollection<AppliedJob> AppliedJobsCollection
{
    get => _appliedJobsCollection;
    set
    {
        _appliedJobsCollection = value;
        RaisePropertyChanged(nameof(AppliedJobsCollection));
    }
}

full属性如何表现?好像集合中每个项目中的所有条目都已更改(因此属性也已更改)一样?

请参阅此伪代码。

// given that AppliedJobsCollection is already initialized

// modify existing collection -> works
// bindings was subscribed to CollectionChanged event and will update
AppliedJobsCollection.Add(new AppliedJob(...));

// change item property -> works
// you implement INotifyPropertyChanged for items
// bindings was subscribed to that and will update
AppliedJobsCollection[0].Company = "bla";

// new instance of collection -> ... doesn't works
// how bindings can update?
AppliedJobsCollection = new ObservableCollection<AppliedJob>(...);

要使最后一种情况正常工作,您需要为包含INotifyPropertyChanged属性和上升通知的类实现AppliedJobsCollection