Xamarin UWP 似乎绑定到错误的视图模型

Posted

技术标签:

【中文标题】Xamarin UWP 似乎绑定到错误的视图模型【英文标题】:Xamarin UWP seems to bind to the wrong view model 【发布时间】:2020-10-20 22:24:07 【问题描述】:

我有一个适用于 android 和 UWP 的 Xamarin 项目。这个问题似乎只发生在 UWP 上。

在我的 Xamarin 项目中,我有 ContentPage 与绑定为上下文的视图模型。在这个 ViewModel 中有一个 ObservableCollection 和另一种视图模型。当我创建这个底层 ViewModel 的新实例并添加到我的 ObservableCollection 时,有时ContentPage 会按预期工作,在我的 ListView 中显示一个项目。但有时会添加一个空元素,将鼠标悬停在列表上时可以看到。发生这种情况时,我会在“输出”选项卡中收到一堆警告。

我的DownloadsPage

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:local="clr-namespace:Downloader.Converters"
             mc:Ignorable="d"
             x:Class="Downloader.Views.DownloadsPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <local:DownloadStatusToColorConverter x:Key="downloadStatusToColor" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <RefreshView IsRefreshing="Binding IsBusy, Mode=TwoWay" Command="Binding LoadItemsCommand">
        <ListView x:Name="DownloadsListView" SelectionMode="None" ItemsSource="Binding Downloads" RowHeight="70">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Padding="10" BackgroundColor="Binding DownloadStatus, Converter=StaticResource downloadStatusToColor">
                            <Label Text="Binding Name"
                                   d:Text="Binding ."
                                   LineBreakMode="NoWrap"
                                   Style="DynamicResource ListItemTextStyle"
                                   FontSize="16" />
                            <Grid Grid.Row="0" Grid.Column="0" Padding="10,0,10,0">
                                <ProgressBar BackgroundColor="Transparent"  Progress="Binding PercentDownloaded" HorizontalOptions="FillAndExpand" HeightRequest="20">
                                </ProgressBar>
                                <Label Text="Binding PercentString" HorizontalTextAlignment="Center"></Label>
                            </Grid>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </RefreshView>

</ContentPage>

DownloadsViewModel 在代码隐藏中设置为上下文,如下所示:

public partial class DownloadsPage : ContentPage

    private readonly DownloadsViewModel _viewModel;

    public DownloadsPage()
    
        InitializeComponent();

        BindingContext = _viewModel = new DownloadsViewModel();

        Device.StartTimer(TimeSpan.FromSeconds(1), () =>
        
            Device.BeginInvokeOnMainThread(() => _viewModel.RefreshDownloads());
            return true;
        );
    

绑定DownloadsViewModel

public class DownloadsViewModel : BaseViewModel

    public ObservableCollection<DownloadViewModel> Downloads  get; set;  = new ObservableCollection<DownloadViewModel>();
 
    public Command LoadItemsCommand  get; set; 

    public DownloadsViewModel()
    
        Title = "Downloads";
        LoadItemsCommand = new Command(() => 
            IsBusy = true;
            Downloads.Clear();
            RefreshDownloads();
            IsBusy = false;
        );
    

    public void RefreshDownloads()
    
        foreach (var download in DownloadManager.GetDownloads())
        
            var existingDownload = Downloads.FirstOrDefault(d => d.Id == download.Id);

            if (existingDownload != null)
            
                existingDownload.UpdateValues(download);
            
            else
            
                Downloads.Add(new DownloadViewModel(download));
            
        
    

ObservableCollection 包含 DownloadViewModel,看起来像这样:

public class DownloadViewModel : BaseViewModel

    private IDownload _download;
    public DownloadViewModel(IDownload download)
    
        UpdateValues(download);
    

    private string _id;
    public string Id
    
        get  return _id; 
        set  SetProperty(ref _id, value); 
    

    private string _name;
    public string Name
    
        get  return _name; 
        set  SetProperty(ref _name, value); 
    

    private DownloadStatus _status;
    public DownloadStatus DownloadStatus
    
        get  return _status; 
        set  SetProperty(ref _status, value); 
    

    public double PercentDownloaded
    
        get
        
            return _download.DownloadedBytes == -1
                ? 0f
                : (double)_download.DownloadedBytes / _download.TotalBytes;
        
    

    public string PercentString  get => $"(int)(PercentDownloaded * 100) %"; 

    public void UpdateValues(IDownload download)
    
        _download = download;
        Id = _download.Id;
        Name = _download.Name;
        DownloadStatus = _download.Status;
    

我有时会遇到导致 ListView 中的项目为空的错误:

Binding: 'DownloadStatus' property not found on 'Downloader.ViewModels.DownloadsViewModel', target property: 'Xamarin.Forms.StackLayout.BackgroundColor'
Binding: 'Name' property not found on 'Downloader.ViewModels.DownloadsViewModel', target property: 'Xamarin.Forms.Label.Text'
Binding: 'PercentDownloaded' property not found on 'Downloader.ViewModels.DownloadsViewModel', target property: 'Xamarin.Forms.ProgressBar.Progress'
Binding: 'PercentString' property not found on 'Downloader.ViewModels.DownloadsViewModel', target property: 'Xamarin.Forms.Label.Text'

在调试时,我已确认该项目已按预期添加到我的 ObservableCollection 中。

为什么有时它会在 DownloadsViewModel 而不是 DownloadViewModel 上查找 DownloadStatus、Name、PercentDownloaded 和 PercentString?

【问题讨论】:

你能分享一下你是如何设置页面BindingContext的吗? @NicoZhu-MSFT - 我编辑包括如何将 DownloadsViewModel 设置为绑定上下文。 你的代码看起来是正确的,我们无法用上面的方法重现这个问题,你能不能用 onedrive 或 github 为我们分享一个代码示例? @NicoZhu-MSFT 好的,我已经创建了一个清理项目,我可以在其中始终如一地重现它,但它现在似乎永远无法工作。我想在它工作之前是我在启动时加载已经创建的下载。当我在运行时创建下载时它不起作用。您可以在此处访问该项目:github.com/OrderlyFashion/Downloader 我已经下班了,明天可以检查一下吗? 【参考方案1】:

Xamarin UWP 似乎绑定到错误的视图模型

我检查了您的代码示例,它按预期工作。但是我发现进度值没有自动更新导致listview项无法显示,我已经更新了IDownload接口添加PercentDownloaded属性。对于测试,它可以在uwp平台上运行。

问题在于 ViewModel 没有为所有属性实现 INotifyPropertyChanged 的​​设置器。源代码在on Github,修复问题的提交是this one。

【讨论】:

如果您收到了上述项目,请告诉我们,为安全起见,我们需要及时删除。 我已经下载了代码,稍后再看。谢谢! 好的,请告诉我测试结果。 您的修复有效。谢谢!我不确定我是否完全理解问题所在。只需确保始终对 ViewModel 中的所有属性使用带有 INotifyPropertyChanged 的​​设置器?至少对我来说,错误消息肯定会让我感到困惑。 当然。我只是认为本着 *** 的精神,我们应该弄清楚实际的修复是什么。所以我已经将你的更改推送到 Github,我想我会离开这个项目。我会编辑你的答案以明确这一点。

以上是关于Xamarin UWP 似乎绑定到错误的视图模型的主要内容,如果未能解决你的问题,请参考以下文章

错误 UWP 与 mvvmcross + xamarin 形式

UWP Xamarin 中的 Oxyplot 未更新

Xamarin Forms Switch Toggled 事件未与视图模型绑定

在 MAUI 中将命令绑定到父视图模型

Xamarin 从视图模型传递列表数据以进行视图绑定

Xamarin 绑定到列表视图项模板中的另一个元素