【问题标题】:Separating properly the view and the viewmodel正确分离视图和视图模型
【发布时间】:2017-06-12 13:48:14
【问题描述】:

我是初学者,在设置 WPF 项目和遵循 MVVM 模式时遇到问题;我看不到如何将视图链接到具有以下组织的视图模型:

我设置了 3 个文件夹:Model、View 和 ViewModel,它们都位于名为“Company.App.UI”的项目的根目录中。 App.xaml 和 MainWindow.xaml 位于项目的根目录。

从这里开始,我想通过以下方式控制 MainWindow 客户区中显示的内容: - 将文件夹“View”中的渲染视图作为 UserControls,例如“LoginView.xaml” - 在“ViewModel”文件夹中有相应的视图模型,例如“LoginView.xaml.cs”

那么我在 MainWindow.xaml 中所做的是:

<Window x:Class="Company.App.UI.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewmodel="clr-namespace:Company.App.UI.ViewModel"
        xmlns:view="clr-namespace:Company.App.UI.View" <!-- does not work, not a namespace -->
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate DataType="{x:Type viewmodel:LoginViewModel}">
            <view:LoginView/> <!-- does not work -->
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <ContentControl Content="{Binding ClientArea}"/>
        </StackPanel>
    </Grid>        
</Window>

在 MainWindow.xaml.cs 中:

using System.Windows;
using System.Windows.Controls;
using Company.App.UI.ViewModel;

namespace Company.App.UI
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    private UserControl _ClientArea = null;

    public UserControl ClientArea
    {
      get { return _ClientArea; }
      set { _ClientArea = value; }
    }

    public MainWindow()
    {
      if (_ClientArea == null) { ClientArea = new LoginViewModel(); }
      InitializeComponent();
    }
  }
}

LoginView 是一个简单的用户控件,带有一个标签,只是为了看看它是什么。 如果我将我的 LoginView.xaml 放在项目的根目录下,在 MainWindow.xaml 旁边,它可以工作...... 我做错了什么/错过了什么? 我不想使用任何框架(PRISM 等)来让它工作。 如果我的帖子重复,我深表歉意,但我在搜索时也找不到它。 谢谢,

更新

我使用带有 0 个更新/补丁/等的 VS2013。 一切都在同一个项目中。

错误输出是:

  1. 类型或命名空间名称“视图”不存在于 命名空间“Company.App.UI”(您是否缺少程序集引用?)
  2. 名称空间中不存在名称“LoginView” “clr 命名空间:Company.App.UI.View”。
  3. 未找到类型“view:LoginView”。确认您没有丢失程序集引用,并且所有引用的程序集都已构建。

LoginView.xaml:

<UserControl x:Class="Company.App.UI.ViewModel.LoginViewModel"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Label>User control login</Label>
    </Grid>
</UserControl>

LoginViewModel.cs:

using System.Windows.Controls;

namespace Company.App.UI.ViewModel
{
  public partial class LoginViewModel : UserControl
  {
    public LoginViewModel()
    {

    }
  }
}

【问题讨论】:

  • 文件夹结构不相关。您必须在代码文件中使用适当的命名空间声明,例如namespace Company.App.UI.View { ... }
  • “有效” - 什么无效?你想要to improve 工作代码吗?
  • ClientArea 被声明为UserControl(非常糟糕的主意)。然后你分配一个名为LoginViewModel 的东西给它。 LoginViewModel 是什么?它是视图模型还是控件?
  • 要绑定到窗口的属性而不是窗口的DataContext 的属性(您将其保留为空),请使用相对源/祖先类型绑定:Content="{Binding ClientArea, RelativeSource={RelativeSource AncestorType=Window}}"
  • 要记住的基本规则。你的 ViewModel 不能使用 System.Windows.Controls; 命名空间。

标签: c# wpf mvvm


【解决方案1】:

LoginView.xaml 中更改:

x:Class="Company.App.UI.ViewModel.LoginViewModel"   

到这里

x:Class="Company.App.UI.ViewModel.LoginView"

因为这是一个控件而不是 ViewModel

另外,LoginView.xaml.cs 应该是这样的(没有看到您的实现):

using System.Windows.Controls;

namespace Company.App.UI.View
{
    /// <summary>
    /// Interaction logic for LoginView.xaml
    /// </summary>
    public partial class LoginView : UserControl
    {
        public LoginView()
        {
            InitializeComponent();
        }
    }
}

当你掌握它的窍门时(mvvm)我建议使用mvvm light toolkit 进行管道(无需重新发明轮子)

【讨论】:

    【解决方案2】:

    完全正确....做任何伊戈尔告诉你的改变。 与此同时,

    更改您的 MainWindow.xaml.cs

     if (_ClientArea == null) { ClientArea = new LoginViewModel(); }
    

     if (_ClientArea == null) { ClientArea = new LoginView(); }
    

    另外,据我了解,您只想从用户控件到主窗口显示一个标签,并想研究 MVVM 概念。所以这里是你的例子的解释,可能会对你有所帮助

     <Grid>
            <!--connect to viewmodel-->
            <Grid.DataContext>
             <viewmodel:LoginViewModel></viewmodel:LoginViewModel>
            </Grid.DataContext>
    
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
    
            <!--import user control-->
            <view:LoginView Grid.Row="0"></view:LoginView>
    
            <StackPanel Grid.Row="1" Orientation="Horizontal">
                <ContentControl Content="{Binding ClientArea}"/>
            </StackPanel>
    
        </Grid>
    

    注意 - 尝试在后面的任何代码中保持零代码。这是主要的 MVVM 的目的。它应该只有

    • 模型 ...(类文件应该只包含属性)
    • 查看 ...(用户控件、xaml、窗口文件,其中应仅包含
      xml 代码,后面有零代码)
    • Modelview...(应包含纯连接的类文件 在视图和模型之间,不应包含任何对象 视图或模型。它通过绑定连接)

    我也不知道您创建“ClientArea”的目的是什么……您是否在某处定义了它的内容?
    如果您需要任何帮助,请告诉我...我有一些关于 MVVM 的示例演示项目。

    【讨论】:

    • 我从上面的所有 cmets 中看到了我的错误:错误是混淆了 xaml 代码隐藏 (.xaml.cs) 和另一个 .cs 的 viewmodel。
    • 我很高兴它有帮助!!1
    【解决方案3】:

    出于类似目的的帖子也帮助我得到了东西:

    Binding a ContentControl to UserControl, and reuse same instance,

    这里有很好的主题:How to preserve the full state of the View when navigating between Views in an MVVM application?

    我发布问题后发现的另一个好点(...):https://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx

    基本上,我想要实现的是在一个窗口中管理内容区域,无需任何框架,更精确地管理“事务”,即在用户交互时从一个屏幕切换到另一个屏幕。

    感谢所有的cmets,事情越来越清楚了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-10
      • 2016-11-28
      相关资源
      最近更新 更多