WPF UI 未更新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16045247/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 08:36:57  来源:igfitidea点击:

WPF UI is not updating

c#wpfmvvm

提问by santosh singh

I have developed a simple wpf application using MVVM model and code as below.I debug the code and found that collection is updating but UI is not updating even I don't see any record in listbox.

我使用 MVVM 模型和代码开发了一个简单的 wpf 应用程序,如下所示。我调试代码并发现集合正在更新但 UI 没有更新,即使我在列表框中没有看到任何记录。

EDIT

编辑

enter image description here

在此处输入图片说明

<Window x:Class="Model.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF Dispatcher Demo" Height="350" Width="525">


    <DockPanel>
        <Grid DockPanel.Dock="Bottom">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="2*" Name="col0" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <TextBox Name="textBlock1" Text="{Binding ShowPath}" 
                 VerticalAlignment="Center" HorizontalAlignment="Left" 
                  Margin="30" Grid.Column="0" />
            <Button Name="FindButton" Content="Select Path" 
              Width="100" Margin="20" Click="FindButton_Click" Grid.Column="1" />
        </Grid>
        <ListBox Name="listBox1"  ItemsSource="{Binding Path=Files}"/>


    </DockPanel>
</Window>

Model

模型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Threading;
using System.ComponentModel;
using System.Collections.ObjectModel;


namespace Model
{
    public class DirectorySearchModel : INotifyPropertyChanged
    {
        private ObservableCollection<string> _files = new ObservableCollection<string>();
        private string _showPath;
        public DirectorySearchModel() { }
        void ShowCurrentPath(string path)
        {
            ShowPath = path;
        }


        void AddFileToCollection(string file)
        {
            _files.Add(file);
        }
        public void Search(string path, string pattern)
        {

            if (System.Windows.Application.Current.Dispatcher.CheckAccess())
                ShowPath = path;
            else
                System.Windows.Application.Current.Dispatcher.Invoke(
                  new Action<string>(ShowCurrentPath), DispatcherPriority.Background, new string[] { path }
                );
            string[] files = Directory.GetFiles(path, pattern);
            foreach (string file in files)
            {
                if (System.Windows.Application.Current.Dispatcher.CheckAccess())
                    Files.Add(file);
                else
                    System.Windows.Application.Current.Dispatcher.Invoke(new Action<string>(AddFileToCollection), DispatcherPriority.Background,
                      new string[] { file }
                    );
            }
            string[] dirs = System.IO.Directory.GetDirectories(path);
            foreach (string dir in dirs)
                Search(dir, pattern);
        }

        public string ShowPath
        {
            get
            {
                return _showPath;
            }
            set
            {
                _showPath = value;
                OnPropertyChanged("ShowPath");
            }
        }
        public ObservableCollection<string> Files
        {
            get
            {
                return _files;
            }
            set
            {
                _files = value;
                OnPropertyChanged("Files");
            }
        }
        public void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));

            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

MainWindow.cs

主窗口.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.Windows.Forms;
using System.IO;

namespace Model
{
    public partial class MainWindow : Window
    {
        IAsyncResult cbResult;
        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);

        }
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = new DirectorySearchModel().Files;
        }

        private void FindButton_Click(object sender, RoutedEventArgs e)
        {

            FolderBrowserDialog dlg = new FolderBrowserDialog();
            string path = AppDomain.CurrentDomain.BaseDirectory;
            dlg.SelectedPath = path;
            DialogResult result = dlg.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                path = dlg.SelectedPath;
                string pattern = "*.*";
                new Model.DirectorySearchModel().Search(path, pattern);
                Action<string, string> proc = new Model.DirectorySearchModel().Search;
                cbResult = proc.BeginInvoke(path, pattern, null, null);

            }
        }
    }
}

回答by Rachel

In WPF, there are two layers: the UI layer and the Data Layer

在WPF中,有两层:UI层和数据层

The Data Layer is your DataContext, and when you write a normal binding, you are binding to the DataContext.

数据层是您的DataContext,当您编写普通绑定时,您将绑定到DataContext.

When you write

当你写

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    this.DataContext = new DirectorySearchModel().Files;
}

you are telling WPF that the data layer for the form is going to be an ObservableCollection of strings, however ObservableCollection<string>does not have a property called Files, so your ListBox binding is failing.

您告诉 WPF 表单的数据层将是字符串的 ObservableCollection,但是ObservableCollection<string>没有名为 的属性Files,因此您的 ListBox 绑定失败。

<!-- Trying to bind to DirectorySearchModel.Files.Files -->
<ListBox ItemsSource="{Binding Path=Files}"/>

You need to change your data layer to your DirectorySearchModelinstead, so the binding correctly evaluates to DirectorySearchModel.Files

您需要将数据层更改为您的DirectorySearchModel,因此绑定正确评估为DirectorySearchModel.Files

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    this.DataContext = new DirectorySearchModel();
}

But this isn't your only problem. Your Button's Click event operates on a newinstance of DirectorySearchModelinstead of the existing one.

但这不是你唯一的问题。您的 Button 的 Click 事件在一个实例上运行,DirectorySearchModel而不是在现有实例上运行。

You could simply use (DirectorySearchModel)MainWindow.DataContext, but this isn't ideal because it tightly combines your UI and your data layers closely together, and it makes the assumption that the DataContextwill always be of type DirectorySearchModel.

您可以简单地使用(DirectorySearchModel)MainWindow.DataContext,但这并不理想,因为它将您的 UI 和您的数据层紧密地结合在一起,并且假设DataContext将始终是 type DirectorySearchModel

You could store the DirectorySearchModelused for the DataContextsomewhere like moncadad suggestedso you can access it from somewhere else in your code:

您可以将DirectorySearchModelused存储在moncadad 建议DataContext某个地方,以便您可以从代码中的其他地方访问它:

DirectorySearchModel _model = new DirectorySearchModel();
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    _model = new DirectorySearchModel();
    this.DataContext = _model ;
}

private void FindButton_Click(object sender, RoutedEventArgs e)
{
    // use _model instead of "new DirectorySearchModel()" here
}

But honestly this still isn't ideal because your View and your Data layers are still tightly coupled together, and this isn't compliant with the MVVM design pattern (which you've tagged your question with, so I'm assuming you're using).

但老实说这仍然不理想,因为您的视图和数据层仍然紧密耦合在一起,并且这不符合 MVVM 设计模式(您已经用它标记了您的问题,所以我假设您是使用)。

The best solution is to actually replace your button's Click event with an ICommandon the DirectorySearchModelso you don't have to worry about storing and accessing a copy of your data layer from your UI layer. This also has the added benefit of keeping application logic inside the application layer, instead of mixing it in with the UI layer:

最好的解决方案是实际使用ICommandon替换按钮的 Click 事件,DirectorySearchModel这样您就不必担心从 UI 层存储和访问数据层的副本。这还有一个额外的好处是将应用程序逻辑保留在应用程序层内,而不是将其与 UI 层混合:

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    this.DataContext = new DirectorySearchModel();
}

<DockPanel>
    <Grid DockPanel.Dock="Bottom">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" Name="col0" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <TextBox Text="{Binding ShowPath}" ... />

        <Button Content="Select Path" Command="FindButtonCommand" ... />
    </Grid>
    <ListBox ItemsSource="{Binding Path=Files}"/>
</DockPanel>

This correctly separates your UI from your Data layer, which is the primary goal of the MVVM design pattern.

这正确地将您的 UI 与您的数据层分开,这是 MVVM 设计模式的主要目标。

This way, your application logic all stays inside your application objects, while your UI becomes just a pretty user-friendly interface that is used to interact with your application objects.

这样,您的应用程序逻辑都保留在您的应用程序对象中,而您的 UI 只是一个非常用户友好的界面,用于与您的应用程序对象进行交互。

I like to write beginner WPF articles, and I'd suggest reading my post What is this "DataContext" you speak of?to understand what the DataContextis and how it works a bit better :)

我喜欢写初学者的 WPF 文章,我建议阅读我的文章你所说的“DataContext”是什么?了解DataContext它是什么以及它如何更好地工作:)

回答by NTyler

You have: this.DataContext = new DirectorySearchModel().Files;

你有: this.DataContext = new DirectorySearchModel().Files;

and this: <ListBox Name="listBox1" ItemsSource="{Binding Path=Files}"/>

和这个: <ListBox Name="listBox1" ItemsSource="{Binding Path=Files}"/>

Which means it will try to bind to DirectorySearchModel().Files.Files. You probably want to change to this: this.DataContext = new DirectorySearchModel();

这意味着它将尝试绑定到 DirectorySearchModel().Files.Files。你可能想改成这样:this.DataContext = new DirectorySearchModel();

回答by Silvermind

** Edit **

** 编辑 **

I was staring blind :)

我一直在瞎看:)

You're not reusing your model: This works, I just tested it

你没有重复使用你的模型:这有效,我刚刚测试过

    private DirectorySearchModel model = new DirectorySearchModel();

    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(MainWindow_Loaded);

    }
    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        this.DataContext = model; //The model is already available as private member
    }

    private void FindButton_Click(object sender, RoutedEventArgs e)
    {

        FolderBrowserDialog dlg = new FolderBrowserDialog();
        string path = AppDomain.CurrentDomain.BaseDirectory;
        dlg.SelectedPath = path;
        DialogResult result = dlg.ShowDialog();
        if (result == System.Windows.Forms.DialogResult.OK)
        {
            path = dlg.SelectedPath;
            string pattern = "*.*";
            Action<string, string> proc = model.Search; // use existing model (the private member).
            cbResult = proc.BeginInvoke(path, pattern, null, null);

        }
    }

In Xaml put this:

在 Xaml 中输入:

<ListBox ItemsSource="{Binding Path=Files}"/>

回答by d.moncada

In your MainWindow.cs, change to:

在您的 MainWindow.cs 中,更改为:

public partial class MainWindow : Window
{
    IAsyncResult cbResult;
    DirectorySearchModel _model = new DirectorySearchModel();
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = _model;
    }
...

In your Xaml, update your binding to:

在您的 Xaml 中,将您的绑定更新为:

<ListBox Name="listBox1"  ItemsSource="{Binding Files, UpdateSourceTrigger="PropertyChanged"}"/>

回答by Allan Chua

You have to set the DataContext of your window to a new instance of DirectorySearchModel. it is not updating because your ListBox inherited the DataContext of the window which is set to the Files property. Your listbox is looking for the Files Property of the Files Collection which does not exists.

您必须将窗口的 DataContext 设置为 DirectorySearchModel 的新实例。它没有更新,因为您的 ListBox 继承了设置为 Files 属性的窗口的 DataContext。您的列表框正在寻找不存在的文件集合的文件属性。

You can use this Code

您可以使用此代码

public MainWindow()
{
   this.DataContext = new DirectorySearchModel();
}

this way your list box will have a datacontext of a new directory search model and will be looking for the Files property which you had specify on the Pathproperty of the ItemSource Binding.

这样,您的列表框将具有新目录搜索模型的数据上下文,并将查找您在 ItemSource Binding的Path属性上指定的 Files 属性。

UPDATE:

更新:

    private void FindButton_Click(object sender, RoutedEventArgs e)
    {

        FolderBrowserDialog dlg = new FolderBrowserDialog();
        string path = AppDomain.CurrentDomain.BaseDirectory;
        dlg.SelectedPath = path;
        DialogResult result = dlg.ShowDialog();
        if (result == System.Windows.Forms.DialogResult.OK)
        {
            path = dlg.SelectedPath;
            string pattern = "*.*";
            //OLD CODE                
            //new Model.DirectorySearchModel().Search(path, pattern);
            //SUGGESTION
            (this.DataContext AS DirectorySearchModel).Search(path, pattern);
            Action<string, string> proc = (this.DataContext AS DirectorySearchModel).Search;
            cbResult = proc.BeginInvoke(path, pattern, null, null);

        }
    }