将值从 View 发送到 ViewModel

Posted

技术标签:

【中文标题】将值从 View 发送到 ViewModel【英文标题】:Sending Values from View To ViewModel 【发布时间】:2011-10-15 07:02:32 【问题描述】:

我这里有一个senario...(非常抱歉写了这么长的帖子)

我有 TreeView(绑定到可观察的电话集合(不同类型))我有一个 contentCONttrol,其内容绑定设置为 TreeView 的 selectedItem

代码....

 <Border Grid.Column="2">
            <ContentControl Name="userControlContentControl"
                            Content="Binding ElementName=PhoneTreeViewUserControl,
                                              Path=SelectedItem,
                                              Converter=StaticResource ResourceKey=rightDisplayConverter">
                <ContentControl.Resources>
                    <DataTemplate DataType="x:Type EntityLayer:Settings">
                        <ViewLayer:SettingsView />
                    </DataTemplate>                       
                    <DataTemplate DataType="x:Type EntityLayer:CandyBarPhones">
                        <ViewLayer:CandyBarPhonesListView />
                    </DataTemplate>                        
                    <DataTemplate DataType="x:Type EntityLayer:CandyBarPhone">
                        <ViewLayer:CandyBarPhoneView />
                    </DataTemplate>
                    <DataTemplate DataType="x:Type EntityLayer:QwertyPhones">
                        <ViewLayer:QwertyPhonesListView />
                    </DataTemplate>
                    <DataTemplate DataType="x:Type EntityLayer:QuertyPhone">
                        <ViewLayer:QuertyPhoneView />
                    </DataTemplate>
                                       </ContentControl.Resources>
            </ContentControl>
        </Border>

问题是,当我从 TreeView 中选择一个电话(从 contentcontrol 填充特定视图)时,我希望将 UniqueId(PhoneBase 具有该属性)传递给我的 ViewModel 并在 viewModel 中触发一个函数,以便它可以得到来自 BusinessLayer 的数据...并初始化 ViewModel 及其所有属性。

用户控件的代码隐藏

区域类 - QuertyPhoneView

/// <summary>
///   Interaction logic for ProductIdEditorView.xaml
/// </summary>
public partial class QuertyPhoneView : BaseUserControl

    QertyPhoneViewModel quertyPhoneViewModel;
    #region Constructor

    /// <summary>
    /// </summary>
    public ProductIdEditorView()
    
        InitializeComponent();
        quertyPhoneViewModel =
            new quertyPhoneViewModel ();
        this.DataContext = quertyPhoneViewModel;
    

    # endregion


#endregion

在 ViewModel 中我也有 Messenger 注册 .... 但是每次我选择另一种电话类型然后选择前一种类型时,信使都会再次注册而不取消注册早先...(我在 Messenger 中没有任何取消注册方法,使用Marlon's Mediator V2) 及其制作应用程序 使用 15-20 分钟左右会非常慢

典型视图的 ViewModel..

区域类 - QuertyPhoneViewModel

/// <summary>
///   QuertyPhoneViewModel
/// </summary>
public class QuertyPhoneViewModel : BaseViewModel

    # region Member Variables

    /// <summary>
    ///   quertyPhoneDetails
    /// </summary>
    private QuertyPhone quertyPhoneDetails;

    /// <summary>
    ///   oldQuertyPhoneDetails
    /// </summary>
    private ProductId oldQuertyPhoneDetails;

    /// <summary>
    ///   productIds
    /// </summary>
    private QuertyPhones quertyPhones;

    /// <summary>
    ///   productIdModel
    /// </summary>
    private readonly QuertyPhoneModal quertyPhoneModal;

    /// <summary>
    ///   log
    /// </summary>
    private static readonly ILog log =
        LogManager.GetLogger(typeof (QuertyPhoneViewModel));

    /// <summary>
    ///   selectedCalTblName
    /// </summary>
    private CalibrationTable selectedCalTblName;

    # endregion

    # region Constructor

    /// <summary>
    ///   QuertyPhoneViewModel
    /// </summary>
    public QuertyPhoneViewModel()
    
        RegisterForMessage();
        quertyPhoneModal= new QuertyPhoneModal();
        if (QuertyPhoneDetails == null)
        
            //Requesting TreeViewUsersontrol To send Details
           // I wish to remove these registrations
            Messenger.NotifyColleagues<ProductId>(
                MessengerMessages.SEND_SELECTED_PHONE, QuertyPhoneDetails);
        
        CancelPhoneDetailsChangeCommand = new RelayCommand(CancelQuertyPhoneDetailsChanges,
                                               obj => this.IsDirty);
        SavePhoneDetailsChangeCommand = new RelayCommand(SaveQuertyPhoneDetailsToTree,
                                                CanSaveQuertyPhoneDetailsToTree);
    

    # endregion

    # region Properties

    /// <summary>
    ///   CalibrationTblNameList
    /// </summary>
    public ObservableCollection<CalibrationTable> CalibrationTblNameList  get; set; 

    /// <summary>
    ///   CancelPhoneDetailsChangeCommand
    /// </summary>
    public ICommand CancelPhoneDetailsChangeCommand  get; set; 

    /// <summary>
    ///   SavePhoneDetailsChangeCommand
    /// </summary>
    public ICommand SavePhoneDetailsChangeCommand  get; set; 

    /// <summary>
    ///   QuertyPhoneDetails
    /// </summary>
    public ProductId QuertyPhoneDetails
    
        get  return quertyPhoneDetails; 
        set
        
            quertyPhoneDetails = value;
            OnPropertyChanged("QuertyPhoneDetails");
        
    

    /// <summary>
    ///   SelectedCalTblName
    /// </summary>
    public CalibrationTable SelectedCalTblName
    
        get  return selectedCalTblName; 
        set
        
            selectedCalTblName = value;
            OnPropertyChanged("SelectedCalTblName");
            if (selectedCalTblName != null)
            
                QuertyPhoneDetails.CalibrationTableId =
                    selectedCalTblName.UniqueId;
            
        
    

    /// <summary>
    ///   QuertyPhones
    /// </summary>
    public QuertyPhones QuertyPhones
    
        get  return productIds; 
        set
        
            productIds = value;
            OnPropertyChanged("QuertyPhones");
        
    

    # endregion

    # region Public Methods

    # endregion

    # region Private Methods

    /// <summary>
    ///   RegisterForMessage
    ///  I wish to remove these registrations too
    /// </summary>
    private void RegisterForMessage()
    
        log.Debug("RegisterForMessage" + BaseModel.FUNCTION_ENTERED_LOG);
        Messenger.Register(MessengerMessages.RECIEVE_SELECTED_PHONE,
                           (Action<ProductId>) (o =>
                                                    
                                                        if (o != null)
                                                        
                                                            QuertyPhoneDetails
                                                                =
                                                                o.Clone() as
                                                                ProductId;
                                                            AttachChangeEvents
                                                                ();
                                                            oldQuertyPhoneDetails
                                                                = o;
                                                            SetCalibrationTables
                                                                ();
                                                        
                                                    ));
        Messenger.Register(MessengerMessages.REFRESH_PHONEDETILAS,
                           (Action<string>)
                           (o =>
                                
                                    GetOldQuertyPhoneDetails(o);
                                    this.IsDirty = false;
                                ));
        log.Debug("RegisterForMessage" + BaseModel.FUNCTION_EXIT_LOG);
    

    /// <summary>
    ///   CanSaveQuertyPhoneDetailsToTree
    /// </summary>
    /// <param name = "obj"></param>
    /// <returns></returns>
    private bool CanSaveQuertyPhoneDetailsToTree(object obj)
    
        return this.IsDirty && ValidateQuertyPhoneDetails();
    

    /// <summary>
    ///   SaveQuertyPhoneDetailsToTree
    /// </summary>
    /// <param name = "obj"></param>
    private void SaveQuertyPhoneDetailsToTree(object obj)
    
        log.Debug("SaveQuertyPhoneDetails" + BaseModel.FUNCTION_ENTERED_LOG);
        if (Utility.IsStringNullOrEmpty(QuertyPhoneDetails.CalibrationTableId))
        
            MessageBoxResult result =
                ShowMessageDialog(
                    "Calibration Table name is not set.Do you wish to proceed?",
                    "Save ProductId",
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Exclamation);
            if (result == MessageBoxResult.Yes)
            
                SaveDetails();
            
        
        else
        
            SaveDetails();
        
        log.Debug("SaveQuertyPhoneDetails" + BaseModel.FUNCTION_EXIT_LOG);
    

    /// <summary>
    ///   SaveDetails
    /// </summary>
    private void SaveDetails()
    
        log.Debug("SaveDetails" + BaseModel.FUNCTION_ENTERED_LOG);
        if (productIdModel.SaveQuertyPhoneDetails(QuertyPhoneDetails))
        
            UpdateProgressbarStatus(
                "ProductId " + QuertyPhoneDetails.Specs
                + " saved successfully.",
                false);
            this.IsDirty = false;
        
        else
        
            ShowMessageDialog(
                "ProductId " + QuertyPhoneDetails.Specs +
                " not saved successfully.",
                "Save ProductId",
                MessageBoxButton.OK,
                MessageBoxImage.Error);
            UpdateProgressbarStatus(
                "ProductId " + QuertyPhoneDetails.Specs
                + " not saved successfully.",
                false);
        
        log.Debug("SaveDetails" + BaseModel.FUNCTION_EXIT_LOG);
    

    /// <summary>
    ///   SetCalibrationTables
    /// </summary>
    private void SetCalibrationTables()
    
        log.Debug("SetCalibrationTables" + BaseModel.FUNCTION_ENTERED_LOG);
        CalibrationTblNameList =
            productIdModel.FileData.CalibrationTables.CalibrationTableList;
        SelectedCalTblName = (CalibrationTblNameList.Where(
            calibrationTable =>
            calibrationTable.UniqueId.Equals(
                QuertyPhoneDetails.CalibrationTableId))).FirstOrDefault();
        log.Debug("SetCalibrationTables" + BaseModel.FUNCTION_EXIT_LOG);
    

    /// <summary>
    ///   CancelQuertyPhoneDetailsChanges
    /// </summary>
    /// <param name = "obj"></param>
    private void CancelQuertyPhoneDetailsChanges(object obj)
    
        log.Debug("CancelQuertyPhoneDetailsChanges" + BaseModel.FUNCTION_ENTERED_LOG);
        GetOldQuertyPhoneDetails(QuertyPhoneDetails.Specs);
        this.IsDirty = false;
        productIdModel.SelectMainProductList();
        Messenger.NotifyColleagues<bool>(
            MessengerMessages.POP_UP_CLOSE_REQUEST, true);
        log.Debug("CancelQuertyPhoneDetailsChanges" + BaseModel.FUNCTION_EXIT_LOG);
    

    /// <summary>
    ///   GetOldQuertyPhoneDetails
    /// </summary>
    /// <param name = "idNumber"></param>
    private void GetOldQuertyPhoneDetails(string idNumber)
    
        log.Debug("GetOldQuertyPhoneDetails" + BaseModel.FUNCTION_ENTERED_LOG);
        if (!string.IsNullOrEmpty(idNumber))
        
            ProductId tempQuertyPhoneDetails =
                productIdModel.GetProduct(idNumber).Clone() as ProductId;
            if (tempQuertyPhoneDetails != null)
            
                QuertyPhoneDetails = tempQuertyPhoneDetails;
                QuertyPhoneDetails.Reset();
            
            AttachChangeEvents();
        
        log.Debug("GetOldQuertyPhoneDetails" + BaseModel.FUNCTION_EXIT_LOG);
    

    /// <summary>
    ///   AttachChangeEvents
    /// </summary>
    private void AttachChangeEvents()
    
        log.Debug("AttachChangeEvents" + BaseModel.FUNCTION_ENTERED_LOG);
        QuertyPhoneDetails.Reset();
        QuertyPhoneDetails.PropertyChanged -= OnQuertyPhoneDetailsChanged;

        QuertyPhoneDetails.PropertyChanged += OnQuertyPhoneDetailsChanged;

        log.Debug("AttachChangeEvents" + BaseModel.FUNCTION_EXIT_LOG);
    

    private void OnQuertyPhoneDetailsChanged(object sender,
                                           PropertyChangedEventArgs e)
    
        if (QuertyPhoneDetails.Changes.Count > 0)
        
            OnItemDataChanged(sender, e, QuertyPhoneDetails.Changes.Count);
        
    

    /// <summary>
    ///   ValidateQuertyPhoneDetails
    /// </summary>
    /// <returns></returns>
    private bool ValidateQuertyPhoneDetails()
    
        log.Debug("ValidateQuertyPhoneDetails" + BaseModel.FUNCTION_ENTERED_LOG);
        if (
            !Utility.IsValidAlphanumeric(
                QuertyPhoneDetails.ControllerInformation)
            ||
            (!Utility.IsValidAlphanumeric(QuertyPhoneDetails.PhoneId))
            || (!Utility.IsValidAlphanumeric(QuertyPhoneDetails.Specs)))
        
            QuertyPhoneDetails.ErrorMsg =
                AddTreeNodeViewModel.ERROR_NO_ALPHANUMERIC;
            return false;
        
        QuertyPhoneDetails.ErrorMsg = string.Empty;
        log.Debug("ValidateQuertyPhoneDetails" + BaseModel.FUNCTION_EXIT_LOG);
        return true;
    

    # endregion


#endregion

我很困惑该怎么办... 在这个 regrad 中的任何帮助都是 GR8 !!!谢谢

【问题讨论】:

有人遇到问题了吗...?? 您是否要从树视图中选择一个项目(其中项目继承自基类“CalibrationTable”)并在边框中显示所选项目,并根据所选项目的实际类型,用特殊视图(由数据模板指定)显示它?我对么?顺便问一下边框的 ContentControl 绑定中的转换器是做什么的? @erikH 感谢您的调查...我会再次向您解释问题...当我在 TreeView 中选择一个项目时,Contentcontrol 会显示特定视图,但视图 ViewModel 不知道哪个“电话”显示为 Phone 的“UniqueId”(EnitityBase) 属性(在 TreeView 中选择)需要为 ViewModel 赋值)...我目前正在使用 Messenger 执行此操作,即(如您在 ViewModels 构造函数中所见)我请求(从 PhonesViewModel) 到 TreeViewModel 给我选定的电话和 TreeViewModel 发送它.... 我正在寻找一种方法,以便我可以将 UniqueId 从 TreeView 发送到 SpecificViewModel,而无需使用 Messengers 作为其让应用程序变慢...(他们一次又一次地注册)。 【参考方案1】:

为什么不让您的 ParentViewModel 跟踪 SelectedItem 而不是让您的 View 来做呢?我个人讨厌在我的视图中做任何类型的业务逻辑。它应该只是一个漂亮的层,位于我的 ViewModel 之上,以使用户更容易与它们交互。

例如,您的 PhonesViewModel 可能有:

// Leaving out get/set method details for simplicity
Observable<QuertyPhone> Phones  get; set; 
QuertyPhone SelectedPhone  get; set; 

ICommand SaveSelectedPhoneCommand  get; 
ICommand CancelSelectedPhoneCommand  get; 

您的视图将看起来像这样:

<DockPanel>
    <TreeView DockPanel.Dock="Left"
              ItemsSource="Binding Phones"
              SelectedItem="Binding SelectedPhone" />

    <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
        <Button Content="Binding SaveCommand" />
        <Button Content="Binding CancelCommand" />
    </StackPanel>

    <ContentControl Content="Binding SelectedPhone />
</DockPanel>

我会将验证保留在您的 QuertyPhone 模型中,但会将数据访问权限移至 ViewModel。在我看来,你的模型应该只是愚蠢的数据对象。您还只需在一个地方(您的 ViewModel)注册消息传递,如果您需要在 SelectedPhone 更改时执行任何操作,只需在 PropertyChanged 事件中执行即可。

作为旁注,我忘记了是否可以绑定 TreeView 的 SelectedItem,但如果不能,您可以使用样式看起来像 TreeView 的 ListBox,或者在过去我已将 IsSelected 属性添加到我的TreeView 数据对象,并将TreeViewItem.IsSelected 属性绑定到该对象。

【讨论】:

TreeViewUserControl 有自己的 ViewModel,mainView 有自己的,所有其他 Usersontrol(在 DataTemplates 中)都有自己的 ViewModel。 TreeView 没有SelectedItem 属性我已经设法将 selectedItem 传递给我的TreeViewUserControlViewModel,但我仍然如何能够将 SelectedItem 从 TreeViewUserControl ViewModel 传递给 OtherUserControl ViewModels(DataTemplates 中的那些)....跨度> 这就是我使用ParentViewModel 的原因。仅将您的应用程序视为您的 ViewModel,而不将 View 用于除漂亮 UI 之外的任何其他内容。如果您的主窗体包含 TreeViewUserControlMainViewUserControl,那么您的 ParentViewModel 应该包含这两个对象的 ViewModel。然后ParentViewModel 可以订阅TreeViewModel.SelectedItem 上的PropertyChanged 事件,并且可以将更改广播到感兴趣的视图模型,或者可以更新视图模型本身,例如设置MainViewModel.CurrentItem = TreeViewModel.SelectedItem

以上是关于将值从 View 发送到 ViewModel的主要内容,如果未能解决你的问题,请参考以下文章

Laravel:如何将值从控制器发送到模型?

将值从一个视图发送到另一个 ExtJS 4

如何将值从ini文件发送到bs4 find? (Python)

在将值从 Servlet 发送到客户端 JSP 时获取 null [重复]

如何将值从Django模板传递给View

将值从控制器发送到“标题”视图,但当页面更改时,变量未定义