C# 嵌套视图模型的双向绑定

C# 嵌套视图模型的双向绑定,c#,xaml,mvvm,devexpress,catel,C#,Xaml,Mvvm,Devexpress,Catel,使用Catel 3.9和DevExpress 15.x 我的客户要求我对应用程序进行一些UI更改,但我不确定是否可以轻松完成 架构: 有一个具有关联视图和ViewModel的主窗口 主窗口包含一个选项卡控件,其中每个选项卡的内容都是一个单独的视图/视图模型。MainWindow ViewModel不拥有任何嵌套的VM;它们由Catel在运行时通过视图自动构造 旧的UI在每个选项卡项上都有按钮,允许客户加载、保存、显示、筛选等。这些按钮的命令/属性直接绑定到该选项卡的ViewModel,工作正

使用Catel 3.9和DevExpress 15.x

我的客户要求我对应用程序进行一些UI更改,但我不确定是否可以轻松完成

架构:

  • 有一个具有关联视图和ViewModel的主窗口
  • 主窗口包含一个选项卡控件,其中每个选项卡的内容都是一个单独的视图/视图模型。MainWindow ViewModel不拥有任何嵌套的VM;它们由Catel在运行时通过视图自动构造
旧的UI在每个选项卡项上都有按钮,允许客户加载、保存、显示、筛选等。这些按钮的命令/属性直接绑定到该选项卡的ViewModel,工作正常

客户希望有一个单一的顶层(在主窗口上)菜单,从该菜单中进行的选择将影响具有当前焦点的选项卡

我可以将命令(使用Messaging或Catel的InterestedIn属性)传递给正确的ViewModel,但我希望与顶级菜单和适当的ViewModel进行更直接的绑定,以便启用/禁用菜单项,甚至修改文本以适应打开的选项卡

我正在寻找主要是XAML和/或Catel解决方案。如果你需要更多的信息,请告诉我

建议

谢谢, 兰迪

编辑:很抱歉,我没有包括额外的研究。如果你认识我,你就会知道我会花上数小时/数天的时间寻找问题的解决方案,只有当我被难倒时,我才会寻求帮助。我的缺点是没有包括更多

这个问题最难的部分是定义好的搜索参数。大多数建议类似于:只需将所有内容放入主窗口ViewModel,这(对我来说)不是一个好的设计选择,因为选项卡上显示的内容不同,应该分开

其他解决方案是让主窗口ViewModel构建每个内部ViewModel,然后对其进行管理。在我使用的Catel框架中,该框架在加载视图时自动构造VM,向构造函数注入任何必需的参数。请参见下文——您只需引用视图,Catel就会将其与ViewModel匹配,并为您创建它。不幸的是,如果不采取其他步骤,您实际上没有对创建的VM的引用

MainWindow.xaml:

<dx:DXTabControl x:Name="MainTabControl" 
                         Grid.Row="1"
                         Margin="10" 
                         BorderThickness="0" 
                         SelectedIndex="{Binding SelectedTabIndex}"
                         >
            <dx:DXTabItem Header="Getting Started" IsEnabled="True">
                <views:GetStarted />
            </dx:DXTabItem>
            <dx:DXTabItem Header="Validate Student Records" Background="Ivory">
                <views:StudentValidation />
            </dx:DXTabItem>
            <dx:DXTabItem Header="Validate Teacher Records" Background="Ivory">
                <views:TeacherValidation />
            </dx:DXTabItem>
            <dx:DXTabItem Header="Validate Admin Records" Background="Ivory">
                <views:AdminRecordValidation />
            </dx:DXTabItem>
        </dx:DXTabControl>

我正在研究的一些可能的解决方案示例:

编辑#2:有人建议使用某项服务,因此不允许您添加详细的评论(而且它会在您的评论上设置一个计时器--SHEEESH!),因此我将在这里给出我的回答

考虑使用服务的这种情况:客户启动应用程序并单击选项卡(StudentValidation)。MainWindowViewModel(通过属性)检测所选选项卡并调用带有更新的服务(我不确定更新了什么;可能是某种状态)。“嵌套”视图模型将(通过事件)通知服务中的更改。我假设StudentValidationViewModel是唯一一个真正响应事件并与服务交互、检索“数据”的人


现在我们显示了StudentValidation选项卡,客户进入应用程序的主菜单。主菜单仍然绑定到MainWindow,并且每个命令都绑定到MainWindowViewModel。服务如何将主菜单绑定到当前选定选项卡的ViewModel,以便StudentValidationViewModel处理这些命令?我可能遗漏了什么。

使用一个保存共享数据的单例模型,这样您就可以从任何您喜欢的地方获取实例。

服务就是解决方案。创建注入所有视图模型的解决方案。然后,顶级vm可以更新服务,所有vm都可以通过事件响应更新


请记住,虚拟机只代表内存中的实时视图,因此您可以与它们交互。

感谢您的所有建议。我试着给每一个都投票,但作为一个“新手”,我不能。当我完成每一项工作时,我意识到我所要做的就是将主菜单项的特定子集绑定到主窗口上当前焦点选项卡的视图/视图模型。这看起来就像更改菜单项的DataContext一样简单

这是主菜单。FileSubMenu是我需要绑定到当前聚焦的ViewModel的子菜单。其他菜单项可由MainWindowViewModel处理

MainWindow.xaml:

<dxb:MainMenuControl Grid.Row="0" 
                     HorizontalAlignment="Left"
                     HorizontalContentAlignment="Stretch"
                     VerticalAlignment="Top"
                     BarItemDisplayMode="ContentAndGlyph"
                     >
    <dxb:BarStaticItem Content="Validator" Glyph="pack://application:,,,/LexValidator;component/Images/lex-logo.png" />
    <dxb:BarSubItem x:Name="FileSubMenu" Content="File">
        <dxb:BarButtonItem Content="{Binding LoadRecordsText}" Glyph="{dx:DXImage Image=LoadFrom_16x16.png}" Command="{Binding LoadRecordsFile}"/>
        <dxb:BarButtonItem Content="Clear Display" Glyph="{dx:DXImageOffice2013 Image=Clear_16x16.png}" Command="{Binding ClearDisplay}"/>
        <dxb:BarButtonItem Content="FTE Counts..." Glyph="{dx:DXImage Image=TextBox_16x16.png}" Command="{Binding ShowFTECounts}"/>
        <dxb:BarButtonItem Content="Show Pay Grid..." Glyph="{dx:DXImage Image=Financial_16x16.png}" Command="{Binding ShowPayGrid}"/>
        <dxb:BarItemLinkSeparator />
        <dxb:BarCheckItem Content="Show Ignored Issues" Glyph="{dx:DXImage Image=ClearFilter_16x16.png}" IsChecked="{Binding ShowIgnoredIssues}" IsEnabled="{Binding IsShowIgnoredIssuesEnabled}" />
    </dxb:BarSubItem>
    <dxb:BarSubItem Content="Exit">
        <dxb:BarButtonItem Content="Exit" Glyph="{dx:DXImage Image=Close_16x16.png}" Command="{Binding ExitApplication}"/>
    </dxb:BarSubItem>
    <dxb:BarSubItem Content="Help">
        <dxb:BarButtonItem Content="About..." Glyph="{dx:DXImageGrayscale Image=Index_16x16.png}" Command="{Binding ShowAboutBox}"/>
    </dxb:BarSubItem>
</dxb:MainMenuControl>
我意识到这可能不是很“MVVM”,但它工作得很好,“老板”说“继续做其他事情”。如果上面的代码可以完全用XAML处理,我会更高兴——也许是某种资源


同样,如果我遗漏了什么或者有更好的(更多MVVM)解决方案,请让我知道。

我想我知道你的想法。有趣的是——让我看看这些方法是否有效,是否仍然适用于MVVM。(即,我们确实不希望ViewModel可以直接访问菜单UI元素进行修改。)谢谢!哦,我正在一个项目中使用这种方法。这真的取决于你的应用程序的架构,但它为我提供了很好的服务。我一直在我的应用程序中使用服务,它们非常有用。但是,虽然它们解决了很多问题,但我不确定它们是否解决了将主窗口视图中的某些内容与所选选项卡的ViewModel绑定的问题。请参见上面的编辑#2。谢谢
private void MainTabControl_SelectionChanged(object sender, TabControlSelectionChangedEventArgs e)
{
    // Turn it off and see if it needs to be enabled.
    this.FileSubMenu.IsEnabled = false;

    var newTabItem = e.NewSelectedItem as DXTabItem;
    if (newTabItem != null)
    {
        var tabView = newTabItem.Content as UserControl;
        if (tabView != null)
        {
            var tabViewModel = tabView.ViewModel;
            if (tabViewModel != null)
            {
                this.FileSubMenu.DataContext = tabViewModel;
                this.FileSubMenu.IsEnabled = true;
            }
        }
    }
}