c# - MVVM 将嵌套 subview 连接到 subview 模型

标签 c# wpf mvvm nested

我正在尝试在我已经使用嵌套 View 的应用程序中建立嵌套 View 模型。这是我想做的一个例子:

主窗口 View :

<Window x:Name="FCTWindow" x:Class="CatalogInterface.MainWindow"
        xmlns:local="clr-namespace:CatalogInterface"
        xmlns:vm="clr-namespace:CatalogInterface.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="532">

    <Window.Resources>
        <vm:MainWindowViewModel x:Key="ViewModel" />
    </Window.Resources>

    <Grid DataContext="{Binding Path=ViewModel.DirFilesListBoxViewModel}" x:Name="BodyGridLeft" Grid.Row="0" Grid.Column="0">
        <local:ctlDirFilesListBox>
            <!--
                Need to access the `ItemsSource="{Binding }"` and
                 `SelectedItem="{Binding Path=}"` of the ListBox in 
                 `ctlDirFilesListBox` view -->
        </local:ctlDirFilesListBox>
</Window>

subview :
<UserControl x:Class="CatalogInterface.ctlDirFilesListBox"
         xmlns:local="clr-namespace:CatalogInterface"
         xmlns:vm="clr-namespace:CatalogInterface.ViewModels"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

<Grid x:Name="MainControlGrid">           
    <ListBox SelectionChanged="ListBoxItem_SelectionChanged" 
                         HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFFFFF"
                         Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" BorderThickness="0">
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
                <EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
                <EventSetter Event="KeyDown" Handler="ListBoxItem_KeyDown"/>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
</Grid>
</UserControl>

主窗口 View 模型
using System;
using System.Text;

namespace CatalogInterface.ViewModels
{
    class MainWindowViewModel
    {
        public DirFilesViewModel DirFilesViewModel { get; set; }

        public MainWindowViewModel()
        {
            DirFilesViewModel = new DirFilesViewModel();
        }
    }
}

所以,我需要 Hook ListBox.SelectedItemListBox.ItemSourceMainWindowViewModel.DirFilesViewModel 中的属性绑定(bind).问题是我必须在 MainWindow View 中进行绑定(bind)不是 ctlDirListBox看法。

如何访问 subview 中的元素?我认为这是我最大的障碍。我认为我所有的数据上下文都是正确的,我只是无法处理 subview 元素。

最佳答案

我假设 DirFilesViewModel是该用户控件的 View 模型。如果不是这样,请告诉我真实情况,我们会解决的。

这是一个非常简单的案例。 @JamieMarshall 如果您提供的 XAML 是您的用户控件的全部内容,那么它可能根本不应该是用户控件。您可以只编写一个包含该 XAML 的 DataTemplate 并使用它,或者您可以为 ListBox 编写一个样式。如果您需要这些事件,那么 UserControl 是有意义的,但您可能实际上并不需要这些事件。

但这可能只是了解如何使用 UserControls 的一个最小示例,为此它非常适合。

您可以在窗口的构造函数中将主视图模型的实例分配给主窗口的 DataContext,

public MainWindow()
{
    InitializeComponent();

    DataContext = new MainWindowViewModel();
}

或在 XAML 中作为
<Window.DataContext>
    <vm:MainWindowViewModel />
<Window.DataContext>

两者都不是特别可取,只是不要在 UserControl 中做任何一个。您的主窗口几乎是 View (正确考虑的窗口是 View )应该创建自己的 View 模型的唯一时间。

使其成为资源不会添加任何内容。您绑定(bind)到 Grid.DataContext 是一个坏主意——您很少将任何人的 DataContext 绑定(bind)到任何东西;这与 Will 在您的另一个问题中所说的有关 - 但即使这是一个好主意,这也是绑定(bind)的样子:
<Grid
    DataContext="{Binding Source={StaticResource ViewModel}}"
    >

但不要那样做!

使用正确数据显示该用户控件可以做的一件事是为您的 View 模型创建“隐式数据模板”,这些 View 模型将显示在像这样的父级中。

例如:

应用程序.xaml
<!-- No x:Key, just DataType: It'll be implicitly used for that type. -->
<DataTemplate DataType="{x:Type vm:DirFilesViewModel>
    <local:ctlDirFilesListBox />
</DataTemplate>

然后在 MainWindow.xaml 中:
<UserControl 
    Grid.Row="0"
    Grid.Column="0"
    Content="{Binding DirFilesViewModel}" 
    />

XAML 将转到窗口的 DataContext 以获取名为 DirFilesViewModel 的属性。 .它发现有一个对象是该类的实例,也名为 DirFilesViewModel .它知道它有该类的 DataTemplate,因此它使用该数据模板。

这非常强大:假设您有一个 ObservableCollection<ViewModelBase>具有十种不同 View 模型的三十个实例和不同的 View ,并且用户选择一个或另一个。所选 View 模型位于名为 SelectedChildVM 的 mainviewmodel 属性中。 .这是使用正确 View 显示 SelectedChildVM 的 XAML:
<ContentControl Content="{Binding SelectedChildVM}" />

就是这样。

一路前行:
        <!--
            Need to access the `ItemsSource="{Binding }"` and
             `SelectedItem="{Binding Path=}"` of the ListBox in 
             `ctlDirFilesListBox` view -->

不,你没有!这是你想做的最后一件事!一些用户控件有自己的属性,而不是 View 模型。有了这些,您可以像任何控件一样绑定(bind)父级中的属性。

这是 UserControls 的一个不同用例:它通过继承 View 模型作为其 DataContext 来“参数化”。你给它的信息是 View 模型。

UserControl 中的控件应该有自己的绑定(bind),它们从 UserControl 的 View 模型的属性中获取这些内容。

让我们假设用户控件的 View 模型(我猜这就是 DirFilesViewModel)有一个 Files属性 ( ObservableCollection<SomeFileClass> ) 和 SelectedFile类(SomeFileClass)。您可能不需要 ListBoxItem_SelectionChanged .
<UserControl x:Class="CatalogInterface.ctlDirFilesListBox"
         xmlns:local="clr-namespace:CatalogInterface"
         xmlns:vm="clr-namespace:CatalogInterface.ViewModels"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="MainControlGrid">           
    <ListBox 
        ItemsSource="{Binding Files}"
        SelectedItem="{Binding SelectedFile}"
        SelectionChanged="ListBoxItem_SelectionChanged" 
        HorizontalAlignment="Stretch" 
        VerticalAlignment="Stretch" 
        Background="#FFFFFF"
        Grid.Row="2" 
        Grid.Column="1" 
        Grid.ColumnSpan="3" 
        BorderThickness="0"
        >
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
                <EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
                <EventSetter Event="KeyDown" Handler="ListBoxItem_KeyDown"/>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
</Grid>
</UserControl>

关于c# - MVVM 将嵌套 subview 连接到 subview 模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46597056/

相关文章:

c# - 为什么在使用 Prism EventAggregator 时没有调用我的订阅方法?

android - MVP架构与Android中的Rx Observables混合在一起?

c# - 如何使用 LINQ 拆分字符串

c# - 移动实时视频编码

c# - 如何循环从同一个数组中删除一个值的所有实例?

c# - WPF "undo"深拷贝

WPF + PRISM - 显示带有控件的模式弹出窗口?

c# - WPF UserControl with Binding Mode=OneWay

c# - 为什么 ICommand 比调用 VM 的代码更好?

ios - 在 MVVM + Coordinator 中,如何处理 subview ?