从视图模型更新模型

时间:2014-01-01 16:53:47

标签: c# wpf mvvm mvvm-light

从视图模型

更新模型

我已经阅读了一些关于MVVM的帖子,但我不确定是否理解 视图模型更新模型的方式

目前,我在UI中有两个文本框,它们绑定到XAML视图,并在引发事件时调用视图模型。 当我更新模型时应该在视图模型中的哪个位置?

这是视图模型

class ViewModel:INotifyPropertyChanged
    {

 private String _url;
 private String _TemplateType;

    public string URL
    {
    get { return _url;  }
        set
        {
            if (value != _url)
            {
                _url= value;
                OnPropertyChanged("URL");
            }
        }
    }

    public string TemplateType
    {
    get { return _TemplateType;  }
        set
        {
            if (value != _TemplateType)
            {
                _TemplateType= value;
                OnPropertyChanged("URL");
            }
        }
    }

模型

internal class DefineAddinModel
{
    public string TemplateType { get; set; }
    public String URL { get; set; }
}

4 个答案:

答案 0 :(得分:5)

ViewModel通常充当模型的包装器,并包含对Model的引用,该引用可以响应命令更新,也可以在属性设置器中自动更新。

更新: 这是一个让VM充当模型包装器的例子。在您的示例中,这似乎无用,但在很多情况下,您会发现VM的getter / setter需要对值进行某种转换,而不是简单地传递它们。

class ViewModel:INotifyPropertyChanged
{
 private DefineAddinModel model;

    public string URL
    {
        get { return model.URL;  }
        set
        {
            if (value != model.URL)
            {
                model.url = value;
                OnPropertyChanged("URL");
            }
        }
    }

    public string TemplateType
    {
        get { return model.TemplateType;  }
        set
        {
            if (value != model.TemplateType)
            {
                model.TemplateType = value;
                OnPropertyChanged("TemplateType");
            }
        }
    }

答案 1 :(得分:1)

更新模型的更好方法是使用一个事件,它更安全,所以选择天气使用按钮点击或失去焦点,或任何你想要的

void button_click(object sender,eventsarg e)
{
    MyObj.URL = App.Locator.MyVM.MyDefineAddinModel.URL;// App.Locator because MVVMLight is tagged
    MyObj.TemplateType = App.Locator.MyVM.MyDefineAddinModel.TemplateType ;

}

但是personnaly我使用以下步骤:

1 - 在ViewModel中创建CurrentItem类型为DefineAddinModel的对象,而不使用OnPropertyChanged,然后将其绑定到View上RootElement的View(UI)DataContext

模型的

2 - 我为每个属性使用INotifyPropertyChanged

将根元素的datacontext绑定到ViewModel的CurrentItem之后

3 - 然后只将URLTemplateType属性绑定到您的控件,所以任何事情都会改变文本框将更新CurrentItem属性

你也可以选择绑定的类型(On LostFocus,或OnPropertyChanged)

答案 2 :(得分:1)

您需要将TextBox绑定到两个属性URLTemplateType。 因为您在Commands,所以请尝试使用ViewModel(在events)而不是MVVM(在CodeBehind中)。 要更新模型:使用一个按钮,将其Command属性绑定到OnSave,就像这个例子一样:

    private String _url;
    private String _TemplateType;
    private DefineAddinModel _defineAddin;

    public DefineAddinModel DefineAddin
    {
        get {return _defineAddin;}
        set
        { 
            _defineAddin = value;
            OnPropertyChanged("DefineAddin");
        }
    }

    public string URL
    {
        get { return _url;  }
        set
            {
                if (value != _url)
                {
                    _url= value;
                    OnPropertyChanged("URL");
                }
            }
    }

    public string TemplateType
    {
        get { return _TemplateType;  }
        set
        {
            if (value != _TemplateType)
            {
                _TemplateType= value;
                OnPropertyChanged("URL");
            }
        }
    }

    public RelayCommand OnSaved
    {
            get;
            set;
    }


    public ViewModel()
    {
        DefineAddin = new DefineAddinModel();
            OnSaved = new RelayCommand(()=>
                              {
                                    DefineAddin.URL  = URL  ;
                                    DefineAddin.TemplateType = TemplateType;
                              });

考虑使用像MVVMLight这样的第三方它可以帮助你很多MVVM及其周围的助手(Commands,Messenger,ViewModelLocator ......)

答案 3 :(得分:1)

我认为这里的正确答案是'它取决于'。

在大多数情况下,实际使用ViewModel的优势还在于跟踪“瞬态”,即“正在编辑”操作的状态。

在这种特殊情况下,每次更新值时,都会直接将更改推送到模型,而是通过“更新”ICommand实现来执行此操作,该实现将收集所有数据从ViewModel中将其推送到Model。

这种方法为您提供了许多优势:

  1. 视图的用户可以根据需要多次改变主意,只有当他们感到满意时,模型才能真正获得最终选择的更新
  2. 它大大减少了持久性服务的负担,因为只会推送最终更改。
  3. 它允许您对一组完整的值进行最终验证,而不是瞬态,从而降低编程复杂性和开销。
  4. 它还使您的UI变得更加流畅,因为上面的所有示例都是在UI Dispatcher上推送更新,并且避免您必须通过Tasks或其他异步方法来满足这一要求。
  5. 支持模型永远不会处于不一致状态,因为我认为一个View / ViewModel上的所有值都是相关的,只有在使用ACID方法一起更新时才有意义。
  6. 这是我如何做的一个例子。

    public class ViewModel:INotifyPropertyChanged {
    
        private String _url;
        private String _TemplateType;
    
        public ViewModel(){
            UpdateCommand = new DelegateCommand(OnExecuteUpdate, OnCanExecuteUpdate);
        }
    
        public bool OnCanExecuteUpdate(object param){
            // insert logic here to return true when one can update
            // or false when data is incomplete
        }
    
        public void OnExecuteUpdate(object param){
            // insert logic here to update your model using data from the view model
        }
    
        public ICommand UpdateCommand { get; set;}
    
        public string URL{
            get { return _url;  }
            set {
                if (value != _url) {
                    _url= value;
                    OnPropertyChanged("URL");
                }
            }
        }
    
        public string TemplateType {
            get { return _TemplateType;  }
            set {
                if (value != _TemplateType) {
                    _TemplateType= value;
                    OnPropertyChanged("TemplateType");
                }
            }
        }
    
        ... etc.
    }
    
    public class DelegateCommand : ICommand {
        Func<object, bool> canExecute;
        Action<object> executeAction;
    
        public DelegateCommand(Action<object> executeAction)
            : this(executeAction, null) {}
    
        public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute) {
            if (executeAction == null) {
                throw new ArgumentNullException("executeAction");
            }
            this.executeAction = executeAction;
            this.canExecute = canExecute;
        }
    
        public bool CanExecute(object parameter) {
            bool result = true;
            Func<object, bool> canExecuteHandler = this.canExecute;
            if (canExecuteHandler != null) {
                result = canExecuteHandler(parameter);
            }
    
            return result;
        }
    
        public event EventHandler CanExecuteChanged;
    
        public void RaiseCanExecuteChanged() {
            EventHandler handler = this.CanExecuteChanged;
            if (handler != null) {
                handler(this, new EventArgs());
            }
        }
    
        public void Execute(object parameter) {
            this.executeAction(parameter);
        }
    }