这是非正式的和原型代码,因此我尝试自己认为应该起作用的方法,如果不行再去查找谷歌,然后在浏览类似问题后在这里询问。
我在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>
然后在ShellViewModel
中,我有以下两个属性和一个事件处理程序:
private string _statusMessage;
public string StatusMessage
{
get => _statusMessage;
set => SetProperty(ref _statusMessage, value);
}
private double _statusProgress;
public double StatusProgress
{
get => _statusProgress;
set => SetProperty(ref _statusProgress, value);
}
private void OnFileTransferStatusChanged(object sender, FileTransferStatusEventArgs fileTransferStatusEventArgs)
{
StatusMessage = fileTransferStatusEventArgs.RelativePath;
StatusProgress = fileTransferStatusEventArgs.Progress;
}
该事件会定期触发,即从文件下载帮助类每n个迭代时触发。现在奇怪的是,当事件处理程序更新vm属性时,在Shell视图上,绑定到StatusMessage的TextBlock会正确更新和显示,但绑定到StatusProgress的ProgressBar则不会更新,保持空白。如果我在事件处理程序中设置断点,我可以看到StatusProgress属性被正确地更新为从0到100的各种值,但这不会反映在ProgressBar上。我意识到事件处理程序在另一个线程上执行,这经常会导致UI更新问题,但为什么一个UI元素能够正确更新而另一个不能呢?
注意: 我已经异常愚蠢并没有静态测试ProgressBar,即只需将viewmodel的StatusProgress设置为一个值并且让shell窗口显示,而不是通过下载循环。如果我这样做,进度条会显示与其Value属性更或多或少相对应的长度。评论或答案中提出的任何布局更改建议都无法改变这一点。静态地它总是可见的,并且总是显示一个值。
示例: 我创建了一个小示例,认为它复制了这个问题。在该示例中,直到等待的任务完成之前,进度条才会更新,我认为这也是我的主要问题所在,但这是一个长时间的下载,我在注意到进度条未更新之前没有等待它完成。
这里是MainWindow.xaml中的StatusBar:
<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>
在 MainWindow.xaml.cs
文件中的代码如下:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
public MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext;
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ViewModel.Download();
}
还有在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();
}
最后,这是 FileDownloader
的代码:
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);
}
}
}
在这个例子中,进度条一直保持空白状态,直到
FileDownloader
完成其循环,然后突然间进度条就显示完整的进度,即下载完成。
Width
重现了您的问题。 - jsanalyticsWidth
解决你的问题。 - jsanalyticsDataBindEngine
可以很好地处理多线程,我认为可能存在一个更简单的问题。也许你应该:ProgressBar.ValueChanged
事件是否真正未更改ProgressBar.Value
。fileTransferStatusEventArgs.Progress
,并且fileTransferStatusEventArgs.Progress
具有您期望的值。fileTransferStatusEventArgs.Progress
是否会导致任何异常。Width
是"missing"时,即使StatusProgress
在UI线程中(或已经以其他方式正确处理),也会发生OP描述的问题。而且,正如我们之前建议的那样,为Width
分配一个合适的值,可以在StatusProgress
在UI线程中更新或不更新的情况下解决这个问题。 - jsanalytics