【问题标题】:DLL Project and GUI Project - Caliburn Micro: Model / View VM QuestionsDLL 项目和 GUI 项目 - Caliburn Micro:模型/查看 VM 问题
【发布时间】:2020-05-25 16:56:34
【问题描述】:

我确信这个问题之前应该被问过,但我无法找到我正在寻找的确切内容;

考虑以下几点:

- Solution
-- Class Library Project [Caliburn.Micro] Referenced
--- [Models] Folder
---- LogEntryModel.cs
--- [ViewModels] Folder
---- LogEntryViewModel.cs
---- ShellViewModel.cs
-- WPF GUI Project [Caliburn.Micro] Referenced
--- [Views] Folder
---- LogEntryView.xaml
---- ShellView.xaml

所以,我有 2 个项目,一个带有模型,一个带有 ViewModels 和视图; 这是我的引导程序:

    public class AppBootstrapper : BootstrapperBase
    {
        private CompositionContainer container;

        public AppBootstrapper()
        {
            Initialize();
        }

        protected override void BuildUp(object instance)
        {
            this.container.SatisfyImportsOnce(instance);
        }

        /// <summary>
        ///     By default, we are configured to use MEF
        /// </summary>
        protected override void Configure()
        {



            var config = new TypeMappingConfiguration
            {
                DefaultSubNamespaceForViews = "WPFGUI.Views",
                DefaultSubNamespaceForViewModels = "ClassLibrary.ViewModels"
            };
            ViewLocator.ConfigureTypeMappings(config);
            ViewModelLocator.ConfigureTypeMappings(config);

            var catalog =
                new AggregateCatalog(
                    AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());

            this.container = new CompositionContainer(catalog);

            var batch = new CompositionBatch();

            batch.AddExportedValue<IWindowManager>(new WindowManager());
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());
            batch.AddExportedValue(this.container);
            batch.AddExportedValue(catalog);

            this.container.Compose(batch);
        }

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

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

            if (exports.Any())
            {
                return exports.First();
            }

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

        protected override void OnStartup(object sender, StartupEventArgs e)
        {
            var startupTasks =
                GetAllInstances(typeof(StartupTask))
                .Cast<ExportedDelegate>()
                .Select(exportedDelegate => (StartupTask)exportedDelegate.CreateDelegate(typeof(StartupTask)));

            startupTasks.Apply(s => s());

            DisplayRootViewFor<IShell>();
        }

    }

现在,当我尝试使用绑定到列表框的 LogEntryModel 时,我收到 Cannot find view for ClassLibrary.Models.LogEntryModel.

  • 我假设我需要“告诉”Caliburn 在我的类库项目中寻找模型(如何)
  • 我应该在我的类库中引用 Caliburn.Micro 吗? (因为它是 GUI 的东西?)
  • 我的 ViewModel 应该在 ClassLibrary 还是 GUI 项目中?

[编辑] 我改变了我的文件夹结构,我的虚拟机和模型现在组合在一起, 我更新了 bootstrapper.cs:

            var config = new TypeMappingConfiguration
            {
                DefaultSubNamespaceForViews = "WPFGUI.Views",
                DefaultSubNamespaceForViewModels = "ClassLibrary.ViewModels"
            };
            ViewLocator.ConfigureTypeMappings(config);
            ViewModelLocator.ConfigureTypeMappings(config);

ShellViewModel 仍然有效;但 LogEntryModel 仍然显示:

Cannot find view for ClassLibrary.Models.LogEntryModel.

[编辑 2] LogEntryModel:

public class LogEntryModel
    {
        //GUID
        public Guid GUID { get; set; }
        //The log message string
        public string Message { get; set; }
        //The module that created the logentry (see enums Module for options)
        public int Module { get; set; }
        //The urgency (used for coloring: 0 = black (normal), 1 = red (error), 2 = cyan (info)
        public int Severity { get; set; }
        //User that triggered the logentry
        public string UserID { get; set; }
        //The datetime of the logentry
        public DateTime LogEntryDateTime { get; set; }

    }

LogEntryViewModel:

    public class LogEntryViewModel
    {
//This is for testing purposes only (I'd expect "Hello World" everywhere
        public String Message { get; set; } = "Hello World";
    }

LogEntryView.xaml:

<UserControl x:Class="ServicesUI_WPF.Views.LogEntryView"
             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:WPFGUI.Views"
             DataContext="ClassLibrary.ViewModels.LogEntryViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="Red">

    </Grid>
</UserControl>

【问题讨论】:

  • LogEntryModel 与视图无关,它的视图模型调用视图...所以 Logentry 的视图和视图模型的名称是什么。 caliburn 的规则是:LogentryView 和 LogViewModel。所以如果它的规则不一样,是的,你必须指出 caliburn。
  • 我是否正确理解 ViewModel 也需要存在?我认为,就我能够谷歌而言,问题在于名称空间。但是他们都在谈论将 VM 从 V 中分离出来,而不是将 M 从 VM 和 V 中分离出来,我应该将我的 VM 移动到我的模型项目并以这种方式解决问题吗?
  • 我看不到您的逻辑,请查看我的答案并阅读链接。 ViewModel 和 View 存在该规则,是的,您可以在同一个库中拥有 VM 和 V,而在其他库中拥有模型...我在您的描述中没有看到视图模型...显示更多代码... Shellviewmodel 和 logentryviewmodel
  • 你应该展示你的代码shellviewmodel和logentryviewmodel,以避免浪费时间
  • 我想你已经在 logentryview.xaml 中引用了库模型?

标签: c# wpf caliburn.micro


【解决方案1】:

如果你想添加新的规则来链接viewModel和view,你必须使用ViewLocator

一些样本:

//link xxxxViewModel with xxxxViewX   
ViewLocator.NameTransformer.AddRule(@"ViewModel", @"ViewX");

//case when view and viewmodel are not in same library       
//link Cockpit.Core.Plugins.Plugins.Properties.xxxViewModel with
//Cockpit.General.Properties.Views.xxxView
ViewLocator.AddNamespaceMapping("Cockpit.Core.Plugins.Plugins.Properties", "Cockpit.General.Properties.Views");

【讨论】:

  • 我更新了我的问题并进行了一些更改(将 VM 移至 ClassLibrary,与模型一起使用) - 视图现在位于单独的程序集中;我更改了 Bootstrapper.cs 以反映这一点(并且应用程序像以前一样工作)。除了命名空间之外,所有的命名都是一样的;但是我在您的链接中找不到将 Model 与 ViewModel 与 View 连接的方法,只有 ViewModel 与 View?
  • 是的,模型只是数据......所以你能知道你如何调用 LogentryViewmodel 吗?假设你在view和viewmodel所在的项目中引用了模型库?
  • 更新了我的问题
【解决方案2】:

我让它工作了;在查看了一些 Youtube 链接后,我注意到我遗漏了一些东西;

LogEntryModel:

namespace ServicesTools.Models
{
    public class LogEntryModel
    {
        //GUID
        public Guid GUID { get; set; }
        //The log message string
        public string Message { get; set; }
        //The module that created the logentry (see enums Module for options)
        public int Module { get; set; }
        //The urgency (used for coloring: 0 = black (normal), 1 = red (error), 2 = cyan (info)
        public int Severity { get; set; }
        //User that triggered the logentry
        public string UserID { get; set; }
        //The datetime of the logentry
        public DateTime LogEntryDateTime { get; set; }


    }
}

LogEntryViewModel

namespace ServicesTools.ViewModels
{
    public class LogEntryViewModel : Screen
    {
        //Create a property that will keep all data from LogEntryModel in this BindableCollecton
        private BindableCollection<LogEntryModel> _logEntries;
        public BindableCollection<LogEntryModel> LogEntries
        {
            get { return _logEntries; }
            set { _logEntries = value;
                NotifyOfPropertyChange(() => LogEntries);
            }
        }

        //On Instantiate; collect all the LogEntries from the datasource
        public LogEntryViewModel()
        {
            LogEntries = new BindableCollection<LogEntryModel>(GlobalConfig.Connection.GetLogEntries());
        }

    }
}

我没有 LogEntryView,因为它在 DebugView.xaml 中被调用:

<UserControl x:Class="ServicesTools.Views.DebugView" 
             xmlns:local="clr-namespace:ServicesTools.Views" 
             xmlns:vms="clr-namespace:ServicesTools.ViewModels;assembly=ServicesLibrary"
             xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls" xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
             xmlns:convert="clr-namespace:ServicesUI_WPF.Converters"
             >
        <Grid Grid.Row="2">
            <Border Padding="5" BorderThickness="1" BorderBrush="{StaticResource CompanyCore1SolidBrush}">
                <DockPanel>
                    <ScrollViewer CanContentScroll="True" VerticalScrollBarVisibility="Visible">
                        <ItemsControl ItemsSource="{Binding LogEntries}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal" Background="{Binding Severity, Converter={StaticResource SeverityToColorConverter}}" >
                                        <TextBlock FontFamily="Consolas">
                                            <TextBlock.Text>
                                                <MultiBinding StringFormat="{}[{0:dd-MM-yy HH:mm:ss}] {1}({2}), {3}">
                                                    <Binding Path="LogEntryDateTime"/>
                                                    <Binding Path="Module" Converter="{StaticResource ModuleToEnumConverter}" />
                                                    <Binding Path="Module" />
                                                    <Binding Path="Message" />
                                                </MultiBinding>
                                            </TextBlock.Text>
                                        </TextBlock>
                                    </StackPanel>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </ScrollViewer>
                </DockPanel>
            </Border>
        </Grid>
    </Grid>
</UserControl>

我几乎让它在 ListBox 中工作,但我最初缺少“DisplayMemberPath”; 我在 LogEntryViewModel 中还缺少的是创建(并填充)属性 BindableCollection&lt;LogEntries&gt; 的实际代码,实际上没有什么可以绑定的。

我必须编辑的最后一件事是 DebugViewModel:

namespace ServicesTools.ViewModels
{
    public class DebugViewModel : Screen
    {

        //Create an ObservableCollection property that will keep all LogEntries
        private ObservableCollection<LogEntryModel> _logEntries;
        public ObservableCollection<LogEntryModel> LogEntries
        {
            get { return _logEntries; }
            set 
            { 
              _logEntries = value;
              NotifyOfPropertyChange(() => LogEntries) ;
            }
        }

        //On Instantiate; call GetLogEntries
        public DebugViewModel()
        {
            GetLogEntries();
        }

        /// <summary>
        /// Call a stored procedure to reset TrackAndTrace
        /// Log an entry into the database with severity High on click
        /// Log an entry into the database with severity Debug on finish
        /// Refresh LogEntries
        /// </summary>
        public void TrackAndTraceReset()
        {
            //Log an Entry into the table
            HelperFunctions.CreateLogEntry(logEntryMessage:$"User Clicked the Track & Trace reset button", Enums.Severity.High, Enums.Module.Debug);

            //TODO something [...]

            HelperFunctions.CreateLogEntry(logEntryMessage: $"And it Worked!", Enums.Severity.Debug, Enums.Module.Debug);

            //Refresh the list of LogEntries
            GetLogEntries();

        }

        /// <summary>
        /// Clear the current property LogEntries (if not Null), 
        /// then instantiate a new LogEntryViewModel and insert LogEntryViewModel.LogEntries property into LogEntries
        /// </summary>
        /// <returns>ObservableCollection<LogEntryModel></returns>
        private ObservableCollection<LogEntryModel> GetLogEntries() {
            //If LogEntries is null, do nothing; otherwise Clear it
            LogEntries?.Clear();

            //Instantiate new LogEntryViewModel
            LogEntryViewModel _lEVM  = new LogEntryViewModel();

            //Insert Property into LogEntries property
            LogEntries = _lEVM.LogEntries;

            //TODO: if filter exists, filter the list

            return LogEntries;            
        }

    }
}

我仍在测试是否真的需要在每次更新日志条目时实例化一个新的 LogEntryViewModel(),但这是更新列表中所有条目的最简单方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 2014-12-19
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多