WPF Prism 6 View Model不止一次创建

时间:2017-04-18 20:16:45

标签: c# wpf prism

我对Prism 6相对较新,并且已经查看了其他stackoverflow帖子,试图找到我的问题的答案。我有一个视图模块构造函数,每次导航到页面时都会执行。

在bootstrap文件的ConfigureContainer函数中,我有以下声明:

base.ConfigureContainer();

//Container.RegisterInstance<JobList>(new JobList());
Container.RegisterTypeForNavigation<JobList>("JobList");

使用JobList的Container.RegisterInstance函数似乎没什么区别。

JobList视图的顶部如下所示:

<UserControl x:Class="JobListModule.Views.JobList"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
    xmlns:prism="http://prismlibrary.com/"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    prism:ViewModelLocator.AutoWireViewModel="True"
    Width="1090" Height="900"
>
<i:Interaction.Triggers>
    <prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest, Mode=OneWay}">
        <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True"/>
    </prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="70*"/>
        <RowDefinition Height="830*"/>
    </Grid.RowDefinitions>
    <Grid Row="0" OpacityMask="#FFEEEBEB" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="83*"/>
            <ColumnDefinition Width="37*"/>
        </Grid.ColumnDefinitions>
        <Label x:Name="JobListLabel" Grid.Column="0" Content="{x:Static p:Resources.JobListBuildListLabelText}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="613" FontSize="48" Margin="67,-2,74,2" Height="70" RenderTransformOrigin="1.695,0.222" FontWeight="Bold" Padding="0" Grid.IsSharedSizeScope="True" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
        <Button x:Name="AddJobFileBtn" Command="{Binding AddJobBtnClickedCommand}" Grid.Column="1" Content="{x:Static p:Resources.JobListAddBuildFileBtnText}" HorizontalAlignment="Center" Margin="56,10,64,11" VerticalAlignment="Center" Width="216" Height="49" RenderTransformOrigin="0.203,-0.173" FontSize="32" FontWeight="Bold" Padding="0">
            <Button.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FFBEC9CF"/>
                    <GradientStop Color="#FF5EB4C7" Offset="0.936"/>
                    <GradientStop Color="#FFC9E9F0" Offset="0.319"/>
                </LinearGradientBrush>
            </Button.Background>
        </Button>
    </Grid>
    <ListBox x:Name="BuildList" Grid.Row="1" Margin="0,0,0,0" ItemsSource="{Binding Path=JobFileInfoList}" SelectedIndex="{Binding Path=JobListSelectedItemIndex, Mode=TwoWay}">
        <ListBox.ItemTemplate>
            <DataTemplate >
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="114*"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="750"/>
                        <ColumnDefinition Width="100*"/>
                        <ColumnDefinition Width="200*"/>
                    </Grid.ColumnDefinitions>
                    <Label x:Name="JobFileNameLabel" Grid.Column="0" Content="{Binding JobFileName}" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="24" FontWeight="Bold" Padding="0"/>
                    <dx:SimpleButton Command="{Binding DataContext.LoadJobBtnClickedCommand,RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding JobFileName}" Grid.Column="1" Content="{x:Static p:Resources.JobListPrintBtnLabelText}" HorizontalAlignment="Center" Margin="25,19,23,25" VerticalAlignment="Center" Width="120" Height="60" RenderTransformOrigin="0.203,-0.173" FontSize="32" FontWeight="Bold" Padding="0"/>
                    <dx:SimpleButton Command="{Binding DataContext.TrashCanBtnClickedCommand,RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding JobFileName}" Grid.Column="2" Margin="0" BorderThickness="0" HorizontalAlignment="Center" Width="86" Height="86" VerticalAlignment="Center" Padding="0" Background="{x:Null}">
                        <Image Source="..\ButtonImages\TrashCan-64.png" />
                    </dx:SimpleButton>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>
</UserControl>

和JobList.xaml.cs文件包含:

public partial class JobList
{
    public JobList()
    {
        InitializeComponent();
    }
}

JobList视图模型如下:

using Common.GatedEvents;
using CommonApp;
using Prism.Commands;
using Prism.Events;
using Prism.Interactivity.InteractionRequest;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using VulcanGUI.Services;
using VulcanGUI.Logging;
using VulcanGUI.DialogService;
using VulcanGUI.DialogService.ViewModels;

namespace VulcanGUI.JobListModule.ViewModels
{
public class JobListViewModel : BindableBase, IDescribe, IInteractionRequestAware, INavigationAware
{
    private readonly IRegionManager _regionManager;
    private readonly log4net.ILog _logger;
    protected readonly IEventAggregator EventAggregator;

    // Delete Job Queue AMX File Confirmation
    public InteractionRequest<IConfirmation> ConfirmationRequest { get; private set; }
    public InteractionRequest<INotification> ErrorNotification { get; private set; }

    private bool _deleteJobConfirmationRequestResult;
    private bool _printJobConfirmationRequestResult;
    public DelegateCommand RaiseDeleteJobConfirmationCommand { get; private set; }
    public DelegateCommand RaiseLoadJobConfirmationCommand { get; private set; }
    public ICommand LoadJobErrorNotificationCommand { get; private set; }

    private readonly IDialogService _openFileDialogService;
    private readonly IJobListDataService _jobListDataService;

    private string JobFilePath {get; set;}

    public int JobListSelectedItemIndex { get; set; }

    public Action FinishInteraction { get; set; }

    private ObservableCollection<JobFileInfoItem> _jobFileInfoList;

    public ObservableCollection<JobFileInfoItem> JobFileInfoList
    {
        get { return _jobFileInfoList; }
        set { _jobFileInfoList = value; }
    }

    public JobListViewModel(IRegionManager regionManager,
                            ILog4NetLogger logger, 
                            IDialogService openFileDialogSvc, 
                            IJobListDataService jobListDataSvc,
                            IEventAggregator eventAggregator
                           )
    {
        _regionManager = regionManager;
        EventAggregator = eventAggregator;

        ConfirmationRequest = new InteractionRequest<IConfirmation>();
        ErrorNotification = new InteractionRequest<INotification>();
        RaiseDeleteJobConfirmationCommand = new DelegateCommand(RaiseDeleteJobFromListConfirmation);
        RaiseLoadJobConfirmationCommand = new DelegateCommand(RaiseLoadJobConfirmation);
        LoadJobErrorNotificationCommand = new RelayCommand(LoadJobErrorNotification);
        _openFileDialogService = openFileDialogSvc;
        _jobListDataService = jobListDataSvc;
        _logger = logger.GetLog4NetLogger();

        AppEvents.MachineStatus.Event += MachineStatusHandler;
        AppEvents.JobFileReady.Event += JobFileReadyHandler;

        LoadJobFileInfoList();
    }

    public JobListViewModel() { }

    public string Describe(string preface = "")
    {
        return preface + " JobListViewModel";
    }

    public log4net.ILog log
    {
        get { return _logger; }
    }

    private void LoadJobErrorNotification(object errMsg)
    {
        //var vm = new ErrorDialogViewModel((string)errMsg);
        var vm = new ErrorDialogViewModel() {Message = (string) errMsg};
        var result = DialogService.DialogService.OpenDialog(vm);
    }

    protected void JobFileReadyHandler(Object sender, GatedEventBase thisEvent, JobReadyArgs args)
    {
            Application.Current.Dispatcher.Invoke(() => LoadJobErrorNotification(args.Error));
        if (args.Error.Length != 0)
        {
        }
        else
        {
            Application.Current.Dispatcher.Invoke(() => _regionManager.RequestNavigate("ViewsRegion", "BuildActivity", new NavigationParameters("Test")));
        }
    }

    protected void MachineStatusHandler(Object sender, GatedEventBase thisEvent, MachineStatusArgs args)
    {
        if (args.Request)
        {
            //CommonEvents.Processor.AddTask(new TaskEvent(this, AppEvents.MachineStatus, new MachineStatusArgs(OperationState)));
        }
    }

    private void LoadJobFileInfoList()
    {
        var jobFileInfoList = _jobListDataService.GetJobListFilePathData();

        _jobFileInfoList = new ObservableCollection<JobFileInfoItem>();

        foreach (string jobFile in jobFileInfoList)
        {
            _jobFileInfoList.Add(new JobFileInfoItem(jobFile));
        }
    }

    #region AddJobToList
    public ICommand AddJobBtnClickedCommand
    {
        get { return new DelegateCommand(AddJobBtnClickedMethod); }
        //get { return new DelegateCommand<object>(LoadJobBtnClickedMethod, CanLoadJobBtnClickedMethodBeExecuted); }
    }

    private void AddJobBtnClickedMethod()
    {
        ShowOpenFileDialog();
    }

    public void ShowOpenFileDialog()
    {
        var settings = new OpenFileDialogSettings()
        {
            Title = "Select Job File",
            InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
            Filter = "Build File ( *.amx; *.txt) |*.amx; *.txt"
        };

        if (_openFileDialogService.ShowOpenFileDialog(settings))
        {
            JobFilePath = settings.FileName;
            // Does the JobFilePath already exist in the list?
            if (_jobFileInfoList.Any(p => p.JobFileFullPath == JobFilePath))
            {
                // Since the path is already in the list, just show it as selected
                JobListSelectedItemIndex = _jobFileInfoList.FindIndex(p => p.JobFileFullPath == JobFilePath);
            }
            else
            {
                // Add it to the list
                _jobFileInfoList.Add(new JobFileInfoItem(JobFilePath));

                //Persist the list items
                List<string> jobFilePathInfo = JobInfoObsListToList();
                _jobListDataService.SaveJobListFilePathData(jobFilePathInfo);
            }
        }
    }
    #endregion AddJobToList

    #region DeleteJobFromList
    private void RaiseDeleteJobFromListConfirmation()
    {
        // By invoking the Raise method we are raising the Raised event and triggering any InteractionRequestTrigger that
        // is subscribed to it.
        // As parameters we are passing a Confirmation, which is a default implementation of IConfirmation (which inherits
        // from INotification) provided by Prism and a callback that is executed when the interaction finishes.
        ConfirmationRequest.Raise(
            new Confirmation { Content = "Remove this build file from the list?", Title = "Confirmation" },
            c => { _deleteJobConfirmationRequestResult = c.Confirmed; });
    }

    public ICommand TrashCanBtnClickedCommand
    {
        get { return new DelegateCommand<object>(TrashCanBtnClickedMethod); }
    }

    private void TrashCanBtnClickedMethod(Object jobFileName)
    {
        RaiseDeleteJobFromListConfirmation();

        if ( _deleteJobConfirmationRequestResult )
        {
            // Determine the index of the _jobFileInfoList item containing the filename
            var listIndex = _jobFileInfoList.FindIndex(p => p.JobFileName == (string)jobFileName);
            _jobFileInfoList.RemoveAt(listIndex);
            var jobFilePathInfo = JobInfoObsListToList();
            _jobListDataService.SaveJobListFilePathData(jobFilePathInfo);
        }
    }

    List<string> JobInfoObsListToList()
    {
        var jobFilePaths = new List<string>();

        foreach( JobFileInfoItem jobInfo in _jobFileInfoList )
        {
            jobFilePaths.Add(jobInfo.JobFileFullPath);
        }

        return jobFilePaths;
    }

    #endregion DeleteJobFromList

    #region LoadJob
    public ICommand LoadJobBtnClickedCommand
    {
        get { return new DelegateCommand<object>(LoadJobBtnClickedMethod); }
    }

    private void RaiseLoadJobConfirmation()
    {
        // By invoking the Raise method we are raising the Raised event and triggering any InteractionRequestTrigger that
        // is subscribed to it.
        // As parameters we are passing a Confirmation, which is a default implementation of IConfirmation (which inherits
        // from INotification) provided by Prism and a callback that is executed when the interaction finishes.
        ConfirmationRequest.Raise(
            new Confirmation { Content = "Load this job file?", Title = "Confirmation" },
            c => { _printJobConfirmationRequestResult = c.Confirmed; });
    }

    private void LoadJobBtnClickedMethod(Object jobFileName)
    {
        RaiseLoadJobConfirmation();

        if (_printJobConfirmationRequestResult)
        {
            int listIndex = _jobFileInfoList.FindIndex(p => p.JobFileName == (string)jobFileName);

            string jobId = Guid.NewGuid().ToString();

            var args = new String2Args(jobId, _jobFileInfoList[listIndex].JobFileFullPath);
            var s = Path.GetExtension(_jobFileInfoList[listIndex].JobFileFullPath);
            if (s != null)
            {
                string extension = s.ToLower();
                if (extension == ".txt")
                    CommonEvents.Processor.AddTask(new TaskEvent(this, AppEvents.TextDataFile, args));
                else
                    CommonEvents.Processor.AddTask(new TaskEvent(this, AppEvents.AMXBuildFile, args));
            }
        }
    }
    #endregion LoadJob
    //private bool CanLoadJobBtnClickedMethodBeExecuted(object context)
    //{
    //    //this is called to evaluate whether FuncToCall can be called
    //    //for example you can return true or false based on some validation logic
    //    return true;
    //}


    public INotification Notification { get; set; }

    public bool IsNavigationTarget(NavigationContext navigationContext)
    {
        return false;
    }

    public void OnNavigatedFrom(NavigationContext navigationContext)
    {
        navigationContext.Parameters.Add("PageFrom", ToString());
    }

    public void OnNavigatedTo(NavigationContext navigationContext)
    {

    }
}

}

非常感谢任何帮助

1 个答案:

答案 0 :(得分:0)

您正在使用INavigationAware界面,而IsNavigationTarget始终返回false。这就是造成这种情况的原因。

根据某些逻辑更改此值以返回true或false,或者让它始终返回true。或者,如果确实不需要,请完全删除INavigationAware的使用。

如果视图或视图模型实现INavigationAware接口,则在导航期间调用IsNavigationTarget函数。如果返回true,则它使用视图或视图模型的现有实例(如果存在)。否则,它会创建一个新实例。

您可以使用传递给函数的NavigationContext执行诸如检查Parameters集合值之类的内容,或者根据这些值,通过返回true来使用现有实例,或者让应用程序通过返回false来创建视图/视图模型的新实例。