【问题标题】:How to update property value in model/service when XAML entry element is changed?更改 XAML 条目元素时如何更新模型/服务中的属性值?
【发布时间】:2021-09-29 08:01:49
【问题描述】:

Xamarin 表单和 MVVM 的新手-如果我想到这个错误,请原谅我。

我的应用程序中的文件包括:

  • 模型(保存我创建的库中对象的自动属性)
  • 视图(每个元素都被初始化为默认值,用户可以选择更改这些值。每个元素都绑定到它在视图模型中的相对属性。)
  • View-model(包含绑定属性,使用调用 INotifyPropertyChanged 方法 OnPropertyChanged 的​​ SetProperty 方法对每个属性进行适当设置。
  • 界面(包含我的服务的签名)
  • 服务(构造函数为我的模型设置默认值。接口中签名的具体实现。提供从视图模型访问模型属性的方法)

从视图绑定到视图模型很好。问题是,我无法弄清楚如何从我的视图模型中更新模型中的相应属性。如果我理解正确,SetProperty 方法以发布-订阅方式与视图一起工作——允许视图通知任何更改并相应地更新。但是,我是否也使用此方法来更新我的服务中的属性?我尝试创建一个单独的方法,与 SetProperty 方法同时调用以更新模型属性值,但这导致 SetProperty 方法没有被调用。我考虑过实现一个命令,但选择器没有绑定到一个命令。欢迎任何建议!简化代码如下:

型号:

 public class Model
  {
    public Device ModelDevice { get; set; }
  }

查看:

<RefreshView x:DataType="local:ConfigViewModel" Command="{Binding LoadModelCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
    <ScrollView Padding="10">
        <StackLayout>
            <Label Text="Address Type" />
            <Picker ItemsSource="{Binding AddressTypes}" SelectedItem="{Binding AddressType, Mode=TwoWay}">
                <Picker.Items></Picker.Items>
            </Picker>
        </StackLayout>
    </ScrollView>
</RefreshView>

视图模型:

private AddressType _myAddressType;

public AddressType MyAddressType;
    {
      get => _myAddressType;
      set => SetProperty(ref _myAddressType, value);      
    }

private ObservableCollection<AddressType> _myAddressTypes;

public ObservableCollection<AddressType> MyAddressTypes;
    {
      get => _myAddressTypes;
      set => SetProperty(ref _myAddressTypes, value);
    }

 public Command LoadModelCommand { get; }

 public ConfigViewModel()
    {
      Title = "Configuration";

      LoadModelCommand = new Command(async () => await ExecuteLoadModelCommand());
    }

async Task ExecuteLoadModelCommand()
    {
      IsBusy = true;

      try
      {
        //The service is injected into my BaseViewModel, making it accessible here
          var model = await Service.GetModelAsync();

        // ----- Device Properties
        MyAddressTypes = model.ModelDevice.AddressTypes;
        MyAddressType = model.ModelDevice.AddressType;

      }
      catch (TaskCanceledException tcex)
      {
        Debug.WriteLine(tcex);
      }
      finally
      {
        IsBusy = false;
      }
    }

服务:

public class Service : IService
  {
    private Model _aModel;

    public Service()
    {
      //Default View Values
      _aModel = new Model {

        ModelDevice = new Device("192.168.0.000"),
        
      };

      // ----- Device Properties
      _aModel.ModelDevice.Label = "L18";
      _aModel.ModelDevice.Simulate = false;
      _aModel.ModelDevice.AddressType = MOD_6_DIGIT;

      //----- Device Address type
      foreach (var addressType in Enum.GetValues(typeof(MBClient.AddressType)))
        __aModel.ModelDeviceAddressTypes.Add((AddressType)addressType);   
    }


    public async Task<Model> GetModelAsync()
    {
      return await Task.FromResult(_aModel);
    }

【问题讨论】:

  • 通常会有某种动作——比如用户选择“保存”或“下一步”,您可以在其中使用 VM 属性更新您的模型,然后保存它。但是没有一种方法可以做到这一点,这取决于您的应用是如何设计和实现的
  • @Jason 我最终调用了我的异步任务以从我的 VM 中的一个方法更新模型,并从属性设置器中调用该方法。但我很感激你的建议!

标签: c# xaml xamarin.forms mvvm dependency-injection


【解决方案1】:

(大概是您问的是如何将选择器值传递回模型/服务,而不是一般的 MVVM 问题)

解决方案 A

从 SelectedIndexChanged 事件中调用命令。

ConfigView.xaml

<Picker SelectedIndexChanged="Picker_SelectedIndexChanged"
        ... />

ConfigView.xaml.cs

    private async void Picker_SelectedIndexChanged(object sender, System.EventArgs e)
    {
        var vm = BindingContext as ConfigViewModel;
        var selectedModel = (sender as Picker).SelectedItem;
        await vm.ExecuteUpdateModelCommand(selectedModel);
    }

查看模型

    private async Task ExecuteUpdateModelCommand(Model model)
    {
        await Service.UpdateModelAsync(model);
    }

服务

    //Update method A
    public async Task UpdateModelAsync(Model model)
    {
        _aModel = model;
    }

解决方案 B

在属性设置器中调用方法。

查看模型

    private AddressType _myAddressType;
    public AddressType MyAddressType
    {
        get => _myAddressType;
        set
        {
            _myAddressType = value;
            await Service.UpdateAddressTypeAsync(_myAddressType);
            OnPropertyChanged();
        }
    }

服务

    //Update method B
    public async Task UpdateAddressTypeAsync(String type)
    {
        _aModel.ModelDevice.AddressType = type;
    }

【讨论】:

  • 我最终采用了解决方案 B 的修改路线并使其工作。这是我昨天尝试过的,但我煎熬的一天结束的大脑没有意识到我必须切换呼叫顺序。我没有调用异步任务,因为我不确定是否允许?还是好的做法?尽管如此,我还是在 VM 的属性设置器中调用了一个方法,该方法在服务中调用了我的异步任务。谢谢你的建议,绝对让我走上正轨。
  • @MelanieMarkarian 实际上,我也不太确定在那里调用 async 。通常,我会有一个按钮来调用服务,而不是更改选择器选择。或者,可以试试 XCT 的eventtocommandbehavior,或者自己写一个。
  • 我的问题中“属性值”一词背后的原因是因为我特别想更改服务中属性的值。选择器只是一个例子,我的页面有很多元素(主要是条目、复选框和一些选择器),当更改时,需要更新我的服务中的属性值,以最终更改模型中的相应值。不确定是否是您建议进行编辑,但如果您这样做了,这就是我不接受的原因!不过,我很感谢您的反馈 :)
  • @MelanieMarkarian 是的,我提出了建议,现在我明白你的意思了。我只是想更好地用代码或例子来说话。那么这些属性是否全部来自您的“模型”(可能未在此处列出),或者哪些属性应该在您的服务中更新?我在解决方案 B 中简化的一件事是,我没有在 setter 中创建另一个模型实例并更新所有属性,而是将 type 更新为选择器示例。
  • 此外,您的选择器示例也有点不同,与常规情况有一点,只要选择更改,就像有条目和复选框一样的其他元素,则应有一个“保存/提交”按钮这页纸。这就是为什么我在考虑一个不同的问题,比如一个关于 MVVM 中的一般属性更新的问题,以及一个关于如何在选择更改时更新选择器属性的问题。
猜你喜欢
  • 2015-04-06
  • 2014-07-27
  • 1970-01-01
  • 2012-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-15
相关资源
最近更新 更多