【问题标题】:Shared viewmodel between two Views in WPF MVVM patternWPF MVVM模式中两个视图之间的共享视图模型
【发布时间】:2015-04-13 14:28:42
【问题描述】:

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

共享视图模型

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>

模态视图模型

class ModalViewModel : Screen
{        
    public ModalViewModel()
    {

    }        
}

所以基本思想是让两个视图共享同一个视图模型。此视图模型具有显示模式内容和删除记录的属性。

这里的问题是ConfirmDelete 方法永远不会被调用。我想问题是子视图DataContext 与父视图不同。那么我该如何解决呢?

谢谢!

编辑

忘了说,我用的是 Caliburn.Micro

编辑 2

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

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

测试视图

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

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

编辑 3

窥探结果:

【问题讨论】:

  • 我认为您误解了按钮如何与命令挂钩。看看here
  • @user1 我会冒险猜测虚拟机源自Screen,这就是 Caliburn Micro。 Caliburn 会神奇地将按钮绑定到与按钮同名的方法。
  • 感谢您的评论。忘了提我正在使用 Caliburn.Micro 所以我不需要显式创建 ICommand
  • @CharlesMager 啊,好吧,我的错。
  • 对视图排列有点困惑。为什么要将 ShowDeleteConfirm 按钮放在 ModalContentPresenter 中?

标签: c# wpf mvvm caliburn.micro


【解决方案1】:

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

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

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

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

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

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

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

<!-- 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。一个用于客户端屏幕,一个用于删除对话框屏幕。

虽然最好的解决方案可能是正确分离您的代码,以便所有删除代码都在一个对象中,而所有客户端代码都在另一个对象中

<!-- 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,所以我不确定我能在这方面为您提供帮助。

快速的 google 搜索表明它可能与命名控件不直接位于主视图中的事实有关,因此 Caliburn 在视图中查找具有该特定名称的元素的搜索可能无法按预期工作。

This answer 建议显式编写绑定,如下所示:

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

【讨论】:

  • 感谢瑞秋的帮助。所以我听从你的建议,从 ClientesViewModel 中删除 ConfirmDelete 并将其放入 ModalViewModel。这似乎合乎逻辑。问题仍然存在:ModalView 似乎与它的视图模型断开连接,当我单击按钮时,从不调用 ConfirmDelete 方法。我在这里想念什么?谢谢!
  • @ercpap 我最好的猜测是你的ModalContentPresenter UserControl 显示自定义ModelContent 属性的方式。您可以分享该控件的相关 XAML 吗?
  • 我也认为这是我的问题,但似乎不是。请参阅我的编辑 2,我将 modalcontenPresenter 更改为简单的 contentpresenter。我的方法仍然没有被调用。我也根据您的建议重新排列视图模型。谢谢!
  • @ercpap 查看我答案底部的更新:)
  • 哇!做到了。极好的。现在唯一的问题是 TryClose() 不起作用,我想它必须与创建视图的方式有关。现在我只需要找到一种方法来关闭创建的 TestView。谢谢!
【解决方案2】:

我认为问题在于命名约定不起作用,因为未使用 Caliburn.Micro 的 ViewLocator 设置视图。

尝试显式设置模型。

<local:ModalContentPresenter cal:Bind.Model="{Binding}" 
          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>

【讨论】:

  • 命名约定是个问题。但是这段代码对此没有帮助。雷切尔代码成功了。谢谢!
猜你喜欢
  • 2017-01-14
  • 2014-10-24
  • 2014-10-11
  • 2011-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多