在 MainWindows 和 UserControl 之间共享 DataContext

Share DataContext between MainWindows and UserControl

我想知道是否可以在 Windows 和 C#/WPF 中的 UserControl 之间共享数据上下文。

我有一个像这样的主要windows(未完成):

MainWindow.xaml:

<Window x:Class="MyProject.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:MyProject"
        xmlns:v="clr-namespace:MyProject.Views"
        mc:Ignorable="d"
        Title="MyProject" >
    <Window.DataContext>
        <local:MainViewModel/>        
    </Window.DataContext>
    <Grid>
        <v:GenerateView/>
        <v:ReadView/>
    </Grid>
</Window>

MainViewModel.cs:

public class MainViewModel : ViewModelBase
{
    #region Properties
    #endregion

    #region Fields
    #endregion

    #region Constructor
    public MainViewModel()
        : base()
    {
    }
    #endregion

    #region Methods
    #endregion

    #region Commands
    #endregion
}

根据将来的参数,我将显示我的视图 GenerateView 或 ReadView。实际上我正在开发 UserControl GenerateView,但我想知道我是否可以使用相同的 Datacontext。

根据that post,我是这样开始的:

<UserControl x:Class="MyProject.Views.GenerateView"
         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:MyProject.Views"
         xmlns:p="clr-namespace:MyProject.Properties"
         xmlns:MyProject="clr-namespace:MyProject" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyProject:MainWindow}}}">
    <Grid>
    </Grid>
</UserControl>

但是它不起作用,当我尝试访问 GenerateView 中的 Datacontext 时,它是空的。

编辑:

我忘记了部分代码:

public partial class GenerateView : UserControl
{
    private MainViewModel Context
    {
        get
        {
            return DataContext as MainViewModel;
        }
    }

    public GenerateView()
    {
        InitializeComponent();
        Context.PropertyChanged += Context_PropertyChanged;
    }

    private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        //Action to perform
    }
}

Context.PropertyChanged += Context_PropertyChanged; 抛出异常,因为 Datacontext 为空。

根据 Andrew 的评论,我找到了解决问题的方法:

public 部分 class 生成视图:用户控件 { 私有 MainViewModel 上下文 { 得到 { return DataContext 作为 MainViewModel; } }

public GenerateView()
{
    InitializeComponent();
    DataContextChanged += GenerateView_DataContextChanged;
}

private void GenerateView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    if (Context != null)
        Context.PropertyChanged += Context_PropertyChanged;
}
private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    //Action to perform
}

}

然后我从我的 UserControl 中删除了 DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyProject:MainWindow}}}"

为 Window 重用视图模型及其子用户控件背后的主要要求是什么? 这没有意义。两者有共同点吗?

在我看来,创建一个 MainWindowViewModel 并创建 SubViewModel 以供用户控制。 在 MainWindowViewModel 中创建子视图模型的实例并使用 DataContext.SubViewModel.

访问它们

通过这样做,您可以很好地维护代码和应用程​​序,并保持编码标准并拥有一个没有复杂性的视图模型。如果您只是为了可重用性而混淆所有内容,您可能会违反 MVVM 模式。让不同的 views/windows 拥有自己的视图模型,因为它们完全不同。

如果两者相似,那么您可以使用 Dependency Properties.

创建可重复使用的控件

我通常在 Window 中设置 DataContext:

public class MainWindow : Window
{
   InitializeComponent();
   ViewModel vm = new ViewModel();
   this.DataContext = vm;
}

或有时更高级:

我向我的 ViewModel 添加静态 属性:

public static ViewModel Instance {get; set;}

public class MainWindow : Window
{
   InitializeComponent();
   if(ViewModel.Instance == null)
   {
       ViewModel.Instance = new ViewModel();           
   }
   this.DataContext = ViewModel.Instance;
}

可以使用视图优先的方法。 您首先要为设计目的定义一个设计上下文。

<UserControl x:Class="MyProject.Views.CustomView"
     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:MyProject.Views"
     xmlns:p="clr-namespace:MyProject.Properties"
     xmlns:MyProject="clr-namespace:MyProject" 
     mc:Ignorable="d"
     d:DataContext="{d:DesignInstance Type=vm:MyViewModel}"
     d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
     .....
  </Grid>
</UserControl>

然后将用户控件绑定到主窗口的数据上下文

<Window x:Class="MyProject.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:MyProject"
    xmlns:v="clr-namespace:MyProject.Views"
    mc:Ignorable="d"
    Title="MyProject" >
<Window.DataContext>
    <local:MyViewModel/>        
</Window.DataContext>
<Grid>
    <v:CustomView/>
</Grid>
</Window>

那么用户控件将自动继承其父控件的数据上下文。