MVVM将嵌套的子视图连接到子视图模型

时间:2017-10-06 01:36:41

标签: c# wpf mvvm nested

我正在尝试在我已经在使用嵌套视图的应用程序中构建嵌套的ViewModel。以下是我想要做的一个例子:

MainWindow查看:

<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>

子视图:

<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>

MainWindowViewModel

using System;
using System.Text;

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

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

所以,我需要挂钩ListBox.SelectedItemListBox.ItemSource来绑定MainWindowViewModel.DirFilesViewModel中的属性。我必须在MainWindow View而不是ctlDirListBox视图中进行绑定。

如何访问子视图中的元素?我认为那是我最大的障碍。我认为我的所有数据上下文都是正确的,我只是无法纠缠子视图元素。

2 个答案:

答案 0 :(得分:3)

我假设DirFilesViewModel是该usercontrol的viewmodel。如果情况并非如此,请告诉我实际情况是什么,我们会整理出来。

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

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

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

public MainWindow()
{
    InitializeComponent();

    DataContext = new MainWindowViewModel();
}

或在XAML中

<Window.DataContext>
    <vm:MainWindowViewModel />
<Window.DataContext>

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

使其成为一种资源并不能添加任何东西。你对Grid.DataContext的绑定是一个坏主意 - 你很少将任何人的DataContext绑定到任何东西;这与Will在你的另一个问题中所讨论的内容有关 - 但即使这是个好主意,这就是绑定的样子:

<Grid
    DataContext="{Binding Source={StaticResource ViewModel}}"
    >

但不要这样做!

使用正确的数据显示usercontrol可以做的一件事是创建&#34;隐式数据模板&#34;为您的视图模型显示在像这样的父母中。

例如:

的App.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,因此它使用该datatemplate。

这非常强大:想象一下,你有一个ObservableCollection<ViewModelBase>,其中包含十种不同视图模型的三十个实例,这些视图模型具有不同的视图,用户可以选择一个或另一个。选定的viewmodel位于名为SelectedChildVM的mainviewmodel属性中。这是使用正确视图显示SelectedChildVM的XAML:

<ContentControl Content="{Binding SelectedChildVM}" />

那就是它。

继续前进:

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

不,你不要!这是你想做的最后一件事!某些UserControl具有自己的属性,而不是viewmodel。有了这些,您可以像任何控件一样绑定父级中的属性。

这是UserControls的另一个用例:它已参数化&#34;通过继承viewmodel作为其DataContext。您提供的信息是viewmodel。

UserControl中的控件应该有自己的绑定,它们从UserControl的viewmodel的属性中获取这些内容。

让我们假设用户控件的视图模型(我猜测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>

答案 1 :(得分:0)

  

如何访问子视图中的元素?

您可以将两个依赖项属性(例如名为ItemsSourceSelectedItem)添加到ctlDirFilesListBox控件的代码隐藏类中,并在父窗口中绑定到这些:

<local:ctlDirFilesListBox ItemsSource="{Binding Property}" SelectedItem="{Binding Property}" />

您还应该在UserControl

中绑定这些属性
<ListBox SelectionChanged="ListBoxItem_SelectionChanged" 
                 HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFFFFF"
                 Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" BorderThickness="0"
                 ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
                 SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=UserControl}}">
    <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>
public class ctlDirFilesListBox : UserControl
{
    //...

    public static readonly DependencyProperty ItemsSourceProperty =
         DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ctlDirFilesListBox));

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("ItemsSource", typeof(object), typeof(ctlDirFilesListBox));

    public object SelectedItem
    {
        get { return GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }
}