【问题标题】:MVVM Light: How to get data from usercontrols?MVVM Light:如何从用户控件获取数据?
【发布时间】:2012-07-11 00:38:43
【问题描述】:

我有一个包含 MyMetaData 和 MyData 的类 MyDataCollection。在我的应用程序中,我有两个向用户显示输入字段的用户控件。一个用于 MyMetaData,另一个用于 MyData。两个用户控件都包含在 MainPage 中。

我的问题是:我应该如何从用户控件中获取数据,然后用户单击保存按钮(位于主页上)?

更新 我已将代码相应地更改为blindmeis post,但现在未显示MetaDataView:

<UserControl.Resources>
    <DataTemplate x:Key="MetaDataTemplate">
        <view:MetaDataView/>
    </DataTemplate>
</UserControl.Resources>

<Grid>
    <ContentPresenter Content="{Binding MetaDataTemplate}"/>
</Grid>

【问题讨论】:

  • 内容必须是您的视图模型对象,例如我的元数据。我没有 silverlight,但就像 rachel 说的那样,您可以尝试将 ContentTemplate 设置为您的数据模板。查看我的编辑。

标签: c# silverlight xaml mvvm mvvm-light


【解决方案1】:

为什么不用简单的方法来做 mvvm?(首先是视图模型)。你说你有一个主页——这意味着你也有一个主页视图模型。您的 mainpageviewmodel 至少处理保存命令。现在你想在你的主页上显示 MyMetaData 和 MyData。所以最简单的方法是在主视图模型中创建一个 MyMetaData 实例和一个 MyData 实例。

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
{}

您的主页只需要 2 个数据模板和 2 个内容演示器。

//资源

    <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>

因为您的 mainpageviewmodel 具有两个“子”视图模型,所以您可以在 savecommand 中获得所需的所有信息。

如果您有其他情况,请更新您的问题,也许发布一些代码。

编辑:我没有银光,所以只是一个建议:也许瑞秋可以给你一个更好的答案。

<Grid>
  <ContentPresenter Content="{Binding MyMetaData}" ContentTemplate="{StaticResource MetaDataTemplate}"/>
</Grid>

如果 silverlight 无法处理具有数据类型的数据模板,您可以直接将用户控件放在那里。

<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>

【讨论】:

  • 他们可能来自 WinForms 背景,或者那里有一些教授 View-First 的教程 :)
  • 我试过你的代码,但我认为 DataTemplate (Silverlight) 不支持 DataType。
  • @jwillmer 没错,Silverlight 不支持隐式 DataTemplates(目前)。相反,给您的DataTemplates 一个x:Key 属性,并将您的ContentControlsContentTemplate 设置为他们应该用来绘制对象的DataTemplate
  • @jwillmer,我错过了你的 silverlight 标签,抱歉。
  • @blindmeis 没问题,但你能更新你的代码吗?我试图用@Rachel 评论修复它,但它没有用(请参阅我的更新)。
【解决方案2】:

由于您将此问题标记为 MVVM,因此您的 ViewModel 应该包含您的 SaveCommand 以及执行实际保存所需的所有数据

您的MainViewModel 应该包含MyMetaDataMyData 属性(绑定到它们各自的UserControls),并且每个对象都应该包含UserControl 中所需的任何数据的属性。例如,如果您的UserControl 有一个用于NameTextBox,那么您的数据对象应该有一个Name 的属性,TextBox 绑定到该属性。

如果 Save 按钮位于其中一个 UserControls 中,则相应的 ViewModel 应该有一个 SaveCommand,当单击 Button 时会执行该 SaveCommandSave 所需的所有数据也都位于该 ViewModel 中,所以您可以开始了。

如果您的MainViewModel 负责保存数据,那么它应该能够挂钩到您的子ViewModel 的SaveCommand 并附加它自己的方法,例如

this.MyData.SaveCommand = this.SaveCommand();

保存所需的所有数据都可以在this.MyData中找到

如果SaveButton 位于您的MainView 中,而不是在其中一个UserControls 中,那么SaveCommand 应该是MainViewModel 的一部分,并且保存所需的所有数据都可以在this.MyData 中找到或This.MyMetaData

请记住,使用 MVVM,您的 ViewModel 就是您的应用程序。 View 只是一个漂亮的界面,它允许用户与您的 ViewModel 进行交互。

【讨论】:

  • +1 用于编写我一直以来的想法 :) “记住,对于 MVVM,您的 ViewModel 就是您的应用程序。View 只是一个漂亮的界面,允许用户与您的 ViewModel 进行交互。”跨度>
【解决方案3】:

您应该使用双向绑定来自动更新控制器中的值。看看this article

这是一个例子:

<TextBox Text="{Binding MyMetaData, Mode=TwoWay }" />
<TextBox Text="{Binding MyData, Mode=TwoWay }" />

【讨论】:

  • 我是这样做的,但是如何将 usercontrol-viewmodel 中的数据获取到 mainpage-viewmodel 中?
【解决方案4】:

我会给你一个小例子,你可以如何使用 MVVM Light Messenger 进行 ViewModel 到 ViewModel 的通信。假设您有一个 MyDataCollection 类:

public class MyDataCollection
{
    public int MyData;
    public string MyMetaData;
}

在您的 MainViewModel 上,您有一个由 View 的 SaveButton 绑定的 RelayCommand(来自 MVVM light toolkit)。执行 Connad 时,您必须发送带有回调操作的 Message 以从订阅者请求数据。回调以 MyDataCollection 作为参数:

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... 
    }
}

UserControlViewModel 具有与 InputTextBoxes 绑定的属性。它只需要注册到消息并使用数据属性调用回调:

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});
            }
        }
    }

}

【讨论】:

    【解决方案5】:

    您必须使用 messenger,否则您必须在 ViewModelLocator 上设置属性

    我如何使用它来设置 UI 语言的 Messenger 示例 ViewModel A,我在这里用“SetLanguage”令牌注册了一个监听器:

    Messenger.Default.Register<string>(this, "SetLanguage", false, input =>
    {
        SetLanguage(input);
    });
    

    ViewModel B,我在这里发送带有“SetLanguage”令牌的消息:

    Messenger.Default.Send("en-EN", "SetLanguage");
    

    ViewModel A 中的 ViewModelLocator 示例,我通过定位器访问 ViewModel B 中的数据:

    short value = 12;
    var myFilteredDataList = ViewModelLocator.ViewModelBStatic.MyDataList.Any(m => m.code == value);
    

    【讨论】:

      【解决方案6】:

      我现在有两个解决方案:

      查看:

      <ContentPresenter Content="{Binding MyMetaDataView}" />
      

      视图模型:

      public MetaDataViewModel MyMetaDataViewModel { get; set; }
      public MetaDataView MyMetaDataView { get; set; }
      
      public MainViewModel()
      {
          MyMetaDataViewModel = new MetaDataViewModel();
          MyMetaDataView = new MetaDataView();
      
          MyMetaDataView.DataContext = MyMetaDataViewModel;
      }
      

      或----

      查看:

      <UserControl.Resources>
          <DataTemplate  x:Key="MetaDataViewTemplate">
              <view:MetaDataView />
          </DataTemplate>
      </UserControl.Resources>
      ...
      <ContentPresenter Content="{Binding MyMetaDataViewModel}" ContentTemplate="{StaticResource MetaDataViewTemplate}"/>
      

      视图模型:

      public MetaDataViewModel MyMetaDataViewModel { get; set; }
      
      public MainViewModel()
      {
          MyMetaDataViewModel = new MetaDataViewModel();
      }
      

      【讨论】:

      • 第一个中断 mvvm 导致 MetaDataView 是 UserControl 还是?
      • @blindmeis 你是对的,因为它破坏了 mvvm 我已经实施了第二个解决方案 ;-)
      猜你喜欢
      • 2019-01-13
      • 1970-01-01
      • 2014-12-03
      • 2017-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-07
      • 2013-07-04
      相关资源
      最近更新 更多