将值从 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 之外的任何其他内容。如果您的主窗体包含 TreeViewUserControl
和 MainViewUserControl
,那么您的 ParentViewModel
应该包含这两个对象的 ViewModel。然后ParentViewModel
可以订阅TreeViewModel.SelectedItem
上的PropertyChanged
事件,并且可以将更改广播到感兴趣的视图模型,或者可以更新视图模型本身,例如设置MainViewModel.CurrentItem = TreeViewModel.SelectedItem
以上是关于将值从 View 发送到 ViewModel的主要内容,如果未能解决你的问题,请参考以下文章
如何将值从ini文件发送到bs4 find? (Python)