【问题标题】:How to reflect changes in viewmodel to tableviewcell view with binding in MVVMcross如何通过 MVVMcross 中的绑定将视图模型中的更改反映到 tableviewcell 视图
【发布时间】:2013-03-15 08:33:03
【问题描述】:

当在MvxBindableTableViewCell 中使用时,我有点难以将更改从 ViewModel 反映到 View。我在 iOS 上使用 MvvmCross 的 vNext 分支。

一切都已正确设置,并且在首次加载/显示列表时可以看到初始值。该列表是一个ObservableCollection<T>,ViewModels 继承自MvxViewModel(因此实现了 INotifyPropertyChanged)。

主视图模型如下所示:

public abstract class BaseViewModel : MvxViewModel, IMvxServiceConsumer
{
    //... just regular implementation
}

public class UploadListViewModel: BaseViewModel
{
    private readonly IUploadItemTasks uploadItemTasks;
    private readonly IPhotoPickerService photoPickerService;

    public IObservableCollection<UploadItemViewModel> Uploads { get { return this.LoadUploadItems(); }  }

    public UploadListViewModel()
    {
        this.uploadItemTasks = this.GetService<IUploadItemTasks>();
        this.photoPickerService = this.GetService<IPhotoPickerService>();
    }

    private IObservableCollection<UploadItemViewModel> LoadUploadItems()
    {
        using (var unitOfWork = UnitOfWork.Start ())
        {
            return new SimpleObservableCollection<UploadItemViewModel>(uploadItemTasks.GetAll());
        }
    }

    public void StartUpload ()
    {
        if (this.Uploads == null || this.Uploads.Count == 0) {
            ReportError("Error", "No images to upload");
            return;
        }

        this.Uploads.ForEach (uploadItem => PostCallback (uploadItem));
    }

    private void PostCallback (UploadItemViewModel uploadAsset)
    {
        IProgressReporter progressReporter = uploadAsset;

        this.photoPickerService.GetAssetFullImage(uploadAsset.ImageUrl,
                                                  (image) => {
            UIImage fullImage = image;
            NSData jpeg = fullImage.AsJPEG();

            byte[] jpegBytes = new byte[jpeg.Length];           
            System.Runtime.InteropServices.Marshal.Copy(jpeg.Bytes, jpegBytes, 0, Convert.ToInt32(jpeg.Length));

            MemoryStream stream = new MemoryStream(jpegBytes);
            Uri destinationUrl = new Uri(uploadAsset.DestinationUrl + "&name=" + uploadAsset.Name + "&contentType=image%2FJPEG");

            //TO DO: Move this to plugin
            var uploader = new Uploader().UploadPicture (destinationUrl, stream, UploadComplete, progressReporter);
            uploader.Host = uploadAsset.Host;

            ThreadPool.QueueUserWorkItem (delegate {
                uploader.Upload ();                 
                jpeg = null;
            });
        });
    }

    private void UploadComplete (string name)
    {
        if (name == null){
            ReportError("Error","There was an error uploading the media.");
        } else 
        {
            //ReportError("Succes", name);
        }
    }


项目 ViewModel 看起来像:

public interface IProgressReporter
{
    float Progress { get; set;} 
}

public abstract class BaseAssetViewModel: BaseViewModel, IBaseAssetViewModel
{
    //... just regular properties 
}

public class UploadItemViewModel: BaseAssetViewModel, IProgressReporter
{
    public UploadItemViewModel(): base()
    {
    }

    private float progress;
    public float Progress {
        get {
            return this.progress;
        }
        set {
            this.progress = value;
            this.RaisePropertyChanged(() => Progress);
        }
    }
}


项目的视图继承自 MvxBindableTableViewCell 并具有以下属性:

private float progress;
public float ProgressMarker {
    get {
        return progress;
    }
    set {
        progress = value;
        // change progressbar or textfield here
    }
}

tableviewcell 通过 BindingText 绑定到UploadItemViewModel

public const string BindingText = @"ProgressMarker Progress, Converter=Float;";

UploadListViewModel 的 sn-p 中提到的 Uploader 类实现了一个尝试在 IProgressReporter 上设置进度的私有方法。

    float progressValue;
    void SetProgress (float newvalue)
    {
        progressValue = newvalue;

        this.dispatcher.InvokeOnMainThread (delegate {
            if (ProgressReporter != null)
                ProgressReporter.Progress = progressValue;
        });
    }

在第一次查看列表期间,我可以看到 ViewModel 和 View 中的属性都被击中,但是当我通过接口 IProgressReporter 使用 Progress 中的新值更新 ViewModel 时,tableviewcell 中的 View没有更新,也没有调用该属性。

我做错了什么或者我在这里错过了什么?

更新:检查此问题的答案。

【问题讨论】:

  • 您可以发布更多代码吗?也许是一个要点,包括列表中项目的类以及包含该列表的 ViewModel 的位。另外,我在这里对Mode=TwoWay 位感到困惑 - 只是检查一下我没有误解你在问什么。
  • 我用更多代码更新了我的问题。希望现在更清楚了。我删除了Mode=TwoWay 部分,因为那是一些测试的残余。应该是OneWay;从 ViewModel 到 View。
  • 谢谢 - 我看不出有什么问题。明天我要尝试复习(但时间对我不利)
  • 我能问一件事吗?你能检查单元格的 ProgressMarker set 没有被调用吗?例如给它添加一些痕迹。我能想到的唯一long shotSetNeedsDisplay() 不足以导致重绘(这通常用于自定义Draw 方法?)
  • 我在 ProgressMarker 的set 中添加了一个跟踪,它表明它仅在初始查看期间被调用。我还在UploadItemViewModel 的Progress 的set 中添加了一个跟踪,它得到了很好的更新,但视图没有得到更新。看起来属性更改的传播未完成或绑定不同步?确实,SetNeedsDisplay() 仅用于自定义绘图的重绘,所以我删除了它。

标签: xamarin.ios mvvmcross


【解决方案1】:

我找到了绑定不起作用的原因。我一遍又一遍地替换 ObservableCollection。我按如下所述更改了那段代码,现在它反映了对单元格视图中的 UploadItemViewModel 所做的更改。

    private IObservableCollection<UploadItemViewModel> uploads;
    private IObservableCollection<UploadItemViewModel> LoadUploadItems()
    {
        if (uploads == null)
        {
            using (var unitOfWork = UnitOfWork.Start ())
            {
                uploads = new SimpleObservableCollection<UploadItemViewModel>(uploadItemTasks.FindAll());
            }
        }
        return uploads;
    }

【讨论】:

  • 感谢@Remco 的自我回答!我昨天构建了一个 v3 案例 - 它工作......所以我今天打算尝试 v2......但现在我不需要:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多