Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
WPF ProgressBar未更新_Wpf_User Interface_Events_Mvvm_Progress Bar - Fatal编程技术网

WPF ProgressBar未更新

WPF ProgressBar未更新,wpf,user-interface,events,mvvm,progress-bar,Wpf,User Interface,Events,Mvvm,Progress Bar,这是一段随意的原型代码,因此我尝试我认为应该有效的东西,如果不行,就在谷歌上搜索,然后在阅读了类似的问题后在这里提问 我的Shell视图中有以下标记: <StatusBarItem Grid.Column="0"> <TextBlock Text="{Binding StatusMessage}" /> </StatusBarItem> <Separator Grid.Column="1" /> <StatusBarItem Grid

这是一段随意的原型代码,因此我尝试我认为应该有效的东西,如果不行,就在谷歌上搜索,然后在阅读了类似的问题后在这里提问

我的
Shell
视图中有以下标记:

<StatusBarItem Grid.Column="0">
    <TextBlock Text="{Binding StatusMessage}" />
</StatusBarItem>
<Separator Grid.Column="1" />
<StatusBarItem Grid.Column="2">
    <ProgressBar Value="{Binding StatusProgress}" Minimum="0" Maximum="100" Height="16" Width="198" />
</StatusBarItem>
从文件下载帮助器类定期(即每n次迭代)引发事件

现在奇怪的是,当事件处理程序更新vm属性时,在
Shell
视图上,绑定到
StatusMessage
TextBlock
更新并正确显示,但是绑定到
StatusProgress
ProgressBar
没有更新并保持空白。如果在事件处理程序中放置断点,我可以看到
StatusProgress
属性在0到100的不同值中正确更新,但这不会反映在
ProgressBar

我想到在另一个线程上执行事件处理程序,这通常会导致UI更新问题,但为什么一个UI元素更新正确而另一个没有

注意:我非常愚蠢,没有静态测试
ProgressBar
,也就是说,只需将viewmodel的
StatusProgress
设置为一个值,然后让shell窗口显示出来,而不必经过下载循环。如果执行此操作,进度条将显示与其
属性大致对应的长度。在评论或回答中提出的布局更改建议均不会改变这一点。静态上,它始终可见并始终显示一个值

示例:我创建了一个小示例,认为它重复了问题。在这个例子中,进度条直到等待的任务完成后才会更新,我相信我的主要问题也是如此,但下载时间很长,我没有等到它完成后才注意到进度条没有更新

以下是`MainWindow.xaml中的
状态栏

<StatusBar DockPanel.Dock="Bottom" Height="20">
    <StatusBar.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="2" />
                    <ColumnDefinition Width="200" />
                </Grid.ColumnDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </StatusBar.ItemsPanel>
    <StatusBarItem Grid.Column="2">
        <ProgressBar Value="{Binding StatusProgress}" Maximum="100" Minimum="0" Height="16" Width="198" />
    </StatusBarItem>
</StatusBar>
以及
MainWindowViewModel
中的代码:

private string _statusMessage = "Downloading something";
public string StatusMessage
{
    get => _statusMessage;
    set
    {
        if (value == _statusMessage) return;
        _statusMessage = value;
        OnPropertyChanged();
    }
}

private int _statusProgress;
public int StatusProgress
{
    get => _statusProgress;
    set
    {
        if (value == _statusProgress) return;
        _statusProgress = value;
        OnPropertyChanged();
    }
}

public void Download()
{
    var dl = new FileDownloader();
    dl.ProgressChanged += (sender, args) =>
    {
        StatusProgress = args.Progress;
    };
    dl.Download();
}
最后是
文件下载程序的代码:

public class ProgressChangedEventArgs
{
    public int Progress { get; set; }
}

public class FileDownloader
{
    public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
    public void Download()
    {            
        for (var i = 0; i < 100; i++)
        {
            ProgressChanged?.Invoke(this, new ProgressChangedEventArgs{Progress = i});
            Thread.Sleep(200);
        }
    }
}
公共类ProgressChangedEventArgs
{
公共int进程{get;set;}
}
公共类文件下载程序
{
公共事件处理程序已更改;
公开下载
{            
对于(变量i=0;i<100;i++)
{
ProgressChanged?.Invoke(这是新的ProgressChangedEventArgs{Progress=i});
睡眠(200);
}
}
}

在本例中,进度条保持空白,直到
FileDownloader
完成其循环,然后进度条突然显示完全进度,即完成。

这种情况发生,因为
StatusBarItem
默认样式将其
HorizontalContentAlignment
设置为
Left
,这将导致
ProgressBar
在水平方向上仅获得少量空间


通过将
StatusBarItem
HorizontalContentAlignment
设置为
Stretch
,您可以使
ProgressBar
完全填充
StatusBarItem
,也可以将
ProgressBar
宽度设置为DispatcherObject,并且

如果我能很好地理解您的问题,那么您的onfiletTransferStatusChanged将在后台线程上触发,因此,由于您没有使用Dispatcher(或从UI线程)访问控件,因此无法保证代码能够正常工作

问题在于,非UI线程的绑定通常会一直工作到它不工作为止——例如,在非开发机器上。

就像第一个答案一样 请确保位于主UI线程上,因为OnFileTransferStatusChanged位于另一个线程上。在你的活动中使用这个

    Application.Current.Dispatcher.Invoke(prio, (ThreadStart)(() => 
{
   StatusMessage = fileTransferStatusEventArgs.RelativePath;
    StatusProgress = fileTransferStatusEventArgs.Progress;
}));

我对您的示例做了一些更改,因为您下载的文件正在UI线程上运行,应用程序只是冻结了。您可以通过将焦点更改为其他应用程序并尝试返回来查看它—窗口将不会出现,也不会更新

变化:

 private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
 {
    Task.Factory.StartNew(() => ViewModel.Download());
 }
强制下载在新线程中执行

public MainWindow()
{
    InitializeComponent();
    DataContext = ViewModel = new MainWindowViewModel();
}
public MainWindowViewModel ViewModel { get; }
已删除仅对UI线程属性的强制转换和访问
DataContext

现在我可以看到进度条了。

您看不到任何更改,因为您的主线程AKAUI线程正在忙于睡眠 而且它没有时间更新您的UI

让任务处理冗长的作业和更新UI的主线程

将代码包装到任务中,您就可以看到进度条的进度

private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
      await Task.Run(() =>
      {
           _viewModel.Download();
       });
            //_viewModel.Download(); //this will run on UI Thread

 }
发生了什么事 任何与UI无关的事情都应该在任务中完成,因为如果不是这样,那么就是阻塞了UI线程和UI。 在您的情况下,下载发生在您的UI线程上,后者在更新UI之前等待下载完成

解决方案 你需要做两件事来解决你的问题:

  • 从UI线程中删除工作

  • 确保工作可以与您的UI线程通信

  • 因此,首先,将下载工作作为
    任务开始,如下所示:

    private ICommand _startDownloadCommand;
    public ICommand StartDownloadCommand
    {
        get
        {
            return _startDownloadCommand ?? (_startDownloadCommand = new DelegateCommand(
                       s => { Task.Run(() => Download()); },
                       s => true));
        }
    }
    
    <Button Command="{Binding StartDownloadCommand}" Content="Start download" Height="20"/>
    
    并将按钮连接到命令,如下所示:

    private ICommand _startDownloadCommand;
    public ICommand StartDownloadCommand
    {
        get
        {
            return _startDownloadCommand ?? (_startDownloadCommand = new DelegateCommand(
                       s => { Task.Run(() => Download()); },
                       s => true));
        }
    }
    
    <Button Command="{Binding StartDownloadCommand}" Content="Start download" Height="20"/>
    
    调度将从非UI线程更新您的属性(在UI线程上)

    然而,
    DelegateCommand
    helper类:

    public class DelegateCommand : ICommand
    {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _execute;
        public event EventHandler CanExecuteChanged;
    
        public DelegateCommand(Action<object> execute)
            : this(execute, null) {}
    
        public DelegateCommand(Action<object> execute,
            Predicate<object> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
        public void Execute(object parameter) => _execute(parameter);
        public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
    
    这种观点:

    <Window x:Class="WpfApp1.MainWindow"
            d:DataContext="{d:DesignInstance local:MainWindowViewModel,
                IsDesignTimeCreatable=True}"
            xmlns:local="clr-namespace:WpfApp1"
    

    如果将
    HorizontalContentAlignment=“Stretch”
    设置为包含
    ProgressBar
    StatusBarItem
    ,它看起来有什么不同吗?没有
    Width
    。使用
    Width
    。由于
    DataIndengine
    可以很好地处理多线程,我认为很像发生了一个更简单的问题。也许你应该1。确认if
    ProgressBar.Value
    realy未通过
    ProgressBar.ValueChanged
    事件更改。2.如果结果为1。至少两次为正,然后确认
    fileTransferStatusEventArgs.Progress
    每次都发生了更改,并且
    fileTransferStatusEventArgs.Progress
    具有您的表达式的值。3.如果结果为1。是阴性
    public partial class MainWindow : IView
    {
        public IViewModel ViewModel
        {
            get { return (IViewModel)DataContext; }
            set { DataContext = value; }
        }
    
        public MainWindow()
        {
            DataContext = new MainWindowViewModel();
        }
    }
    
    public interface IViewModel {}
    
    public interface IView {}
    
    <Window x:Class="WpfApp1.MainWindow"
            d:DataContext="{d:DesignInstance local:MainWindowViewModel,
                IsDesignTimeCreatable=True}"
            xmlns:local="clr-namespace:WpfApp1"
    
    public class MainWindowViewModel: INotifyPropertyChanged, IViewModel