WPF DataContext使用

92 篇文章 9 订阅
21 篇文章 8 订阅

DataContext连接View与ViewModel挂钩。

有3种方法可以将View与ViewModel连接起来。

1.在XAML中

2.后台代码分配

3.视图模型定位

我们的重点是如何绑定DataContext,因此我们不会在本文中关注样式或数据。

我们需要2个文件夹,每个文件夹用于View和ViewModel。然后我创建了两个UserControl,LoginView和RegisterView以及它们各自的ViewModel1.LoginViewModel和2.RegisterViewModel。请参阅图1以了解结构。

笔记

我们必须遵守这里的标准。每个视图都以术语“视图”结束,例如LoginView,每个视图模型都以术语“视图模型”结束,例如LoginViewModel。

在XAML中绑定DataContext

我们将在这个实验中使用LoginView

第1步

将LoginViewModel的命名空间添加到LoginVIew.xaml。

xmlns:local="clr-namespace:WPF_DataContext.VIewModel" 

使用UserControl的DataContext属性来分配ViewModel

<UserControl.DataContext>  
     <local:LoginViewModel/>  
</UserControl.DataContext>

就这样。现在要确认View是否与ViewModel连接,我们可以在LoginView.xaml中添加TextBlock。完成后,您的最终LoginView.xaml将如下所示。

<UserControl x:Class="WPF_DataContext.View.LoginView"    
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"     
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     
             xmlns:local="clr-namespace:WPF_DataContext.VIewModel"    
             xmlns:ViewModelWire ="clr-namespace:WPF_DataContext"    
             mc:Ignorable="d"     
             d:DesignHeight="450" d:DesignWidth="800"    
             Height="40" Width="200">    
    <UserControl.DataContext>    
        <local:LoginViewModel/>    
    </UserControl.DataContext>    
    <Grid>    
        <TextBlock Text="{Binding Message}" HorizontalAlignment="Center"/>    
    </Grid>    
</UserControl>     

请注意,TextBlock与属性名称Message绑定,为此我们需要在LoginVIewModel中创建此字符串类型属性。让我们在构造函数中为Message赋值。

在构造函数中分配值或调用API是一种不好的做法,您应该始终使用具有多线程的方法来调用或更新数据。但由于这是一个小例子,我们将直接在构造函数中赋值。

namespace WPF_DataContext.VIewModel  
{  
    public class LoginViewModel  
    {  
        private string _message;  
  
        public string Message  
        {  
            get { return _message; }  
            set { _message = value; }  
        }  
  
        public LoginViewModel()  
        {  
            _message = "Login View Model is Connected..";  
        }  
    }  
} 

只需调用MainWindow.xaml中的LoginView UserControl,如下。

<Window x:Class="WPF_DataContext.MainWindow"    
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"    
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
        xmlns:local="clr-namespace:WPF_DataContext"     
        xmlns:localView="clr-namespace:WPF_DataContext.View"    
        mc:Ignorable="d"    
        Title="MainWindow" Height="450" Width="800">    
    <Grid>    
        <localView:LoginView/>    
    </Grid>    
</Window>    

现在,运行您的项目。您将能够看到如图所示的输出,

使用代码分配DataContext

在这种方法中,我们将使用RegisterView。 打开RegisterView的代码隐藏类,即RegisterView.xaml.cs并设置this.DataContext值。

this.DataContext = new RegisterViewModel(); 

您的最终 RegiserView.xaml.cs 将类似于以下代码片段,

using System.Windows.Controls;  
using WPF_DataContext.VIewModel;  
  
namespace WPF_DataContext.View  
{  
    /// <summary>  
    /// Interaction logic for RegisterView.xaml  
    /// </summary>  
    public partial class RegisterView : UserControl  
    {  
        public RegisterView()  
        {  
            InitializeComponent();  
            this.DataContext = new RegisterViewModel();  
        }  
    }  
} 

现在我们在 View 上有了 TextBlock,我们已经在代码隐藏中设置了 DataContext,我们需要在 ViewModel 中为该 TextBlock 分配值。

以下 RegisterView.xaml 显示了 TextBlock 的外观,以下代码片段显示了 RegisterViewModel 的外观。

RegisterView.xaml

<UserControl x:Class="WPF_DataContext.View.RegisterView"    
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"     
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     
             xmlns:local="clr-namespace:WPF_DataContext.View"    
             xmlns:ViewModelWire ="clr-namespace:WPF_DataContext"    
             mc:Ignorable="d"     
             d:DesignHeight="450" d:DesignWidth="800">    
    <Grid>    
        <TextBlock Text="{Binding Message}" HorizontalAlignment="Center"/>    
    </Grid>    
</UserControl>

RegisterViewModel.cs

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
  
namespace WPF_DataContext.VIewModel  
{  
    public class RegisterViewModel  
    {  
        private string _message;  
  
        public string Message  
        {  
            get { return _message; }  
            set { _message = value; }  
        }  
  
        public RegisterViewModel()  
        {  
            _message = "Register View Model is Connected..";  
        }  
    }  
} 

最后将Mainwindow.xaml中的LoginView替换为RegisterView

<Window x:Class="WPF_DataContext.MainWindow"      
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"      
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"      
        xmlns:local="clr-namespace:WPF_DataContext"       
        xmlns:View="clr-namespace:WPF_DataContext.View"      
        mc:Ignorable="d"      
        Title="MainWindow" Height="450" Width="800">      
    <Grid>      
        <View:RegisterView/>      
    </Grid>      
</Window>     

使用ViewModelLocator自动连接

ViewModelLocator 集中代码以连接View和ViewModel。这意味着它为我们提供了将 View 与 ViewModel 绑定的松耦合方式,通过这种方法,View 不需要对它所连接的 ViewModel 进行硬编码。所以基本上我们用五步方法自动化整个过程,

  • 第 1 步:确定视图,例如LoginView
  • 第2步:确定ViewModel,如果我们正确地遵循命名约定,那么View的名称总是以ViewModel结尾。例如 LoginViewModel
  • 第 3 步:从第 2 步中获得 ViewModel 类型后,您需要创建该 ViewModel 类型的实例。
  • 第 4 步:创建一个 ViewModel 实例
  • 第 5 步:设置视图的 DataContext。

所有这些步骤都发生在运行时,这就是为什么 View 在编译时不必担心它的 ViewModel 绑定。

现在我们已经确定了这个逻辑,我们可以简单地将它包装在一个方法中,然后重用相同的方法。接下来,我们需要弄清楚如何从 View 调用这个方法,并且有一个简单的方法可以做到这一点,这可以通过附加属性的帮助来实现。

让我们通过添加一个名为 ViewModelLocator 的类来开始这个过程,

  • 类 ViewModelLocator 将封装
  1. 一个名为:AutoWireViewModel 的附加属性
  2. 和一个名为:AutoWireViewModelChanged 的​​事件处理程序(此方法将包括上面提到的 4 个步骤)

您最终的 ViewModelLocator 将如下所示:注意:检查代码中的摘要和注释以了解其含义。

using System;  
using System.Collections.Generic;  
using System.ComponentModel;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using System.Windows;  
  
namespace WPF_DataContext  
{  
    public static class ViewModelLocator  
    {  
        /// <summary>  
        /// Gets AutoWireViewModel attached property  
        /// </summary>  
        /// <param name="obj"></param>  
        /// <returns></returns>  
        public static bool GetAutoWireViewModel(DependencyObject obj)  
        {  
            return (bool)obj.GetValue(AutoWireViewModelProperty);  
        }  
  
        /// <summary>  
        /// Sets AutoWireViewModel attached property  
        /// </summary>  
        /// <param name="obj"></param>  
        /// <param name="value"></param>  
        public static void SetAutoWireViewModel(DependencyObject obj, bool value)  
        {  
            obj.SetValue(AutoWireViewModelProperty, value);  
        }  
  
        /// <summary>  
        /// AutoWireViewModel attached property  
        /// </summary>  
        public static readonly DependencyProperty AutoWireViewModelProperty =  
            DependencyProperty.RegisterAttached("AutoWireViewModel",  
            typeof(bool), typeof(ViewModelLocator),  
            new PropertyMetadata(false, AutoWireViewModelChanged));  
  
        /// <summary>  
        /// Step 5 approach to hookup View with ViewModel  
        /// </summary>  
        /// <param name="d"></param>  
        /// <param name="e"></param>  
        private static void AutoWireViewModelChanged(DependencyObject d,  
            DependencyPropertyChangedEventArgs e)  
        {  
            if (DesignerProperties.GetIsInDesignMode(d)) return;  
            var viewType = d.GetType(); //Step 1: Ex- LoginView  
            var viewModelTypeName = (viewType).ToString().Replace("View", "ViewModel"); //Step 2: Ex- LoginViewModelName  
            var viewModelType = Type.GetType(viewModelTypeName); // step 3: Ex- get the type of LoginViewModel  
            var viewModel = Activator.CreateInstance(viewModelType); // step 4: Ex- create an instance of LoginViewModel  
            ((FrameworkElement)d).DataContext = viewModel; // step 5: Ex- LoginView's DataContext is set to LoginViewModel              
        }  
    }  
} 

到目前为止,我们已经创建了一个附加属性,它使用它的事件调用我们的 AutoWire 逻辑。这是一种布尔类型的附加属性,当它设置为 true 时将触发事件。让我们看看如何在视图中做到这一点。

首先,将 LoginViewModel 的命名空间添加到您的视图中

<UserControl x:Class="WPF_DataContext.View.LoginView"    
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"     
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     
             xmlns:local="clr-namespace:WPF_DataContext.VIewModel"    
             xmlns:ViewModelWire ="clr-namespace:WPF_DataContext"    
             mc:Ignorable="d"     
             d:DesignHeight="450" d:DesignWidth="800"    
             Height="40" Width="200"    
             ViewModelWire:ViewModelLocator.AutoWireViewModel="True">    
    <!--<UserControl.DataContext>    
        <local:LoginViewModel/>    
    </UserControl.DataContext>-->    
    <Grid>    
        <TextBlock Text="{Binding Message}" HorizontalAlignment="Center"/>    
    </Grid>    
</UserControl> 

为了确认它可以同时处理多个视图,让我们在 RegisterView 中也做一些更改。并且不要忘记注释我们在 RegisterView.xaml.cs 的代码隐藏中设置的 DataContext。

RegisterView.xaml

<UserControl x:Class="WPF_DataContext.View.RegisterView"    
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"     
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     
             xmlns:local="clr-namespace:WPF_DataContext.View"    
             xmlns:ViewModelWire ="clr-namespace:WPF_DataContext"    
             mc:Ignorable="d"     
             d:DesignHeight="450" d:DesignWidth="800"    
             ViewModelWire:ViewModelLocator.AutoWireViewModel="True">    
    <Grid>    
        <TextBlock Text="{Binding Message}" HorizontalAlignment="Center"/>    
    </Grid>    
</UserControl>    

请将两个视图都添加到 MainWindow.xaml

<Window x:Class="WPF_DataContext.MainWindow"    
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"    
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
        xmlns:local="clr-namespace:WPF_DataContext"     
        xmlns:localView="clr-namespace:WPF_DataContext.View"    
        mc:Ignorable="d"    
        Title="MainWindow" Height="450" Width="800">    
    <Grid>    
        <Grid.RowDefinitions>    
            <RowDefinition/>    
            <RowDefinition/>    
        </Grid.RowDefinitions>    
        <localView:LoginView/>    
        <localView:RegisterView Grid.Row="1"/>    
    </Grid>    
</Window>     

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值