Это случайный и прототипный код, поэтому я пробовал то, что, по моему мнению, должно работать, гуглял, если не получилось, а затем задавал здесь после просмотра похожих вопросов.
В моем представлении 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
TextBlock
, связанный с StatusMessage
, обновляется и отображается правильно, но ProgressBar
, связанный с StatusProgress
нет и остается пустым. Если я установлю точку останова в обработчике событий, я увижу, что свойство StatusProgress
правильно обновляется в различных значениях от 0 до 100, но это не отражается на ProgressBar
.
Мне пришла в голову идея выполнения обработчика событий в другом потоке, который часто вызывает проблемы с обновлением пользовательского интерфейса, но почему один элемент пользовательского интерфейса обновляется правильно, а другой - нет?
ПРИМЕЧАНИЕ: Я был монументально глупо и не тестировал ProgressBar
статически, то есть просто установите StatusProgress
модели представления в значение и получите окно оболочки для отображения, не пройдя через цикл загрузки. Если я это сделаю, индикатор выполнения покажет длину, которая более или менее соответствует его свойству Value
. Ни одно из предложений по изменению макета, сделанных в комментариях или ответах, не меняет этого. Статически оно всегда видно и всегда отображает значение.
ПРИМЕР: Я создал небольшой пример, который, кажется, дублирует проблему. В этом примере индикатор выполнения не обновляется до тех пор, пока не будет выполнено ожидаемое задание, и я полагаю, что это касается моего основного вопроса, но это была долгая загрузка, и я не дождался ее завершения, прежде чем заметил индикатор выполнения не обновлялся.
Вот StatusBar
в `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>
С кодом в 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
не завершит свой цикл, а затем индикатор выполнения не показывает полный прогресс, т.е. завершен.