在c#MEF上下文

时间:2017-08-21 08:26:47

标签: c# wpf interface window mef

我的任务是实现一个简单的确认对话框,以尝试关闭我的应用程序的主窗口,但在特定进程中在跑。关闭应用程序意味着中止该过程。如果该流程未运行,则无需确认。

刚开始我创建了一个接口来获取 Application.Current.MainWindow 并使用了 Closing 事件,但我的老师提出了其他的建议,我只是找不到合适的方法来完成它。

我使用的代码库中已经存在 CanClose 方法,默认情况下为空。它位于打开MainWindow的 AppInit 类中。我在构造函数中所做的是:

[ImportingConstructor]
    public AppInit(MainWindow mainWindow, [ImportMany] IEnumerable<IClosingValidator> closingValidator)
    {
        this.mainWindow = mainWindow;
        this.closingValidator = closingValidator;
    }

那是我老师的想法。现在在CanClose方法中,我可以迭代这些结束验证并返回true或false,如下所示:

public Boolean CanClose()
    {
        foreach (var validation in this.closingValidator)
        {
            var result = validation.CanClose();

            if (result == false)
            {
                return false;
            }
        }

        return true;
    }

ClosingValidator目前看起来如下,我认为这是错误的,但我需要在某处构建对话框。整个过程是有效的,但指示相关进程是否正在运行的标志位于另一个项目的ViewModel中,这意味着始终会显示此确认对话框。

 [Export(typeof(IClosingValidator))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ClosingValidator : IClosingValidator
{
    private readonly IMessageDialog messageDialog;
    private readonly IOverlayEffect overlayEffect;

    [ImportingConstructor]
    public ClosingValidator(IMessageDialog messageDialog, IOverlayEffect overlayEffect)
    {
        this.messageDialog = messageDialog;
        this.overlayEffect = overlayEffect;
    }

    public Boolean CanClose()
    {
        using (this.overlayEffect.Apply())
        {
            var result = this.messageDialog.ShowDialog(
                new MessageDialogArgs(Resources.Resources.ConfirmClosingApplicationTitle,
                                      Resources.Resources.ConfirmClosingApplicationMessage,
                                      MessageBoxButton.YesNo,
                                      MessageBoxImage.SystemQuestion));

            if (result == MessageBoxResult.Yes)
            {
                return true;
            }
        }

        return false;
    }

我认为我的问题归结为: 哪里 我是否构建了对话框,如何使用 ViewModel 中的布尔标志来确定是否显示对话框首先在哪?我对MEF比较陌生,对不起任何事情都不清楚。

编辑:我认为这个想法是我可以使用该界面在将来的某个时候实现进一步的结束验证。

编辑3:

简化的ViewModel实现(实际的viewModel有太多参数):

[Export]
public class TocViewModel
{
    [ImportingConstructor]
    public TocViewModel(MeasurementSequenceExecution measurementSequenceExecution)
    {
        this.measurementSequenceExecution = measurementSequenceExecution;
    }

        public Boolean CanApplicationClose
    {
        get { return !this.measurementSequenceExecution.IsMeasurementSequenceRunning; }
        set
        {
            this.measurementSequenceExecution.IsMeasurementSequenceRunning = !value;
        }
    }

IClosingValidator实施:

[Export(typeof(IClosingValidator))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ClosingValidator : IClosingValidator
{
    [ImportingConstructor]
    public ClosingValidator()
    {
    }

    public Boolean CanApplicationClose { get; }

IClosingValidator界面:

public interface IClosingValidator
{
    Boolean CanApplicationClose { get; }
}

处理结束的类:

[Export(typeof(IApp))]
public class AppInit : IApp
{
    [ImportingConstructor]
    public AppInit(MainWindow mainWindow,
                   [ImportMany(typeof(IClosingValidator))] IEnumerable<IClosingValidator> closingValidatorClients,
                   IOverlayEffect overlayEffect,
                   IMessageDialog messageDialog)
    {
        this.mainWindow = mainWindow;
        this.closingValidatorClients = closingValidatorClients;
        this.overlayEffect = overlayEffect;
        this.messageDialog = messageDialog;
    }


    public Boolean CanClose()
    {
        if (this.closingValidatorClients != null)
        {
            foreach (var client in this.closingValidatorClients)
            {
                if (!client.CanApplicationClose)
                {
                    using (this.overlayEffect.Apply())
                    {
                        var result = this.messageDialog.ShowDialog(
                            new MessageDialogArgs(Resources.Resources.ConfirmClosingApplicationTitle,
                                                  Resources.Resources.ConfirmClosingApplicationMessage,
                                                  MessageBoxButton.YesNo,
                                                  MessageBoxImage.SystemQuestion));

                        if (result == MessageBoxResult.Yes)
                        {
                            return true;
                        }

                        return false;
                    }
                }
            }
        }

        return true;
    }

private readonly IEnumerable<IClosingValidator> closingValidatorClients;
    private readonly MainWindow mainWindow;
    private readonly IMessageDialog messageDialog;
    private readonly IOverlayEffect overlayEffect;

更新 我使它工作,使用typeof(IClosingValidator)导出ViewModel的建议是对的,我只需要添加第二个导出,而不是替换默认导出,不知道你可以有2个。所以现在使用2个ExportAttributes就可以了!

1 个答案:

答案 0 :(得分:2)

我认为您的IClosingValidator实现位置错误。 我有一个类似的项目,我的工作如下:

我有一个类似于IClosingValidator的界面:

public interface IConfirmShellClosing
{
    /// <summary>
    /// Gets a value that indicates whether the shell window can be closed.
    /// </summary>
    bool CanShellClose { get; }
}

此接口由所有ViewModel实现,如果可以关闭shell,应该询问它们。在您的情况下,可以运行进程的所有ViewModel都应该实现此接口。因此,每个ViewModel本身都将实现CanShellClose属性并确定进程是否在此上下文中运行。只有当所有ViewModel都为此返回true时,才能关闭Window。

然后,在您的窗口实例中,如果Window可以关闭,您可以订阅WindowClosing事件并询问所有已注册的ViewModel。此实现将转到Window的ViewModel(或文件后面的代码):

[ImportMany(typeof(Lazy<IConfirmShellClosing>))]
private IEnumerable<Lazy<IConfirmShellClosing>> _confirmShellClosingClients;

    private void ExecuteWindowClosing(CancelEventArgs args)
        {
            if (_confirmShellClosingClients != null)
            {
                foreach (var client in _confirmShellClosingClients)
                {
                    if (!client.Value.CanShellClose)
                    {
                        // Show your Dialog here and handle the answer
                    }
                }
            }
        }

我希望这会有所帮助。

修改

好的,您在ViewModel和Validator的实现中仍然存在一些错误。

首先,不再需要Class ClosingValidator。您希望将责任赋予ViewModel,而不是中央验证程序类。

之后,我们需要像这样更改ViewModel:

    [Export]
    public class TocViewModel : IClosingValidator
    {
        [ImportingConstructor]
        public TocViewModel(MeasurementSequenceExecution measurementSequenceExecution)
        {
            this.measurementSequenceExecution = measurementSequenceExecution;
        }

            public Boolean CanApplicationClose
        {
            get { return !this.measurementSequenceExecution.IsMeasurementSequenceRunning; }
            set
            {
                this.measurementSequenceExecution.IsMeasurementSequenceRunning = !value;
            }
        }

现在发生了什么? ViewModel实现IClosingValidator接口,因此必须实现CanShellClose属性。在此属性中,您可以定义逻辑,此ViewModel可以决定是否可以关闭Shell。如果您添加了其他ViewModel,它也可以实现该接口,并具有不同的结束逻辑。

在应用程序本身的导入中,导入所有实现IClosingValidator接口的类,然后询问,如果属性为true或false。

编辑2:

我有3 .dll(HelpingProject,InterfaceProject和ViewModelProject)所有3都应该在compositionContainer正在搜索的目录中。就我而言,我构建了这样的容器:

var catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new DirectoryCatalog(@"C:\Projects\HelpingSolution\HelpingWpfProject\bin\Debug"));
        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);

因此将扫描Debug文件夹以查找匹配的导出。我建议您在代码库中搜索此代码,并查看容器在哪里寻找导出。这不一定只是一个文件夹,但也可以是插件文件夹或不同的位置。你可以找到一个很好的介绍here