Reputation: 3790
I have a class MyDataCollection that contains MyMetaData and MyData. In my application i have two usercontrolls that display input fields to the user. One for the MyMetaData and the other for MyData. Both usercontrols are included in the MainPage.
My Question is: How should i get the data from the usercontrols then the user klicks on the save-button (located on the mainpage)?
Update I have changed my code accordingly to blindmeis post but now the MetaDataView is not shown:
<UserControl.Resources>
<DataTemplate x:Key="MetaDataTemplate">
<view:MetaDataView/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentPresenter Content="{Binding MetaDataTemplate}"/>
</Grid>
Upvotes: 0
Views: 1541
Reputation: 22445
why not doing mvvm the easy way?(viewmodel first). you say you have a mainpage - this means you also have a mainpageviewmodel. your mainpageviewmodel handles at least the save command. now you want to show MyMetaData and MyData on your mainpage. so the easy way would be to create one MyMetaData instance and one MyData instance in your mainviewmodel.
public class MainPageViewmodel
{
public ICommand SaveCommand { get; set; }
public MyDataViewmodel MyData { get; set; }
public MyMetaDataViewmodel MyMetaData { get; set; }
public MainPageViewmodel()
{
this.MyData = new MyDataViewmodel();
this.MyMetaData = new MyMetaDataViewmodel();
}
}
public class MyDataViewmodel
{}
public class MyMetaDataViewmodel
{}
your mainpage just need 2 datatemplates and 2 contentpresenter.
//resources
<DataTemplate DataType="{x:Type Local:MyDataViewmodel}">
<view:MyDataUserControl/>
</DataTemplate>
<DataTemplate DataType="{x:Type Local:MyMetaDataViewmodel}">
<view:MyMetaDataUserControl/>
</DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentPresenter Content="{Binding MyData}" Grid.Column="0"/>
<ContentPresenter Content="{Binding MyMetaData}" Grid.Column="1"/>
<Button Content="Save" Command="{Binding SaveCommand}" Grid.Column="2"/>
</Grid>
because your mainpageviewmodel has both "child" viewmodel, you have all information you want on your savecommand.
if you have another scenario pls update your question, maybe post some code.
EDIT: i have no silverlight so that just a suggestion: maybe rachel can give you a better answer.
<Grid>
<ContentPresenter Content="{Binding MyMetaData}" ContentTemplate="{StaticResource MetaDataTemplate}"/>
</Grid>
if silverlight cant handle datatemplates with datatype you could just put the usercontrol there directly.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<view:MyDataUserControl DataContext="{Binding MyData}" Grid.Column="0"/>
<view:MyMetaDataUserControl DataContext="{Binding MyMetaData}" Grid.Column="1"/>
<Button Content="Save" Command="{Binding SaveCommand}" Grid.Column="2"/>
</Grid>
Upvotes: 4
Reputation: 3790
I have two solutions now:
View:
<ContentPresenter Content="{Binding MyMetaDataView}" />
ViewModel:
public MetaDataViewModel MyMetaDataViewModel { get; set; }
public MetaDataView MyMetaDataView { get; set; }
public MainViewModel()
{
MyMetaDataViewModel = new MetaDataViewModel();
MyMetaDataView = new MetaDataView();
MyMetaDataView.DataContext = MyMetaDataViewModel;
}
or ----
View:
<UserControl.Resources>
<DataTemplate x:Key="MetaDataViewTemplate">
<view:MetaDataView />
</DataTemplate>
</UserControl.Resources>
...
<ContentPresenter Content="{Binding MyMetaDataViewModel}" ContentTemplate="{StaticResource MetaDataViewTemplate}"/>
ViewModel:
public MetaDataViewModel MyMetaDataViewModel { get; set; }
public MainViewModel()
{
MyMetaDataViewModel = new MetaDataViewModel();
}
Upvotes: 0
Reputation: 595
I'll give you a little sample, how you can use the MVVM Light Messenger for ViewModel-to-ViewModel communication. Say you have an MyDataCollection class:
public class MyDataCollection
{
public int MyData;
public string MyMetaData;
}
On your MainViewModel you have a RelayCommand (from MVVM light toolkit) binded by your View's SaveButton. When the Connad is executed, you will have to send a Message with a callback action to request data from the subcriber. The callback takes MyDataCollection as parameter:
public class MainViewModel : ViewModelBase
{
public RelayCommand SaveCommand { get; private set; }
//Ctor
public MainViewModel()
{
SaveCommand = new RelayCommand(
() =>
Messenger.Default.Send<NotificationMessageAction<MyDataCollection>>(
new NotificationMessageAction<MyDataCollection>("SaveData", SaveCallback)));
}
private void SaveCallback(MyDataCollection dataCollection)
{
// process your dataCollection...
}
}
The UserControlViewModel has properties the InputTextBoxes are binded too. It just has to register to the message and call the callback with data properties:
public class UserControlViewModel : ViewModelBase
{
//Properties
public string UserControlMetaData { get; set; }
public int UserControlData { get; set; }
//Ctor
public UserControlViewModel()
{
Messenger.Default.Register<NotificationMessageAction<MyDataCollection>>(this, MessageReceived);
}
// private Method to handle all kinds of messages.
private void MessageReceived(MessageBase msg)
{
if(msg is NotificationMessageAction<MyDataCollection>)
{
var actionMsg = msg as NotificationMessageAction<MyDataCollection>;
if(actionMsg.Notification == "SaveData") // Is this the Message, we are looking for?
{
// here the MainViewModels callback is called.
actionMsg.Execute(new MyDataCollection() {MyData = UserControlData, MyMetaData = UserControlMetaData});
}
}
}
}
Upvotes: 1
Reputation: 132558
Since you tagged this question as MVVM
, your ViewModels should contain both your SaveCommand
and all the data needed to perform the actual save
Your MainViewModel
should contain MyMetaData
and MyData
properties (which are bound to their respective UserControls
), and each of those objects should contain properties for any data needed in the UserControl
. For example, if your UserControl
had a TextBox
for Name
, then your data object should have a property for the Name
that the TextBox
binds to.
If the Save button is located in one of those UserControls
then the respective ViewModel should have a SaveCommand
that gets executed when the Button
is clicked. All the data needed for the Save
is also located in that ViewModel, so you're good to go.
If your MainViewModel
is in charge of saving the data, then it should be able to hook into your sub ViewModel's SaveCommand
and attach it's own method, such as
this.MyData.SaveCommand = this.SaveCommand();
and all the data needed for the save can be found in this.MyData
If the SaveButton
is located in your MainView
, and not in one of the UserControls, then the SaveCommand should be part of MainViewModel
, and all the data needed for the save can be found in this.MyData
or This.MyMetaData
.
Remember, with MVVM your ViewModels are your application. The View is just a pretty interface that allows users to interact with your ViewModels.
Upvotes: 4
Reputation: 2768
You will have to use messengers or you will have to set the properties over the ViewModelLocator
Messenger example of how I use it to set the UI language ViewModel A, I register a listener here with the "SetLanguage" token:
Messenger.Default.Register<string>(this, "SetLanguage", false, input =>
{
SetLanguage(input);
});
ViewModel B, here I send the message with the "SetLanguage" token:
Messenger.Default.Send("en-EN", "SetLanguage");
ViewModelLocator example in ViewModel A, I access data in ViewModel B over the locator:
short value = 12;
var myFilteredDataList = ViewModelLocator.ViewModelBStatic.MyDataList.Any(m => m.code == value);
Upvotes: 0
Reputation: 18530
You should use Two-way bindings to automatically update the value in your controller. Take a look at this article.
Here's an example:
<TextBox Text="{Binding MyMetaData, Mode=TwoWay }" />
<TextBox Text="{Binding MyData, Mode=TwoWay }" />
Upvotes: 1