WPF MVVM模式中两个视图之间的共享视图模型 [英] Shared viewmodel between two Views in WPF MVVM pattern

查看:87
本文介绍了WPF MVVM模式中两个视图之间的共享视图模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试寻找一种从其他视图中显示模态视图的技术,但是我遇到了问题。这是我要执行的操作的一个简单示例:

I'm trying to find a technique to show modal views from within other views but I am having problems. Here's a simple example of what i'm trying to do:

Shared ViewModel

class ClientesViewModel : Screen
{
    private bool _deleteconfirmvisible;
    public bool DeleteConfirmVisible
    {
        get { return _deleteconfirmvisible; }
        set
        {
            _deleteconfirmvisible = value;
            NotifyOfPropertyChange("DeleteConfirmVisible");
        }
    }

    public void ShowDeleteConfirm()
    {
        this.DeleteConfirmVisible = true;
    }

    public ModalViewModel ModalDelete
    {
        get { return new ModalViewModel(); }            
    }

    public void ConfirmDelete()
    {
        //Actually delete the record
        //WCFService.DeleteRecord(Record)
    }
}

第一视图

<UserControl x:Class="Ohmio.Client.ClientesView"
             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:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
             xmlns:local="clr-namespace:Ohmio.Client"   
             mc:Ignorable="d" 
             d:DesignHeight="364" d:DesignWidth="792">

    <UserControl.Resources>
        <DataTemplate DataType="{x:Type local:ModalViewModel}">
            <local:ModalView/>
        </DataTemplate>
    </UserControl.Resources>
    <local:ModalContentPresenter DataContext="{Binding}" IsModal="{Binding DeleteConfirmVisible}" Grid.ColumnSpan="5" Grid.RowSpan="4" ModalContent="{Binding Path=ModalDelete}">
        <Grid>        
            <Button x:Name="ShowDeleteConfirm" Margin="5" Grid.Column="2" Content="Delete Record"/>        
        </Grid>
    </local:ModalContentPresenter>
</UserControl>

第二视图(模态内容)

<UserControl x:Class="Ohmio.Client.ModalView"
             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:Ohmio.Client"   
             mc:Ignorable="d" Height="145" Width="476">
    <Grid>        
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>            
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="50"></RowDefinition>
        </Grid.RowDefinitions>        
        <Label Content="Are you sure you want to delete this record?" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>
        <Button x:Name="ConfirmDelete" IsDefault="True" Grid.Row="2" Content="Aceptar" Margin="10"></Button>
        <Button x:Name="TryClose" IsCancel="True"  Grid.Row="2" Grid.Column="1" Content="Cancelar" Margin="10"></Button>        
    </Grid>        
</UserControl>

ModalViewModel

class ModalViewModel : Screen
{        
    public ModalViewModel()
    {

    }        
}

因此,基本思想是拥有两个共享相同视图模型的视图。这个视图模型具有显示模态内容和删除记录的属性。

So the basic idea is to have two views that share the same viewmodel. This viewmodel has properties for showing the modal content and deleting the record.

这里的问题是 ConfirmDelete 方法是我猜问题是子视图 DataContext 与父视图不同。那么我该如何解决呢?

The problema here is that ConfirmDelete method is never called.I guess the problem is that the child views DataContext is different from parent view. So How can I Solve this?

谢谢!

编辑

忘记了,我使用的是Caliburn.Micro

Forgot to mention, i'm using Caliburn.Micro

编辑2

我按照Rachel的建议对视图模型进行划分。问题仍然存在。这是我的代码现在的样子:

I follow Rachel suggest and divide the viewmodel. Still the problem pesist. Here is how my code looks like now:

TestViewModel

class TestViewModel :Screen
    {
        private bool _deleteconfirmvisible;
        TestModalViewModel _modaldelete;

        public TestViewModel()
        {
            _modaldelete = new TestModalViewModel();
        }
        public bool DeleteConfirmVisible
        {
            get { return _deleteconfirmvisible; }
            set
            {
                _deleteconfirmvisible = value;
                NotifyOfPropertyChange("DeleteConfirmVisible");
            }
        }

        public void ShowDeleteConfirm()
        {
            this.DeleteConfirmVisible = true;
        }

        public TestModalViewModel ModalDelete
        {
            get { return _modaldelete; }
        }
    }

TestView

<UserControl x:Class="Ohmio.Client.TestView"
             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:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
             xmlns:local="clr-namespace:Ohmio.Client"   
             mc:Ignorable="d" 
             d:DesignHeight="364" d:DesignWidth="792">
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type local:TestModalViewModel}">
            <local:TestModalView/>
        </DataTemplate>
    </UserControl.Resources>    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="40"></RowDefinition>
            </Grid.RowDefinitions>
        <ContentPresenter Content="{Binding Path=ModalDelete}"></ContentPresenter>
        <Button x:Name="ShowDeleteConfirm" Margin="5" Grid.Row="1" Content="Delete Record"/>
        </Grid>    
</UserControl>

TestModalViewModel

class TestModalViewModel : Screen
    {
        private Boolean _result;

        public TestModalViewModel()
        {
            _result = false;
        }        

        public void ConfirmAction()
        {
            _result = true;
            TryClose();
        }        

        public bool Result
        {
            get { return _result; }            
        }
    }    

TestModalView

<UserControl x:Class="Ohmio.Client.TestModalView"
             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:Ohmio.Client"   
             mc:Ignorable="d" Height="145" Width="476">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"></RowDefinition>            
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>

        <Label Content="Are you sure you want to delete this record?" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>        
        <Button x:Name="ConfirmAction" IsDefault="True" Grid.Row="2" Content="Aceptar" Margin="10"></Button>
        <Button x:Name="TryClose" IsCancel="True"  Grid.Row="2" Grid.Column="1" Content="Cancelar" Margin="10"></Button>
    </Grid>
</UserControl>

我更改了ModalContentPresenter for和ContentPresenter,问题仍然存在:ConfirmAction从未被调用,我可以不明白为什么。谁能告诉我为什么?

I change the ModalContentPresenter for and ContentPresenter, and the problema remains: ConfirmAction is never called, and i can't understand why. Can anyone tell me why?

编辑3

监听结果:

推荐答案

使用隐式DataTemplate时,WPF会自动将 .DataContext 属性设置为任何数据对象。

When you use an implicit DataTemplate, WPF automatically sets the .DataContext property to whatever the data object was.

<DataTemplate DataType="{x:Type local:ModalViewModel}">
    <local:ModalView/> <!-- DataContext is set to the ModelViewModel object -->
</DataTemplate>

因此, ModelView 控件的任何实例都将将它的 .DataContext 设置为 ModelViewModel 对象以进行绑定。

So any instance of your ModelView control will have it's .DataContext set to a ModelViewModel object for binding purposes.

您可以更改特定的绑定以指向与当前DataContext不同的Source,例如:

You could change your specific bindings to point to a different Source than the current DataContext, like this :

<Button x:Name="ConfirmDelete" 
        Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:ModalContentPresenter}}, 
                          Path=DataContext.ConfirmDelete }" ... />

但是,这并不理想,因为它依赖于开发人员知道使用特定的UserControl结构,例如确保 ModelView 始终嵌套在 ModelContentPresenter 控件内部

however this is not ideal since it relies on the developer knowing to use a specific UserControl structure, such as making sure ModelView is always nested inside of a ModelContentPresenter controls

一个更好的解决方案是确保您的代码正确分开,并且ModelView只需要担心显示模型,而其他代码在模板的另一部分中。

A better solution is to ensure your code is properly separated, and the ModelView only has to worry about displaying the Model, while other code is in another part of the template.

<!-- This layer is used to display the entire ClientesViewModel object -->
<UserControl>

    <!-- this UserControl is only responsible for displaying the ModalViewModel object -->
    <UserControl> 
        <!-- However ModelViewModel should look... -->
        <Label Content="Are you sure you want to delete this record?" ... />
    </UserControl>

    <!-- DataContext here is ClientesViewModel, so these bindings work -->  
    <Button Content="Aceptar" Command="{Binding ConfirmDelete}" ... /> 
    <Button Content="Cancelar" Command="{Binding TryClose}" ... />

</UserControl>     

对于同一数据对象,您可以轻松拥有多个UserControl。一种是客户端屏幕,另一种是删除对话框屏幕。

You can easily have more than one UserControl for the same data object. One for the Clients screen, and one for the Delete dialog screen.

尽管最好的解决方案可能是正确分离代码,以便所有删除代码都在一个对象中,并且所有客户代码都在另一个

Although the best solution would probably be to separate your code properly so all delete code is in one object, and all Clients code is in another

<!-- This layer is used to display the entire ClientesViewModel object -->
<local:ClientsView>

    <!-- this UserControl is only responsible for displaying the ModalDelete object -->
    <local:DeleteView />

</local:ClientsView>

class ClientesViewModel
{
    bool DeleteConfirmVisible;
    void ShowDeleteConfirm();
    ModalViewModel ModalDelete;
}

public class ModalViewModel
{
    ICommand ConfirmDelete;
    ICommand TryClose;
}

编辑

基于对问题的更新,我最好的猜测是Caliburn Micro的自动魔术绑定的实现存在问题。我以前从未使用过Caliburn Micro,所以不确定是否可以为您提供帮助。

Based on the update to your question, my best guess is it is a problem with the implementation of Caliburn Micro's automagical bindings. I've never used Caliburn Micro before, so I'm not sure I can help you there.

一个快速的Google搜索表明,它可能与命名控件不直接位于主视图中有关,因此Caliburn的搜索是在视图中查找元素使用该特定名称的计算机可能无法正常工作。

A quick google search suggests it may have something to do with the fact the named control is not directly in the main View, so Caliburn's search to find an element in the View with that specific name may not be working as expected.

此答案建议显式编写绑定,如下所示:

This answer suggests writing the binding explicitly, like this :

<Button cal:Message.Attach="ConfirmDelete" />

这篇关于WPF MVVM模式中两个视图之间的共享视图模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆