如何将加载动画和完成复选标记添加到 MVVM 中的绑定可观察集合?
How to add a loading animation and finished check mark to a bound observable collection in MVVM?
我有一个正在显示的文件列表,其中有一个用于输入文件名的文本框、一个可以让您 select 从文件资源管理器中访问一个文件的按钮,以及一个文本更新指示器,让用户知道文件何时正在加载以及何时完成加载。我想将文本指示器更改为 gif/jpg 指示器。我希望在文件开始加载时出现一个加载指示器,然后在文件加载完成时出现一个复选标记。我一直无法找到有关如何将 images/gifs 绑定到列表视图的任何信息。这可能吗?如果可以,该怎么做?有没有更好的方法来尝试处理此功能?
型号
using AuditEfficiencyMVVM.Helpers;
using AuditEfficiencyMVVM.Sources;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AuditEfficiencyMVVM.Model
{
public class File : INotifyPropertyChanged
{
private Enums.FileType _type;
private string _location;
private SourceBase _source;
private Enums.FileLoadStatus _fileLoadStatus;
private List<Enums.TestType> _tests = new List<Enums.TestType>();
public Enums.FileType Type
{
get
{
return _type;
}
set
{
if (_type != value)
{
_type = value;
RaisePropertyChanged("Type");
}
}
}
public string Location
{
get
{
return _location;
}
set
{
if (_location != value)
{
_location = value;
RaisePropertyChanged("Location");
}
}
}
public List<Enums.TestType> Tests
{
get
{
return _tests;
}
set
{
if (_tests != value)
{
_tests = value;
}
}
}
public SourceBase Source
{
get
{
return _source;
}
set
{
if (_source != value)
{
_source = value;
}
}
}
public Enums.FileLoadStatus FileLoadStatus {
get
{
return _fileLoadStatus;
}
set
{
if (_fileLoadStatus != value)
{
_fileLoadStatus = value;
RaisePropertyChanged("FileLoadStatus");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
视图模型
// Populate the List of Files on startup
public void LoadFiles()
{
ObservableCollection<Model.File> files = new ObservableCollection<Model.File>();
foreach (Model.Test test in IncludedTests)
{
foreach (Enums.FileType type in test.ExpectedSources)
{
Boolean containsType = false;
foreach (Model.File file in files)
{
if (file.Type == type)
{
containsType = true;
break;
}
}
if (!containsType)
{
files.Add(new Model.File { Type = type, Location = "", Source = null, FileLoadStatus = Enums.FileLoadStatus.NotStarted, Tests = new List<Enums.TestType> { test.Type } });
}
else
{
files.Where(t => t.Type == type).First().Tests.Add(test.Type);
}
}
}
Files = files;
}
// Logic to load the files on a button press
private async Task<SortedList<Enums.FileType, DataTable>> LoadFilesAsync()
{
try
{
SortedList<Enums.FileType, DataTable> fileList = new SortedList<Enums.FileType, DataTable>();
foreach (var file in Files)
{
// I want this to be something like LoadStatus = spinner.gif
file.FileLoadStatus = Enums.FileLoadStatus.InProgress;
fileList.Add(file.Type, await file.Source.LoadRecords(file));
// I want this to be something like LoadStatus = checkmark.jpg
file.FileLoadStatus = Enums.FileLoadStatus.Completed;
}
return fileList;
}
catch (Exception)
{
return null;
}
}
查看
<ListView Grid.Row="1" Grid.Column="1" Grid.RowSpan="4" Margin="10" ItemsSource="{Binding Path=Files}">
<ListView.View>
<GridView x:Name="file">
<GridViewColumn Header="File Type" DisplayMemberBinding="{Binding Type, Mode=OneWay}"/>
<GridViewColumn Header="File Location" Width="250">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Location, Mode=TwoWay}" Width="225"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Width="30" Height="20" Command="{Binding Path=DataContext.SelectFileCommand,
RelativeSource={RelativeSource AncestorType=ListView}}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type GridViewRowPresenter}}, Path=DataContext}" Content="..."></Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Load Status" DisplayMemberBinding="{Binding FileLoadStatus, Mode=OneWay}"/>
</GridView>
</ListView.View>
</ListView>
给你的视图模型一个 IsLoading
属性 和一个 IsLoaded
属性,都是布尔值。
为您的视图提供某种在 IsLoading
为真时可见的动画微调器控件,以及在 IsLoaded
为真时可见的某种 "loaded" 标记。我会将微调器叠加在列表视图的顶部:
<Grid>
<ListView ...>
<!-- ...stuff... -->
</ListView>
<Grid
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}"
>
<!-- Spinner stuff -->
</Grid>
</Grid>
在LoadFileAsync()
中适当设置IsLoading
和IsLoaded
。
I have not been able to find anything on how to bind images/gifs to a list view.
你看起来有多努力?将 Image
控件放在单元格模板中,并将其 Source
属性 绑定到行项视图模型的 ImageSource
属性。 This may help。
我发现了 this 教程,这就是我最终实现加载微调器的方法。
注意:我已经从 ListView 更改为 DataGrid 以显示数据,这就是为什么我的视图略有不同。
CircularProgressBar
<UserControl x:Class="AuditEfficiencyMVVM.View.CircularProgressBar"
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:AuditEfficiencyMVVM.View"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Height="Auto" Width="Auto" Background="Transparent"
IsVisibleChanged="HandleVisibleChanged">
<Grid x:Name="LayoutRoot" Background="Transparent"
ToolTip="Searching...."
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Canvas RenderTransformOrigin="0.5,0.5"
HorizontalAlignment="Center"
VerticalAlignment="Center" Width="120"
Height="120" Loaded="HandleLoaded"
Unloaded="HandleUnloaded" >
<Ellipse x:Name="C0" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="1.0"/>
<Ellipse x:Name="C1" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.9"/>
<Ellipse x:Name="C2" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.8"/>
<Ellipse x:Name="C3" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.7"/>
<Ellipse x:Name="C4" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.6"/>
<Ellipse x:Name="C5" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.5"/>
<Ellipse x:Name="C6" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.4"/>
<Ellipse x:Name="C7" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.3"/>
<Ellipse x:Name="C8" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.2"/>
<Canvas.RenderTransform>
<RotateTransform x:Name="SpinnerRotate"
Angle="0" />
</Canvas.RenderTransform>
</Canvas>
</Grid>
</UserControl>
查看
<DataGrid Grid.Row="1" Grid.ColumnSpan="3" Margin="10" ItemsSource="{Binding Path=Files}" SelectedValue="{Binding Path=SelectedUploadFile}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Viewbox Width="25" Height="25"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<local:CircularProgressBar Visibility="{Binding Path=Loading}" />
</Viewbox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
视图模型
public class AuditTests : INotifyPropertyChanged
{
public ObservableCollection<FileMeta> Files { get; set; }
private void AddFileRequested()
{
Files.Add(new FileMeta());
SaveFilesCommand.RaiseCanExecuteChanged();
}
private void RemoveFileRequested()
{
Files.Remove(SelectedUploadFile);
SaveFilesCommand.RaiseCanExecuteChanged();
}
public void InitializeData()
{
Files = new ObservableCollection<FileMeta>() { new FileMeta () };
}
}
文件元
public partial class FileMeta : INotifyPropertyChanged
{
private System.Windows.Visibility _loading;
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public System.Windows.Visibility Loading
{
get
{
return _loading;
}
set
{
if (_loading != value)
{
_loading = value;
RaisePropertyChanged("Loading");
}
}
}
}
我有一个正在显示的文件列表,其中有一个用于输入文件名的文本框、一个可以让您 select 从文件资源管理器中访问一个文件的按钮,以及一个文本更新指示器,让用户知道文件何时正在加载以及何时完成加载。我想将文本指示器更改为 gif/jpg 指示器。我希望在文件开始加载时出现一个加载指示器,然后在文件加载完成时出现一个复选标记。我一直无法找到有关如何将 images/gifs 绑定到列表视图的任何信息。这可能吗?如果可以,该怎么做?有没有更好的方法来尝试处理此功能?
型号
using AuditEfficiencyMVVM.Helpers;
using AuditEfficiencyMVVM.Sources;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AuditEfficiencyMVVM.Model
{
public class File : INotifyPropertyChanged
{
private Enums.FileType _type;
private string _location;
private SourceBase _source;
private Enums.FileLoadStatus _fileLoadStatus;
private List<Enums.TestType> _tests = new List<Enums.TestType>();
public Enums.FileType Type
{
get
{
return _type;
}
set
{
if (_type != value)
{
_type = value;
RaisePropertyChanged("Type");
}
}
}
public string Location
{
get
{
return _location;
}
set
{
if (_location != value)
{
_location = value;
RaisePropertyChanged("Location");
}
}
}
public List<Enums.TestType> Tests
{
get
{
return _tests;
}
set
{
if (_tests != value)
{
_tests = value;
}
}
}
public SourceBase Source
{
get
{
return _source;
}
set
{
if (_source != value)
{
_source = value;
}
}
}
public Enums.FileLoadStatus FileLoadStatus {
get
{
return _fileLoadStatus;
}
set
{
if (_fileLoadStatus != value)
{
_fileLoadStatus = value;
RaisePropertyChanged("FileLoadStatus");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
视图模型
// Populate the List of Files on startup
public void LoadFiles()
{
ObservableCollection<Model.File> files = new ObservableCollection<Model.File>();
foreach (Model.Test test in IncludedTests)
{
foreach (Enums.FileType type in test.ExpectedSources)
{
Boolean containsType = false;
foreach (Model.File file in files)
{
if (file.Type == type)
{
containsType = true;
break;
}
}
if (!containsType)
{
files.Add(new Model.File { Type = type, Location = "", Source = null, FileLoadStatus = Enums.FileLoadStatus.NotStarted, Tests = new List<Enums.TestType> { test.Type } });
}
else
{
files.Where(t => t.Type == type).First().Tests.Add(test.Type);
}
}
}
Files = files;
}
// Logic to load the files on a button press
private async Task<SortedList<Enums.FileType, DataTable>> LoadFilesAsync()
{
try
{
SortedList<Enums.FileType, DataTable> fileList = new SortedList<Enums.FileType, DataTable>();
foreach (var file in Files)
{
// I want this to be something like LoadStatus = spinner.gif
file.FileLoadStatus = Enums.FileLoadStatus.InProgress;
fileList.Add(file.Type, await file.Source.LoadRecords(file));
// I want this to be something like LoadStatus = checkmark.jpg
file.FileLoadStatus = Enums.FileLoadStatus.Completed;
}
return fileList;
}
catch (Exception)
{
return null;
}
}
查看
<ListView Grid.Row="1" Grid.Column="1" Grid.RowSpan="4" Margin="10" ItemsSource="{Binding Path=Files}">
<ListView.View>
<GridView x:Name="file">
<GridViewColumn Header="File Type" DisplayMemberBinding="{Binding Type, Mode=OneWay}"/>
<GridViewColumn Header="File Location" Width="250">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Location, Mode=TwoWay}" Width="225"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Width="30" Height="20" Command="{Binding Path=DataContext.SelectFileCommand,
RelativeSource={RelativeSource AncestorType=ListView}}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type GridViewRowPresenter}}, Path=DataContext}" Content="..."></Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Load Status" DisplayMemberBinding="{Binding FileLoadStatus, Mode=OneWay}"/>
</GridView>
</ListView.View>
</ListView>
给你的视图模型一个 IsLoading
属性 和一个 IsLoaded
属性,都是布尔值。
为您的视图提供某种在 IsLoading
为真时可见的动画微调器控件,以及在 IsLoaded
为真时可见的某种 "loaded" 标记。我会将微调器叠加在列表视图的顶部:
<Grid>
<ListView ...>
<!-- ...stuff... -->
</ListView>
<Grid
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}"
>
<!-- Spinner stuff -->
</Grid>
</Grid>
在LoadFileAsync()
中适当设置IsLoading
和IsLoaded
。
I have not been able to find anything on how to bind images/gifs to a list view.
你看起来有多努力?将 Image
控件放在单元格模板中,并将其 Source
属性 绑定到行项视图模型的 ImageSource
属性。 This may help。
我发现了 this 教程,这就是我最终实现加载微调器的方法。
注意:我已经从 ListView 更改为 DataGrid 以显示数据,这就是为什么我的视图略有不同。
CircularProgressBar
<UserControl x:Class="AuditEfficiencyMVVM.View.CircularProgressBar"
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:AuditEfficiencyMVVM.View"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Height="Auto" Width="Auto" Background="Transparent"
IsVisibleChanged="HandleVisibleChanged">
<Grid x:Name="LayoutRoot" Background="Transparent"
ToolTip="Searching...."
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Canvas RenderTransformOrigin="0.5,0.5"
HorizontalAlignment="Center"
VerticalAlignment="Center" Width="120"
Height="120" Loaded="HandleLoaded"
Unloaded="HandleUnloaded" >
<Ellipse x:Name="C0" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="1.0"/>
<Ellipse x:Name="C1" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.9"/>
<Ellipse x:Name="C2" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.8"/>
<Ellipse x:Name="C3" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.7"/>
<Ellipse x:Name="C4" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.6"/>
<Ellipse x:Name="C5" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.5"/>
<Ellipse x:Name="C6" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.4"/>
<Ellipse x:Name="C7" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.3"/>
<Ellipse x:Name="C8" Width="20" Height="20"
Canvas.Left="0"
Canvas.Top="0" Stretch="Fill"
Fill="Black" Opacity="0.2"/>
<Canvas.RenderTransform>
<RotateTransform x:Name="SpinnerRotate"
Angle="0" />
</Canvas.RenderTransform>
</Canvas>
</Grid>
</UserControl>
查看
<DataGrid Grid.Row="1" Grid.ColumnSpan="3" Margin="10" ItemsSource="{Binding Path=Files}" SelectedValue="{Binding Path=SelectedUploadFile}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Viewbox Width="25" Height="25"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<local:CircularProgressBar Visibility="{Binding Path=Loading}" />
</Viewbox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
视图模型
public class AuditTests : INotifyPropertyChanged
{
public ObservableCollection<FileMeta> Files { get; set; }
private void AddFileRequested()
{
Files.Add(new FileMeta());
SaveFilesCommand.RaiseCanExecuteChanged();
}
private void RemoveFileRequested()
{
Files.Remove(SelectedUploadFile);
SaveFilesCommand.RaiseCanExecuteChanged();
}
public void InitializeData()
{
Files = new ObservableCollection<FileMeta>() { new FileMeta () };
}
}
文件元
public partial class FileMeta : INotifyPropertyChanged
{
private System.Windows.Visibility _loading;
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public System.Windows.Visibility Loading
{
get
{
return _loading;
}
set
{
if (_loading != value)
{
_loading = value;
RaisePropertyChanged("Loading");
}
}
}
}