【问题标题】:How to get access to the MainWindow viewmodel from Page?如何从 Page 访问 MainWindow 视图模型?
【发布时间】:2018-12-23 12:25:27
【问题描述】:

我正在尝试了解 WPF 中的绑定系统。在我的示例中,我需要从 XAML 中的 Page 访问 MainWindow 视图模型。

我有一个解决方案来实现这一点。但我想知道更多不同的方式

MainWindow.xaml

<Window x:Class="FunAnkiWPF.MainWindow"
    ...omitted for brevity
    Height="450" Width="800"
    DataContext="{Binding ViewModel, RelativeSource={RelativeSource 
Self}}">

MainWindow.xaml.cs

public partial class MainWindow : Window
{        
    public MainWindowViewModel ViewModel { get; set; }

    public MainWindow()
    {
        ViewModel = new MainWindowViewModel(this);
        InitializeComponent();            
    }    
}

StartPage.xaml普通页面

StartPage.xaml.cs一种可行的解决方案

public partial class StartPage : Page
{
    public StartPage()
    {
        InitializeComponent();
        DataContext = App.Current.MainWindow.DataContext;
    }
}

如何直接访问 MainWindow ViewModel 属性(在 XAML 和代码隐藏中)? 如何访问 XAML 中的另一个数据上下文(例如在我的 StartPage 代码隐藏中)?

【问题讨论】:

  • 如果页面在 MainWindow 的可视化树中,则根本不需要显式设置 DataContext,因为它将被继承。如果没有,您可能需要重新考虑您的概念,因为它引入了紧密耦合。
  • @KlausGütter 在 MainWindow.xaml 中实现如下:
  • 不要使用页面。不要使用框架。而是在 ContentControl 中托管 UserControl。首先查找视图模型。您可以使用 relativesource 来获取父窗口的数据上下文
  • @Andy 我观看了 YouTube 视频并尝试像在视频中那样实现我自己的应用程序。这就是我选择页面的原因
  • 除非你真的要使用帧的日志,否则它们只是内存开销。顺便说一句,这可能是大量的。它们中还潜藏着一些微妙的陷阱,可能会让你大吃一惊。我从未见过商业应用使用页面。

标签: c# wpf xaml mvvm viewmodel


【解决方案1】:

从子控件绑定到父窗口数据上下文中的属性的简短答案是相对源,例如:

         <TextBlock Text="{Binding Path=DataContext.MainWinVMString, 
          RelativeSource={RelativeSource  AncestorType={x:Type Window}}}"

这里有一个例子,让你了解我的建议。

MainWindow 标记有点快而且很脏。 我会将数据模板放入由 app.xaml 合并的资源字典中。

    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate DataType="{x:Type local:LoginViewModel}">
        <local:LoginUC/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:UserViewModel}">
        <local:UserUC/>
    </DataTemplate>
</Window.Resources>
<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <ItemsControl ItemsSource="{Binding NavigationViewModelTypes}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding Name}"
                    Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                    CommandParameter="{Binding VMType}"
                />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <ContentControl Grid.Column="1"
                    Content="{Binding CurrentViewModel}"
                    />
</Grid>

视图模型:

public class MainWindowViewModel : INotifyPropertyChanged
{
    public string MainWinVMString { get; set; } = "Hello from MainWindoViewModel";

    public ObservableCollection<TypeAndDisplay> NavigationViewModelTypes { get; set; } = new ObservableCollection<TypeAndDisplay>
        (
        new List<TypeAndDisplay>
        {
           new TypeAndDisplay{ Name="Log In", VMType= typeof(LoginViewModel) },
           new TypeAndDisplay{ Name="User", VMType= typeof(UserViewModel) }
        }
        );

    private object currentViewModel;

    public object CurrentViewModel
    {
        get { return currentViewModel; }
        set { currentViewModel = value; RaisePropertyChanged(); }
    }
    private RelayCommand<Type> navigateCommand;
    public RelayCommand<Type> NavigateCommand
    {
        get
        {
            return navigateCommand
              ?? (navigateCommand = new RelayCommand<Type>(
                vmType =>
                {
                    CurrentViewModel = null;
                    CurrentViewModel = Activator.CreateInstance(vmType);
                }));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Relaycommand 来自 nuget 包 mvvmlightlibs。

用户UC:

<Grid Background="pink">
    <TextBlock Text="This is the User module Control"
               VerticalAlignment="Top"
               />
    <TextBlock Text="{Binding Path=DataContext.MainWinVMString, RelativeSource={RelativeSource  AncestorType={x:Type Window}}}"
               VerticalAlignment="Bottom"
               />
</Grid>

完整的工作示例: https://1drv.ms/u/s!AmPvL3r385QhgqIZUul-ppiIHZ9uyA

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-24
    • 2019-10-03
    • 1970-01-01
    • 1970-01-01
    • 2020-03-24
    • 2019-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多