如何在Catel中以编程方式实例化控件

时间:2014-04-08 01:46:53

标签: catel

我正在尝试动态创建控件并将其加载到数据窗口中。

我在顶部有标签,它们是不同类型的报告。我希望能够创建新的报告,而不必记住将它们添加到选项卡控件。我正在尝试使用工厂执行此操作,使用反射来标识实现特定接口的视图。一旦控件被实例化(下面的代码),我想将它们包装在TabItem中并将它们添加到我的选项卡控件中。这是工厂:

class ReportHandlerFactory : IReportHandlerFactory
{
    private static IList<IReportControl> ReportHandlers;

    public IEnumerable<IReportControl> GetReportHandlers()
    {
        if (null == ReportHandlers)
        {
            ReportHandlers = LoadHandlers() ?? new List<IReportControl>();

            if (ReportHandlers.Count < 1)
            {
                ReportHandlers.Add(new DefaultReportControl());
            }
        }

        return ReportHandlers;
    }

    private static IList<IReportControl> LoadHandlers()
    {
        return (from t in Assembly.GetExecutingAssembly().GetTypes()
                where t.GetInterfaces().Contains(typeof(IReportControl))
                      && !t.IsAbstract
                      && !(t.IsEquivalentTo(typeof(DefaultReportControl)))
                select (IReportControl)Activator.CreateInstance(t)
                   ).ToList<IReportControl>();
    }

    public class DefaultReportControl : TabItem, IReportControl
    {
        public DefaultReportControl() : base()
        {
            Header = "Error";
            Content = "No Reports Found.";
        }

        public new string Header
        {
            get { return base.Header.ToString(); }
            private set { base.Header = value; }
        }

        public IReportHandler ReportHandler
        {
            get { throw new Exception("No Handler Available for Default Report Control."); }
        }
    }

这是我的MainViewDataWindow:

<mvvm:DataWindow x:Class="Petersco.Reports.Views.MainWindowView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:winForms="clr-namespace:Microsoft.Reporting.WinForms;assembly=Microsoft.ReportViewer.WinForms"
    xmlns:mvvm="clr-namespace:Catel.Windows;assembly=Catel.MVVM"
    xmlns:views="clr-namespace:Petersco.Reports.Views"
    xmlns:catel="http://catel.codeplex.com"
    ShowInTaskbar="True" ResizeMode="CanResize" Icon="../Images/Icons/favicon.ico"
    Title="PCL Reports" MinHeight="768" MinWidth="1024">
<Grid Loaded="Grid_Loaded">

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="1*"/>
    </Grid.RowDefinitions>

    <TabControl Grid.Row="0" Margin="10,10,10,0" SelectedItem="{Binding SelectedReportTabItem}" 
                ItemsSource="{Binding ReportTabItems}" />
    <Button Grid.Row="1" Margin="0,5,10,10" Width="75"
                Content="Run Report" Command="{Binding RunReport}"
                HorizontalAlignment="Right" />
    <WindowsFormsHost Grid.Row="2" Margin="10,0,10,10">
        <winForms:ReportViewer x:Name="_reportViewer"/>
    </WindowsFormsHost>
</Grid>

这是ViewModel:

public class MainWindowViewModel : ViewModelBase
{
    private readonly IReportHandlerFactory _reportHandlerFactory;

    public MainWindowViewModel(IMessageMediator messageMediator,
                               IReportHandlerFactory reportHandlerFactory) : base(messageMediator)
    {
        Argument.IsNotNull(() => reportHandlerFactory);
        _reportHandlerFactory = reportHandlerFactory;

        ReportTabItems = new ObservableCollection<TabItem>(
            _reportHandlerFactory.GetReportHandlers().Select(x =>
                                                             new TabItem
                                                             {
                                                                 Header = x.Header,
                                                                 Content = x
                                                             }
                )
            );
        SelectedReportTabItem = ReportTabItems[0];
        RunReport = new Command(ExecuteRunReport, CanExecuteRunReport);
    }

    private bool CanExecuteRunReport()
    {
        if (SelectedReportTabItem == null)
        {
            return false;
        }
        return SelectedReportTabItem != null && GetReportHandler().ReportHandler.CanExecuteConstruct();
    }

    private void ExecuteRunReport()
    {
        if (SelectedReportTabItem == null)
        {
            return;
        }
        WaitCursor.Show();
        GetReportHandler().ReportHandler.Construct(ReportControl);
        ReportControl.RefreshReport();
    }

    private IReportControl GetReportHandler()
    {
        return SelectedReportTabItem.Content as IReportControl;
    }

    public Command RunReport { get; set; }
    public ReportViewer ReportControl { get; set; }
    public TabItem SelectedReportTabItem { get; set; }
    public ObservableCollection<TabItem> ReportTabItems { get; set; } 
}

问题是当以这种方式实例化视图时,初始化ViewModel时不会发生任何Catel魔法。也许我没有以正确的方式接近这个,但Catel中是否有设施/帮助程序以编程方式加载/初始化视图/视图模型?

3 个答案:

答案 0 :(得分:1)

Catel的神奇之处在于UserControlLogic类。这是一个可供所有UserControls使用的类,并确保一旦视图加载,就会发生魔力。

如果您希望视图支持Catel魔术,请确保从Catel.Windows.Controls.UserControl 派生自己在用户控件中创建UserControlLogic的实例。

我认为你能做的最好的事情是创建一个&#34; TabItemWrapper&#34;派生自Catel.Windows.UserControl的类(所以你得到了所有的魔法),你可以把内容放在那里。请注意,默认情况下,视图模型通过命名约定来解析,因此即使对于动态创建的视图,您也可以遵循命名约定。

顺便说一句。在视图模型中创建视图并不是真正的MVVM。创建视图等可以在服务(可以模拟)或代码隐藏(是的,代码隐藏)中完成。

答案 1 :(得分:0)

所以我尝试过这样的事情:

    private IList<IReportControl> LoadHandlers()
    {
        var controls = (from t in Assembly.GetExecutingAssembly().GetTypes()
                        where t.GetInterfaces().Contains(typeof(IReportControl))
                              && !t.IsAbstract
                              && !(t.IsEquivalentTo(typeof(DefaultReportControl)))
                        select t).ToList();

        var result = new List<IReportControl>();
        foreach (var ctrl in controls)
        {
            var serviceType = ctrl.GetInterfaces().FirstOrDefault(x => x.Name.Equals(typeof(IView<>).Name));
            _serviceLocator.RegisterType(serviceType, ctrl);
            result.Add(_serviceLocator.ResolveType(serviceType) as IReportControl);
        }

        return result;
    }

希望Catel ServiceLocator可能在实例化UserControl时执行它的魔力(通过魔术我的意思是初始化和设置ViewModel),但这也不起作用。我是否必须手工完成这个过程?

答案 2 :(得分:0)

好的,到目前为止这是唯一可以做我想做的解决方案,如果有更清洁和/或推荐的方法,请告诉我。

我已经为我的所有基于UserControl的视图添加了一个构造函数,它将是TabItems:

public MyViewConstructor(IViewModel vmb) : base(vmb)
{
}

接下来,我有以下动态构建控件:

private IList<IReportControl> LoadHandlers()
{
    var controls = (from t in Assembly.GetExecutingAssembly().GetTypes()
                    where t.GetInterfaces().Contains(typeof(IReportControl))
                          && !t.IsAbstract
                          && !(t.IsEquivalentTo(typeof(DefaultReportControl)))
                    select t).ToList();
    var result = new List<IReportControl>();
    foreach (var ctrl in controls)
    {
        var serviceType = ctrl.GetInterfaces().FirstOrDefault(x => x.Name.Equals(typeof(IView<>).Name));
        var viewModelType = serviceType.GetGenericArguments().FirstOrDefault();  
        var vmFactory = _serviceLocator.ResolveType<IViewModelFactory>();
        var vm = vmFactory.CreateViewModel(viewModelType, null);
        var reportControl = Activator.CreateInstance(ctrl, new object[] {vm});
        result.Add(reportControl as IReportControl);
    }

    return result;
}