简体   繁体   English

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

[英]Xamarin UWP seems to bind to the wrong view model

I have a Xamarin project for Android and UWP.我有一个 Xamarin 项目,用于 Android 和 UWP。 This issue seems to only happen on UWP.这个问题似乎只发生在 UWP 上。

In my Xamarin project I have ContentPage with a view model bound as context.在我的 Xamarin 项目中,我将ContentPage与视图 model 绑定为上下文。 In this ViewModel there's an ObservableCollection with another kind of view model.在这个 ViewModel 中有一个带有另一种视图 model 的ObservableCollection When I create a new instance of this underlying ViewModel and add to my ObservableCollection, sometimes the ContentPage works as expected, showing an item in my ListView.当我创建这个底层 ViewModel 的新实例并添加到我的 ObservableCollection 时,有时ContentPage会按预期工作,在我的 ListView 中显示一个项目。 But sometimes there's an empty element added, that I can see when hovering over the list.但有时会添加一个空元素,将鼠标悬停在列表上时可以看到。 When this happens I get a bunch of warnings in the Output tab.发生这种情况时,我会在 Output 选项卡中收到一堆警告。

My DownloadsPage :我的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 is set as context in the code-behind like this: 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;
        });
    }
}

The bound DownloadsViewModel :绑定的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));
            }
        }
    }
}

And the ObservableCollection contains DownloadViewModel that looks like this: 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;
    }
}

The error I sometimes get which causes items in my ListView to be empty:我有时遇到的错误会导致我的 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'

悬停时看到的幽灵物品

When debugging I've confirmed that the item is added to my ObservableCollection as expcted.调试时,我已确认该项目已按预期添加到我的 ObservableCollection 中。

How come sometimes it's looking for DownloadStatus, Name, PercentDownloaded and PercentString on DownloadsViewModel instead of DownloadViewModel?为什么有时它会在 DownloadsViewModel 而不是 DownloadViewModel 上寻找 DownloadStatus、Name、PercentDownloaded 和 PercentString?

Xamarin UWP seems to bind to the wrong view model Xamarin UWP 似乎绑定到错误的视图 model

I checked your code sample and it works as expect.我检查了您的代码示例,它按预期工作。 But I found the progress value does not update automatically that cause the listview item can't display, I have update the IDownload interface add PercentDownloaded property.但是我发现进度值没有自动更新导致listview项无法显示,我已经更新了IDownload接口添加PercentDownloaded属性。 For the testing it could works in uwp platform.对于测试,它可以在 uwp 平台上工作。

The problem was that the ViewModels did not have setters with INotifyPropertyChanged implemented for all properties.问题是 ViewModel 没有为所有属性实现 INotifyPropertyChanged 的设置器。 The source code is available on Github , and the commit that fixes the issue is this one .源代码在 Github 上可用,修复问题的提交就是这个

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM