C# Caliburn.Micro+;MEF+;现代用户界面:IContent事件

C# Caliburn.Micro+;MEF+;现代用户界面:IContent事件,c#,mef,caliburn.micro,modern-ui,wpf-4.5,C#,Mef,Caliburn.micro,Modern Ui,Wpf 4.5,我已经启动了一个使用Caliburn.Micro和modernui()的项目,在我的视图模型上启动IContent的导航事件时遇到了一些困难。我已经将这两个人连接起来,通过以下方式彼此合作: CM引导程序: public class CMBootstrapper : Bootstrapper<IShell> { private CompositionContainer container; private DirectoryCatalog catalog;

我已经启动了一个使用Caliburn.Micro和modernui()的项目,在我的视图模型上启动IContent的导航事件时遇到了一些困难。我已经将这两个人连接起来,通过以下方式彼此合作:

CM引导程序:

public class CMBootstrapper : Bootstrapper<IShell> {
    private CompositionContainer container;
    private DirectoryCatalog catalog;

    public CMBootstrapper() { }

    protected override void Configure() {
        catalog = new DirectoryCatalog(".", "*.*");
        container = new CompositionContainer(catalog);

        var compositionBatch = new CompositionBatch();
        compositionBatch.AddExportedValue<IWindowManager>(new WindowManager());
        compositionBatch.AddExportedValue<IEventAggregator>(new EventAggregator());
        compositionBatch.AddExportedValue(container);
        container.Compose(compositionBatch);
    }

    protected override IEnumerable<Assembly> SelectAssemblies() {
        List<Assembly> assemblies = new List<Assembly>();
        assemblies.Add(Assembly.GetExecutingAssembly());
        return assemblies;
    }

    protected override object GetInstance(Type serviceType, string key) {
        string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
        var exports = container.GetExportedValues<object>(contract);

        if (exports.Count() > 0)
            return exports.First();

        throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
    }

    protected override IEnumerable<object> GetAllInstances(Type serviceType) {
        return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
    }

    protected override void BuildUp(object instance) {
        container.SatisfyImportsOnce(instance);
    }
}
[Export]
public class MuiContentLoader : DefaultContentLoader {
    protected override object LoadContent(Uri uri) {
        var content = base.LoadContent(uri);
        if (content == null)
            return null;

        // Locate VM
        var viewModel = ViewModelLocator.LocateForView(content);

        if (viewModel == null)
            return content;

        // Bind VM
        if (content is DependencyObject)
            ViewModelBinder.Bind(viewModel, content as DependencyObject, null);

        return content;
    }
}
MuiView.xaml(Shell)

但没有人开枪。经过一些调试后,我发现
ModernFrame
正在检查
(设置视图为IContent)
中的事件,而这些事件没有,因为它只是一个普通的
用户控件。因此,我创建了一个自定义UserControl类,试图将事件传递给ViewModel:

多内容控制

<mui:ModernWindow x:Class="XMOperations.Views.MuiView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mui="http://firstfloorsoftware.com/ModernUI"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         ContentLoader="{StaticResource ModernContentLoader}"
         d:DesignHeight="300" d:DesignWidth="300">

<mui:ModernWindow.TitleLinks>
    <mui:Link DisplayName="Settings" Source="/Views/SettingsView.xaml" />
</mui:ModernWindow.TitleLinks>

<mui:ModernWindow.MenuLinkGroups>
    <mui:LinkGroupCollection>
        <mui:LinkGroup GroupName="Hello" DisplayName="Hello">
            <mui:LinkGroup.Links>
                <mui:Link Source="/Views/ChildView.xaml" DisplayName="Click me"></mui:Link>
            </mui:LinkGroup.Links>
        </mui:LinkGroup>
    </mui:LinkGroupCollection>
</mui:ModernWindow.MenuLinkGroups>
[Export(typeof(IShell))]
public class MuiViewModel : Conductor<IScreen>.Collection.OneActive, IShell {

}
public delegate void FragmentNavigationEventHandler(object sender, FragmentNavigationEventArgs e);
public delegate void NavigatedFromEventHandler(object sender, NavigationEventArgs e);
public delegate void NavigatedToEventHandler(object sender, NavigationEventArgs e);
public delegate void NavigatingFromEventHandler(object sender, NavigatingCancelEventArgs e);

public class MuiContentControl : UserControl, IContent {
    public event FragmentNavigationEventHandler FragmentNavigation;
    public event NavigatedFromEventHandler NavigatedFrom;
    public event NavigatedToEventHandler NavigatedTo;
    public event NavigatingFromEventHandler NavigatingFrom;

    public MuiContentControl() : base() {

    }

    public void OnFragmentNavigation(FragmentNavigationEventArgs e) {
        if(FragmentNavigation != null)
            FragmentNavigation(this, e);
    }

    public void OnNavigatedFrom(NavigationEventArgs e) {
        if (NavigatedFrom != null)
            NavigatedFrom(this, e);
    }

    public void OnNavigatedTo(NavigationEventArgs e) {
        if(NavigatedTo != null)
            NavigatedTo(this, e);
    }

    public void OnNavigatingFrom(NavigatingCancelEventArgs e) {
        if(NavigatingFrom != null)
            NavigatingFrom(this, e);
    }
}
然后我修改了视图以侦听带有消息的事件。附加:

设置视图

<local:MuiContentControl x:Class="XMOperations.Views.SettingsView"
         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:mui="http://firstfloorsoftware.com/ModernUI" 
         xmlns:cal="http://www.caliburnproject.org"
         xmlns:local="clr-namespace:XMOperations"
         cal:Message.Attach="[Event FragmentNavigation] = [Action OnFragmentNavigation($source, $eventArgs)];
                             [Event NavigatedFrom] = [Action OnNavigatedFrom($source, $eventArgs)];
                             [Event NavigatedTo] = [Action OnNavigatedTo($source, $eventArgs)];
                             [Event NavigatingFrom] = [Action OnNavigatingFrom($source, $eventArgs)]"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid Style="{StaticResource ContentRoot}">
    <mui:ModernTab SelectedSource="/Views/Settings/AppearanceView.xaml" Layout="List" ContentLoader="{StaticResource ModernContentLoader}">
        <mui:ModernTab.Links>
            <mui:Link DisplayName="Appearance" Source="/Views/Settings/AppearanceView.xaml" />
        </mui:ModernTab.Links>
    </mui:ModernTab>
</Grid>


唯一没有触发的事件是导航到,因此我相信Message.Attach在发送事件之前不会被应用。我可能是用了一种非常错误的方式来做这件事,并且我愿意进行大规模的重建。

好吧,这最终并没有那么糟糕——它确实让尝试将事件传递给虚拟机的过程变得更容易了

我为
ModernFrame
控件创建了一个导体,该控件存在于
ModernWindow
控件模板中

// Example viewmodel:

public class ModernWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
    protected override void OnViewLoaded(object view)
    {
        base.OnViewLoaded(view);

        // Instantiate a new navigation conductor for this window
        new FrameNavigationConductor(this);
    }
}
您需要在虚拟机的
OnViewLoaded
事件中为您的
ModernWindow
创建一个导体实例,因为这似乎是最好的地方(即尚未进行导航,但控件已完全加载并已解析其模板)

现在,任何添加了
IContent
界面的视图都将在导航发生时自动获得框架调用的方法

public class TestViewModel : Conductor<IScreen>, IContent
{
    public void OnFragmentNavigation(FragmentNavigationEventArgs e)
    {
        // Do stuff
    }

    public void OnNavigatedFrom(NavigationEventArgs e)
    {
        // Do stuff
    }

    public void OnNavigatedTo(NavigationEventArgs e)
    {
        // Do stuff
    }

    public void OnNavigatingFrom(NavigatingCancelEventArgs e)
    {
        // Do stuff
    }
}
公共类TestViewModel:Conductor,IContent
{
FragmentNavigation上的公共无效(FragmentNavigationEventArgs e)
{
//做事
}
OnNavigatedFrom(NavigationEventArgs e)上的公共无效
{
//做事
}
导航到时的公共无效(导航目标e)
{
//做事
}
NavigatingFrom上的公共无效(NavigatingCancelEventArgs e)
{
//做事
}
}
我已经测试过,这适用于出现在
IContent
上的所有4个导航事件-因为它通过
EventArgs
可以直接从虚拟机取消导航事件,或者在仅查看的场景中执行通常会执行的任何操作

我认为这可能是我能想到的最省力的方法——在窗口中输入一行代码,在虚拟机上实现接口,您就完成了:)

编辑:


我唯一可能添加的是在将导体添加到窗口时引发一些异常,或者可能是调试日志通知,以防由于某种原因,它找不到框架(框架的名称可能会在以后的m:ui版本中更改)

我在我的IContent视图中执行了后续操作,并在我的ViewModels上实现了IContent

   public void OnFragmentNavigation(FirstFloor.ModernUI.Windows.Navigation.FragmentNavigationEventArgs e)
    {
        if (this.DataContext != null)
        {
            var viewModel = this.DataContext as IContent;
            if (viewModel != null)
            {
                viewModel.OnFragmentNavigation(e);
            }
        }
    }

    public void OnNavigatedFrom(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e)
    {
        if (this.DataContext != null)
        {
            var viewModel = this.DataContext as IContent;
            if (viewModel != null)
            {
                viewModel.OnNavigatedFrom(e);
            }
        }
    }

    public void OnNavigatedTo(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e)
    {
        if (this.DataContext != null)
        {
            var viewModel = this.DataContext as IContent;
            if (viewModel != null)
            {
                viewModel.OnNavigatedTo(e);
            }
        }
    }

    public void OnNavigatingFrom(FirstFloor.ModernUI.Windows.Navigation.NavigatingCancelEventArgs e)
    {
        if (this.DataContext != null)
        {
            var viewModel = this.DataContext as IContent;
            if (viewModel != null)
            {
                viewModel.OnNavigatingFrom(e);
            }

        }
    }

我感觉到了你的痛苦——由于MUI是一种视图优先的方法,这似乎比它应该做的要困难一些。我的磁盘上某处有一个MUI测试项目,所以我会看一看(我将在不久的将来使用它,所以这是有用的信息!)做得好!我对CM的研究还不够深入,无法构建类似于FrameNavigationDirector的东西。
public class TestViewModel : Conductor<IScreen>, IContent
{
    public void OnFragmentNavigation(FragmentNavigationEventArgs e)
    {
        // Do stuff
    }

    public void OnNavigatedFrom(NavigationEventArgs e)
    {
        // Do stuff
    }

    public void OnNavigatedTo(NavigationEventArgs e)
    {
        // Do stuff
    }

    public void OnNavigatingFrom(NavigatingCancelEventArgs e)
    {
        // Do stuff
    }
}
   public void OnFragmentNavigation(FirstFloor.ModernUI.Windows.Navigation.FragmentNavigationEventArgs e)
    {
        if (this.DataContext != null)
        {
            var viewModel = this.DataContext as IContent;
            if (viewModel != null)
            {
                viewModel.OnFragmentNavigation(e);
            }
        }
    }

    public void OnNavigatedFrom(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e)
    {
        if (this.DataContext != null)
        {
            var viewModel = this.DataContext as IContent;
            if (viewModel != null)
            {
                viewModel.OnNavigatedFrom(e);
            }
        }
    }

    public void OnNavigatedTo(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e)
    {
        if (this.DataContext != null)
        {
            var viewModel = this.DataContext as IContent;
            if (viewModel != null)
            {
                viewModel.OnNavigatedTo(e);
            }
        }
    }

    public void OnNavigatingFrom(FirstFloor.ModernUI.Windows.Navigation.NavigatingCancelEventArgs e)
    {
        if (this.DataContext != null)
        {
            var viewModel = this.DataContext as IContent;
            if (viewModel != null)
            {
                viewModel.OnNavigatingFrom(e);
            }

        }
    }