【问题标题】:textbox input to get / set viewmodel文本框输入以获取/设置视图模型
【发布时间】:2014-08-19 17:29:37
【问题描述】:

我确定这已经被问过了,但我对 MVVM 和 WPF 还是新手,不太确定我应该搜索什么。

我有一个视图模型,其中包括模型中的项目,以及一些额外的临时数据项目,这些数据项目将全部传递给 process.start()。我有一个文本框的堆栈面板,并希望允许用户输入“ModelName”,如果存在,ViewModel 将获取并设置与 ModelName 关联的“TemplateName”。

我对如何实现这一点有点迷茫。我是否需要创建一个完全独立的 ViewModel,然后从 ModelViewModel 中提取数据?难道我只是在ModelName的集合下写一些代码,可以验证、查询、设置TemplateName吗?

型号:

public partial class Model
   {
    public string ModelName { get; set; }
    public virtual Template Template { get; set; }

和 ViewModel,它接受模型和一些临时数据:

public class LauncherViewModel:ViewModelBase
{        
    public LauncherViewModel()
    {
        _ESTContext = new ESTContext();
        Models = new ObservableCollection<Model>(_ESTContext.Models); 
    }

    private ESTContext _ESTContext;
    private string _modelname;
    private string _serialno;
    private string _sonumber;
    private string _templatename;
    private string _outputname;
    private Model _selectedmodel;

    public ObservableCollection<Model> Models { get; set; }

    public string ModelName
    {
        get { return _modelname; }
        set
        {
            if (!string.Equals(_modelname, value))
            {
                _modelname = value;
            };
        }
    }
    public string TemplateName { get { return _templatename;  }}
    public string SerialNo { get { return _serialno; } }
    public string SONumber { get { return _sonumber; } }
    public string OutputName { get { return _outputname; } }

    public Model SelectedModel
    {
        get { return _selectedmodel; }
        set
        {
            if (_selectedmodel != value)
            {
                _selectedmodel = value;
            }
        }
    }
} 

我的观点:

<DockPanel>
    <StackPanel Margin="0,78,0,68" Width="233" DataContext="{Binding Models}">
        <ComboBox IsEditable="True" Text="{Binding ModelName}" SelectedItem="{Binding SelectedModel}"/>
        <TextBox Height="23" TextWrapping="Wrap" Text="{Binding SONumber}"/>
        <TextBox Height="23" TextWrapping="Wrap" Text="{Binding SerialNumber}"/>
        <Button Content="Button"/>
    </StackPanel>
</DockPanel>

【问题讨论】:

    标签: c# wpf mvvm viewmodel


    【解决方案1】:

    要更新您的 gui,您必须实现 INotifyPropertyChanged 并在所有绑定属性上调用它。

    // basic base class for your models, you a
    public class ModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator] // remove if you are not using R#
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
    // your model
    public class Model : ModelBase
    {
        private string modelName;
        private Template template;
        public string ModelName
        {
            get { return modelName; }
            set
            {
                if (value == modelName) return;
                modelName = value;
                OnPropertyChanged();
            }
        }
        public virtual Template Template
        {
            get { return template; }
            set
            {
                if (Equals(value, template)) return;
                template = value;
                OnPropertyChanged();
            }
        }
    }
    

    查看:

    <DockPanel>
        <StackPanel Margin="0,78,0,68" Width="233"> 
        <ComboBox IsEditable="True" Text="{Binding ModelName, Mode='TwoWay'}" SelectedItem="{Binding SelectedModel}" ItemsSource="{Binding Models}"/>
        <TextBox Height="23" TextWrapping="Wrap" Text="{Binding SONumber Mode='TwoWay'}"/>
        <TextBox Height="23" TextWrapping="Wrap" Text="{Binding SerialNumber Mode='TwoWay'}"/>
        <Button Content="Button"/>
    </StackPanel>
    

    注意 Mode='TwoWay' 这会使 GUI 更改视图模型中的值,而不仅仅是显示它们。所以你需要在上面应该可以编辑的所有东西上设置它。 WPF 的默认行为是,当控件失去焦点时,它将更新绑定的属性,如果您设置 UpdateSourceTrigger='PropertyChanged',则每次在文本框中输入一个字母时都会更新该属性。我会把这部分留给你,但你必须在你的虚拟机中完成!至少属性 SONumber、SerialNumber 和 Models(如果它的 ref. 更改)。

    VM:我假设你在这里使用 galasoft

    public class LauncherViewModel : ViewModelBase
    {
        private ESTContext _ESTContext;
        private string _templatename;
        private string _modelname;
        private string serialNumber;
        private string _outputname;
        private string modelName;
        private ObservableCollection<Model> models;
        private Model selectedModel;
        private string soNumber;
    
        public LauncherViewModel()
        {
            // dangerous ;)
            _ESTContext = new ESTContext();
            Models = new ObservableCollection<Model>(_ESTContext.Models);
        }
    
    
        public ObservableCollection<Model> Models
        {
            get { return models; }
            set
            {
                if (Equals(value, models)) return;
                models = value;
                RaisePropertyChanged();
            }
        }
    
        public string ModelName
        {
            get { return modelName; }
            set
            {
                if (value == modelName) return;
                modelName = value;
                RaisePropertyChanged();
            }
        }
        public string TemplateName { get { return _templatename;  }}
        public string SerialNumber // Note you spelled this wrong in your xaml. SONumber
        {
            get { return serialNumber; }
            set
            {
                if (value == serialNumber) return;
                serialNumber = value;
                RaisePropertyChanged();
            }
        }
    
        public string SONumber
        {
            get { return soNumber; }
            set
            {
                if (value == soNumber) return;
                soNumber = value;
                RaisePropertyChanged();
            }
        }
    
        public string OutputName { get { return _outputname; } }
    
        public Model SelectedModel
        {
            get { return selectedModel; }
            set
            {
                if (Equals(value, selectedModel)) return;
                selectedModel = value;
                RaisePropertyChanged();
            }
        }
    }
    

    如果您将 galasoft 或 sim 与 R# 一起使用,这是一件很酷的小事。

    public class YourViewModelBase : ViewModelBase
    {
        [NotifyPropertyChangedInvocator] // alt + enter = convert auto property to prop  
                                         // with backing field and change notification :)
        override protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            base.RaisePropertyChanged(propertyName);
        }
        // .. :)
    }
    

    干杯,

    斯蒂安

    【讨论】:

    • 我尝试了你的建议,并创建了一个文本框来显示 SelectedModel 属性,但文本框没有填充。我没有使用 GalaSoft,而是创建了自己的,看起来很相似。有没有办法可以查看 RaiseProperties 是否正在触发?我没有在我的模型上设置 raiseproperty,因为它在一个单独的项目中,但我在我的 ViewModel 类中的“SelectedModel”上有它。
    • @chickenricekid 确保在模型设置器中设置断点,然后跟踪代码并验证处理程序不为空并且事件 (PropertyChanged) 触发!唷,那是一个长句子:) GL
    • @chickenricekid 如果您在 cbox 绑定中设置 DisplayMemberPath="{Binding ModelName, Mode='TwoWay'}" 会发生什么?
    • 我将文本框设置为 ,它工作正常。但是当我尝试做 那是它不合作的时候。
    • 没关系,想通了 必须使用 DataContext 而不是将其全部绑定在 Text 字段中。感谢您的所有帮助
    【解决方案2】:

    如果我没有误解您的问题,请将模型绑定到 ComboBox 的 ItemsSource 而不是将其绑定到 StackPanel 的 DataContext

    移除 DataContext 绑定

    <StackPanel Margin="0,78,0,68" Width="233" >
    

    将 ComboBox 的 Itemssource 绑定到模型,您还必须将 DisplayMemberPath 指定为要在 ComboBox 中显示的模型的属性。

    <ComboBox IsEditable="True" ItemsSource="{Binding Models}" Text="{Binding ModelName}" SelectedItem="{Binding SelectedModel}"/>
    

    我假设您将窗口的 DataContext 设置为 LauncherViewModel 类的实例。

    【讨论】:

      【解决方案3】:

      您需要在绑定到的属性上引发属性更改事件,即:

      private string _ModelName;
          public string ModelName
          {
              get { return _ModelName; }
              set
              {
                  if (_ModelName != value)
                  {
                      _ModelName = value;
                      RaisePropertyChanged("ModelName");
                  }
              }
          }
      

      在您的基本视图模型中,您需要这样的东西(确保实现 INotifyPropertyChanged):

           public event PropertyChangedEventHandler PropertyChanged;
      
          protected void RaisePropertyChanged(string propertyName)
          {
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
          }
      

      在视图的构造函数或加载事件中添加:

      DataContext = new LauncherViewModel();
      

      【讨论】:

      • 对不起,没有提到,但我已经有属性更改事件;省略了它们,因为我认为这不是我问题的重点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-07
      • 1970-01-01
      相关资源
      最近更新 更多