提问者:小点点

PRISM/WPF视图/视图模型被创建两次


我有一个Prism7/WPF/MVVM应用程序,它在具有ViewModel的视图中配置了autoWireviewModel=“true”。 我的大多数视图模型都具有在Prism Unity容器中配置的依赖项,这些依赖项被注入到viewmodel Contructor中。 我没有在任何地方后面的代码中显式地创建Views/ViewModel的实例。 我也没有在XAML中设置数据上下文,即d:dataContext)。 HamburgerMenu控件的一部分)。

除了每个View/ViewModel由于某种原因被构造了两次之外,所有的工作都很好。 我在模块管理器中放置了断点(只命中一次)--并且在视图构造函数和viewmodel构造函数中也放置了断点,它们都被命中两次。 下面是我为一个名为Messaging的模块编写的代码,该模块是在运行时加载的,而不是按需加载的--我不完全确定要发布什么代码。 由于希望尽可能多地使用MVVM模式,我在视图后面的代码中只有很少的代码。

模块管理器(我省略了RegisterTypes方法,因为依赖项没有问题):

public void OnInitialized(IContainerProvider containerProvider)
{
    // Register main view with region manager
    var regionManager = containerProvider.Resolve<IRegionManager>();
    regionManager.RegisterViewWithRegion("MainRegion", typeof(MessagingMainView));
}

MessagingMainView:

<UserControl x:Class="Ascensos.Wpf.Modules.Messaging.Views.MessagingMainView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
         xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
         xmlns:materialDesignConverters="clr-namespace:MaterialDesignThemes.Wpf.Converters;assembly=MaterialDesignThemes.Wpf"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:views="clr-namespace:Ascensos.Wpf.Modules.Messaging.Views"
         xmlns:helpers="clr-namespace:Ascensos.Wpf.Modules.Messaging.Helpers"
         xmlns:prism="http://prismlibrary.com/"
         prism:ViewModelLocator.AutoWireViewModel="True"
         d:DesignHeight="300"
         d:DesignWidth="400"
         mc:Ignorable="d">

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Themes/HamburgerMenu.xaml" />
        </ResourceDictionary.MergedDictionaries>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
        <helpers:HamburgerMenuIconItemTemplateSelector x:Key="myDataTemplateSelector"/>

        <!--  This is the template for the menu items (no badge/count control).  -->
        <DataTemplate x:Key="MenuItemTemplate" DataType="{x:Type Controls:HamburgerMenuIconItem}">
            <Grid Height="48">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="48" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <ContentControl Grid.Column="0"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                Content="{Binding Icon}"
                                Focusable="False"
                                IsTabStop="False" />
                <TextBlock Grid.Column="1"
                           VerticalAlignment="Center"
                           FontSize="16"
                           Text="{Binding Label}" />
            </Grid>
        </DataTemplate>

        <!--  This is the template for the menu items (with badge/count control).  -->
        <DataTemplate x:Key="MenuItemTemplateBadged" DataType="{x:Type Controls:HamburgerMenuIconItem}">
            <Grid Height="48">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="48" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <Controls:Badged Grid.Column="0"
                                 Badge="{Binding Tag.DataContext.UnreadMessagesCount}"
                                 BadgeBackground="Red"
                                 HorizontalAlignment="Center"
                                 VerticalAlignment="Center">
                    <ContentControl Content="{Binding Icon}"
                                    Margin="2"
                                    Focusable="False"
                                    IsTabStop="False" />
                </Controls:Badged>
                <TextBlock Grid.Column="1"
                           VerticalAlignment="Center"
                           FontSize="16"
                           Text="{Binding Label}" />
            </Grid>
        </DataTemplate>

        <!--  This is the template for all menu items. In this sample we use the glyph items.  -->
        <DataTemplate x:Key="HamburgerMenuItem" DataType="{x:Type Controls:HamburgerMenuGlyphItem}">
            <DockPanel Height="48" LastChildFill="True">
                <Grid x:Name="IconPart"
                      Width="48"
                      DockPanel.Dock="Left">
                    <Image Margin="12"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           Source="{Binding Glyph}"
                           Stretch="UniformToFill" />
                </Grid>
                <TextBlock x:Name="TextPart"
                           VerticalAlignment="Center"
                           FontSize="16"
                           Text="{Binding Label}" />
            </DockPanel>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:HamburgerMenu}}, Path=PanePlacement}" Value="Right">
                    <Setter TargetName="IconPart" Property="DockPanel.Dock" Value="Right" />
                    <Setter TargetName="TextPart" Property="Margin" Value="8 0 0 0" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>

        <!--  This is the template for the option menu item  -->
        <DataTemplate x:Key="HamburgerOptionsMenuItem" DataType="{x:Type Controls:HamburgerMenuIconItem}">
            <DockPanel Height="48" LastChildFill="True">
                <ContentControl x:Name="IconPart"
                                Width="48"
                                Content="{Binding Icon}"
                                DockPanel.Dock="Left"
                                Focusable="False"
                                IsTabStop="False" />
                <TextBlock x:Name="TextPart"
                           VerticalAlignment="Center"
                           FontSize="16"
                           Text="{Binding Label}" />
            </DockPanel>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:HamburgerMenu}}, Path=PanePlacement}" Value="Right">
                    <Setter TargetName="IconPart" Property="DockPanel.Dock" Value="Right" />
                    <Setter TargetName="TextPart" Property="Margin" Value="8 0 0 0" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>

        <ObjectDataProvider x:Key="DisplayModeEnumValues"
                            MethodName="GetValues"
                            ObjectType="{x:Type Controls:SplitViewDisplayMode}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="Controls:SplitViewDisplayMode" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>

        <ObjectDataProvider x:Key="VisibilityEnumValues"
                            MethodName="GetValues"
                            ObjectType="{x:Type Visibility}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="Visibility" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>

        <materialDesignConverters:BrushRoundConverter x:Key="BrushRoundConverter" />

        <Style x:Key="MahApps.Styles.ListBoxItem.HamburgerMenuItem.Ripple"
               BasedOn="{StaticResource MahApps.Styles.ListBoxItem.HamburgerMenuItem}"
               TargetType="{x:Type ListBoxItem}">
            <Setter Property="Controls:ItemHelper.ActiveSelectionBackgroundBrush" Value="Transparent" />
            <Setter Property="Controls:ItemHelper.ActiveSelectionForegroundBrush" Value="{DynamicResource MahApps.Brushes.AccentBase}" />
            <Setter Property="Controls:ItemHelper.DisabledForegroundBrush" Value="{DynamicResource MahApps.Brushes.Gray}" />
            <Setter Property="Controls:ItemHelper.DisabledSelectedBackgroundBrush" Value="Transparent" />
            <Setter Property="Controls:ItemHelper.DisabledSelectedForegroundBrush" Value="{DynamicResource MahApps.Brushes.Gray}" />
            <Setter Property="Controls:ItemHelper.HoverBackgroundBrush" Value="{DynamicResource MahApps.Brushes.Gray9}" />
            <Setter Property="Controls:ItemHelper.HoverSelectedBackgroundBrush" Value="{DynamicResource MahApps.Brushes.Gray9}" />
            <Setter Property="Controls:ItemHelper.SelectedBackgroundBrush" Value="Transparent" />
            <Setter Property="Controls:ItemHelper.SelectedForegroundBrush" Value="{DynamicResource MahApps.Brushes.AccentBase}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Grid Background="{TemplateBinding Background}" RenderOptions.ClearTypeHint="{TemplateBinding RenderOptions.ClearTypeHint}">
                            <Border x:Name="Border"
                                    Background="{TemplateBinding Background}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                            <Grid Margin="{TemplateBinding BorderThickness}">
                                <Grid HorizontalAlignment="Left"
                                      VerticalAlignment="Center"
                                      Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:HamburgerMenu}}, Path=ShowSelectionIndicator, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
                                    <Rectangle x:Name="SelectionIndicator"
                                               Width="{DynamicResource HamburgerMenuSelectionIndicatorThemeWidth}"
                                               Height="{DynamicResource HamburgerMenuSelectionIndicatorThemeHeight}"
                                               Fill="{TemplateBinding Foreground}"
                                               Focusable="False"
                                               Opacity="0.0" />
                                </Grid>
                                <materialDesign:Ripple Padding="{TemplateBinding Padding}"
                                                       HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                       VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                                       Content="{TemplateBinding Content}"
                                                       ContentTemplate="{TemplateBinding ContentTemplate}"
                                                       ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
                                                       Feedback="{TemplateBinding Foreground, Converter={StaticResource BrushRoundConverter}}"
                                                       Focusable="False"
                                                       SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                            </Grid>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:ItemHelper.SelectedForegroundBrush), Mode=OneWay}" />
                                <Setter TargetName="Border" Property="Background" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Controls:ItemHelper.SelectedBackgroundBrush), Mode=OneWay}" />
                                <Setter TargetName="SelectionIndicator" Property="Opacity" Value="1.0" />
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="True" />
                                    <Condition Property="Selector.IsSelectionActive" Value="True" />
                                </MultiTrigger.Conditions>
                                <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:ItemHelper.ActiveSelectionForegroundBrush), Mode=OneWay}" />
                                <Setter TargetName="Border" Property="Background" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Controls:ItemHelper.ActiveSelectionBackgroundBrush), Mode=OneWay}" />
                            </MultiTrigger>

                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True" />
                                    <Condition Property="IsSelected" Value="True" />
                                </MultiTrigger.Conditions>
                                <Setter TargetName="Border" Property="Background" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Controls:ItemHelper.HoverSelectedBackgroundBrush), Mode=OneWay}" />
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True" />
                                    <Condition Property="IsSelected" Value="False" />
                                </MultiTrigger.Conditions>
                                <Setter TargetName="Border" Property="Background" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Controls:ItemHelper.HoverBackgroundBrush), Mode=OneWay}" />
                            </MultiTrigger>

                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:ItemHelper.DisabledForegroundBrush), Mode=OneWay}" />
                                <Setter TargetName="Border" Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:ItemHelper.DisabledBackgroundBrush), Mode=OneWay}" />
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsEnabled" Value="False" />
                                    <Condition Property="IsSelected" Value="True" />
                                </MultiTrigger.Conditions>
                                <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:ItemHelper.DisabledSelectedForegroundBrush), Mode=OneWay}" />
                                <Setter TargetName="Border" Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:ItemHelper.DisabledSelectedBackgroundBrush), Mode=OneWay}" />
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="MahApps.Styles.HamburgerMenu.Ripple"
               BasedOn="{StaticResource MahApps.Styles.HamburgerMenu}"
               TargetType="{x:Type Controls:HamburgerMenu}">
            <Setter Property="ItemContainerStyle" Value="{StaticResource MahApps.Styles.ListBoxItem.HamburgerMenuItem.Ripple}" />
            <Setter Property="OptionsItemContainerStyle" Value="{StaticResource MahApps.Styles.ListBoxItem.HamburgerMenuItem.Ripple}" />
            <Setter Property="PaneBackground" Value="{DynamicResource MahApps.Brushes.ThemeBackground}" />
            <Setter Property="PaneForeground" Value="{DynamicResource MahApps.Brushes.Text}" />
            <Setter Property="ShowSelectionIndicator" Value="True" />
        </Style>

    </ResourceDictionary>
</UserControl.Resources>

<Grid>
    <Border Grid.Column="0"
            Margin="10 10 0 10"
            BorderBrush="{DynamicResource MahApps.Brushes.Gray7}"
            BorderThickness="1">
        <Controls:HamburgerMenu x:Name="HamburgerMenuControl"
                                HamburgerWidth="48"
                                IsPaneOpen="True"
                                CanResizeOpenPane="True"
                                ItemInvoked="HamburgerMenuControl_OnItemInvoked"
                                ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
                                SelectedItem="{Binding SelectedMenuItem}"
                                Style="{StaticResource MahApps.Styles.HamburgerMenu.Ripple}"
                                VerticalScrollBarOnLeftSide="False">

            <!--  Items  -->
            <Controls:HamburgerMenu.ItemsSource>
                <Controls:HamburgerMenuItemCollection>
                    <Controls:HamburgerMenuIconItem Icon="{iconPacks:FontAwesome Kind=CommentAltSolid}" Label="Chat">
                        <Controls:HamburgerMenuIconItem.Tag>
                            <views:ChatView />
                        </Controls:HamburgerMenuIconItem.Tag>
                    </Controls:HamburgerMenuIconItem>
                    <Controls:HamburgerMenuIconItem Icon="{iconPacks:FontAwesome Kind=PenSquareSolid}" Label="Compose">
                        <Controls:HamburgerMenuIconItem.Tag>
                            <views:ComposeMessageView />
                        </Controls:HamburgerMenuIconItem.Tag>
                    </Controls:HamburgerMenuIconItem>
                    <Controls:HamburgerMenuIconItem Icon="{iconPacks:Material Kind=InboxArrowDown}" Label="Inbox">
                        <Controls:HamburgerMenuIconItem.Tag>
                            <views:InboxView />
                        </Controls:HamburgerMenuIconItem.Tag>
                    </Controls:HamburgerMenuIconItem>
                </Controls:HamburgerMenuItemCollection>
            </Controls:HamburgerMenu.ItemsSource>

            <Controls:HamburgerMenu.ContentTemplate>
                <DataTemplate DataType="{x:Type Controls:HamburgerMenuIconItem}">
                    <Grid Margin="20 0 10 0">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <TextBlock Grid.Row="0"
                                   Margin="0 15 0 5"
                                   Padding="0"
                                   FontFamily="{DynamicResource MahApps.Fonts.Family.Header}"
                                   FontSize="{DynamicResource MahApps.Font.Size.Header}"
                                   Text="{Binding Label}" />
                        <ScrollViewer Grid.Row="1"
                                      Focusable="False"
                                      HorizontalScrollBarVisibility="Disabled"
                                      VerticalScrollBarVisibility="Auto">
                            <ContentControl Content="{Binding Tag}" Focusable="False" />
                        </ScrollViewer>
                    </Grid>
                </DataTemplate>
            </Controls:HamburgerMenu.ContentTemplate>
        </Controls:HamburgerMenu>
    </Border>
</Grid>

null

...

背后的代码:

public sealed partial class MessagingMainView : UserControl
{
    public MessagingMainView()
    {
        this.InitializeComponent();

        HamburgerMenuControl.SelectedItem = ((HamburgerMenuItemCollection)HamburgerMenuControl.ItemsSource)[0];
    }

    private void HamburgerMenuControl_OnItemInvoked(object sender, HamburgerMenuItemInvokedEventArgs e)
    {
        HamburgerMenuControl.Content = e.InvokedItem;
    }
}

viewmodel(调用基消息视图模型类):

public sealed class MessagingMainViewModel : BaseMessageViewModel
{
    private HamburgerMenuIconItem _selectedMenuItem;

    public HamburgerMenuIconItem SelectedMenuItem
    {
        get => _selectedMenuItem;
        set => SetPropertyAndNotify(ref _selectedMenuItem, value, () => SelectedMenuItem);
    }

    public MessagingMainViewModel(
        IRegionManager regionManager,
        IEventAggregator eventAggregator,
        IUserService userService,
        IChatService chatService,
        IMessageService messageService,
        IDialogService dialogService,
        IMetroMessageDisplayService metroMessageDisplayService,
        INotifyIconService notifyIconService,
        ILogger<MessagingMainViewModel> logger) : base(regionManager, eventAggregator, userService, chatService, messageService, dialogService, metroMessageDisplayService, notifyIconService, logger)
    {

    }
}

BaseMessagViewModel继承自BaseViewModel类:

public class BaseMessageViewModel : BaseViewModel
{
...

public BaseMessageViewModel(
        IRegionManager regionManager,
        IEventAggregator eventAggregator,
        IUserService userService,
        IChatService chatService,
        IMessageService messageService,
        IDialogService dialogService,
        IMetroMessageDisplayService metroMessageDisplayService,
        INotifyIconService notifyIconService,
        ILogger<BaseMessageViewModel> logger): base(regionManager, eventAggregator, dialogService, metroMessageDisplayService, notifyIconService)
    {
    ...

BaseViewModel类:

public abstract class BaseViewModel : DisposableObject, INotifyPropertyChanged
{
...

public BaseViewModel(
    IRegionManager regionManager = null,
    IEventAggregator eventAggregator = null,
    IDialogService dialogService = null,
    IMetroMessageDisplayService metroMessageDisplayService = null,
    INotifyIconService notifyServce = null)
{
...

共1个答案

匿名用户

我没有在任何地方后面的代码中显式地创建Views/ViewModel的实例(即仅在XAML中)。

这是一个矛盾--在xaml中创建视图模型意味着以与在后面或其他任何地方的代码中相同的方式创建一个。

就像之前说的,就是不要做。 从xaml中删除对视图模型构造函数的所有引用(如)。 若要在xaml中获取intellisense,请使用d:DataContext