我正在尝试动态创建控件并将其加载到数据窗口中。
我在顶部有标签,它们是不同类型的报告。我希望能够创建新的报告,而不必记住将它们添加到选项卡控件。我正在尝试使用工厂执行此操作,使用反射来标识实现特定接口的视图。一旦控件被实例化(下面的代码),我想将它们包装在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中是否有设施/帮助程序以编程方式加载/初始化视图/视图模型?
答案 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;
}