如何將帶有構造函數參數的ViewModel傳遞給TabServiceExtensions方法?

[英][Catel]How do I pass a ViewModel with constructor argument(s) to TabServiceExtensions methods?


So I've just started out using Catel this past week but I'm having trouble getting a tabbed interfaced working. I've been using the following resource Using a tabbed interface with MVVM (https://catelproject.atlassian.net/wiki/display/CTL/Using+a+tabbed+interface+with+MVVM#UsingatabbedinterfacewithMVVM-CreatingClosableTabItem):

所以上周我剛開始使用Catel,但是我在使用帶標簽界面的工作時遇到了麻煩。我一直在使用MVVM的選項卡接口(https://catelproject.atlassian.net/wiki/display/CTL/Using+a+tabbed+interface+with+MVVM# usingabbedinterfacewithmvm - creatclosabletabitem):

I have a MainWindow (catel:Window) which contains a TabControl with xmlns:controls="clr-namespace:AutoProgram.UI.Controls":

我有一個主窗口(catel:Window),其中包含一個帶有xmlns:controls="clr-namespace:AutoProgram.UI.Controls"的選項卡控件:

<Border Background="#50FFFFFF" BorderBrush="{StaticResource WindowFrameBrush}" BorderThickness="5" Margin="-6" Padding="0">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Border Grid.Row="0" Grid.Column="0" Background="{StaticResource WindowFrameBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="0,0,0,0" Margin="0" Padding="0">
            <Grid>
                <TextBlock Foreground="White" FontWeight="Bold" VerticalAlignment="Center" Margin="10,2,10,2" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=Title}"/>
                <Button Content="X" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="1" FontSize="7" Width="15" Height="15" Padding="0" Command="ApplicationCommands.Close"/>
            </Grid>
        </Border>
        <catel:StackGrid Grid.Row="1" Grid.Column="0">
            <catel:StackGrid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </catel:StackGrid.RowDefinitions>
            <catel:StackGrid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
            </catel:StackGrid.ColumnDefinitions>

            <ItemsControl x:Name="ItemsControlAutomators" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Automators}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Columns="3" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button x:Name="Button" Content="{Binding Automator.Name, Mode=OneWay}" Command="{Binding ElementName=ItemsControlAutomators, Path=DataContext.RunAutomator}" CommandParameter="{Binding}"></Button>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </catel:StackGrid>

        <catel:TabControl x:Name="TabControlAutomators" Grid.Row="2" Grid.Column="0" Margin="-2" LoadTabItems="LazyLoading">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <controls:ClosableTabItem Title="{Binding ViewModel.Title}" CanClose="{Binding CanClose}" />
                </DataTemplate>
            </TabControl.ItemTemplate>

            <TabControl.ContentTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding ViewModel, Converter={catel:ViewModelToViewConverter}}" />
                </DataTemplate>
            </TabControl.ContentTemplate>
        </catel:TabControl>
    </Grid>
</Border>

Relevant MainWindow.xaml.cs

有關MainWindow.xaml.cs

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();

        var serviceLocator = this.GetServiceLocator();
        var tabService = serviceLocator.ResolveType<ITabService>() as TabService;

        tabService?.SetTabControl(TabControlAutomators);
    }
}

Relevant MainWindowViewModel.cs and location of failure:

有關MainWindowViewModel。cs和故障位置:

public class MainWindowViewModel : ViewModelBase
{
    private readonly IAutomatorService _automatorService;
    private readonly ITabService _tabService;

    public MainWindowViewModel(IAutomatorService automatorService, ITabService tabService)
    {
        Argument.IsNotNull(() => automatorService);
        Argument.IsNotNull(() => tabService);

        _automatorService = automatorService;
        _tabService = tabService;

        RunAutomator = new Command<AutomatorModel>(OnRunAutomator, OnRunAutomatorCanExecute);
    }

    public override string Title => "AutoProgram";

    public ObservableCollection<AutomatorModel> Automators
    {
        get { return GetValue<ObservableCollection<AutomatorModel>>(AutomatorsProperty); }
        set { SetValue(AutomatorsProperty, value); }
    }

    public static readonly PropertyData AutomatorsProperty = RegisterProperty("Automators", typeof(ObservableCollection<AutomatorModel>), () => new ObservableCollection<AutomatorModel>());

    public Command<AutomatorModel> RunAutomator { get; private set; }

    public async void OnRunAutomator(AutomatorModel automatorModel)
    {
        Debug.WriteLine($"NAME: {automatorModel.Automator.Name}");
        _tabService.AddAndActivate<AutomatorViewModel>(new AutomatorViewModel(automatorModel), true);   // Throws a null exception in TabItem.cs
        //_tabService.AddAndActivate<AutomatorViewModel>(new AutomatorViewModel(), true);   // But this works (sort of, see bottom error).
    }

}

TabServiceExtensions.cs

TabServiceExtensions.cs

public static class TabServiceExtensions
{
    public static TabItem Add<TViewModel>(this ITabService tabService, object dataContext = null, bool canClose = false)
        where TViewModel : IViewModel
    {
        Argument.IsNotNull(() => tabService);

        var tabItem = CreateTabItem<TViewModel>(tabService, dataContext);
        tabItem.CanClose = canClose;

        tabService.Add(tabItem);

        return tabItem;
    }

    public static TabItem AddAndActivate<TViewModel>(this ITabService tabService, object dataContext = null, bool canClose = false)
        where TViewModel : IViewModel
    {
        Argument.IsNotNull(() => tabService);

        var tabItem = Add<TViewModel>(tabService, dataContext, canClose);
        tabService.Activate(tabItem);

        return tabItem;
    }

    public static TabItem CreateTabItem<TViewModel>(this ITabService tabService, object dataContext)
        where TViewModel : IViewModel
    {
        Argument.IsNotNull(() => tabService);

        var dependencyResolver = tabService.GetDependencyResolver();
        var viewModelFactory = dependencyResolver.Resolve<IViewModelFactory>();
        var vm = viewModelFactory.CreateViewModel<TViewModel>(typeof(TViewModel), dataContext);

        return new TabItem(vm);
    }

    public static void AddAndActivate(this ITabService tabService, TabItem tabItem)
    {
        Argument.IsNotNull(() => tabService);
        Argument.IsNotNull(() => tabItem);

        tabService.Add(tabItem);
        tabService.Activate(tabItem);
    }
}

TabItem.cs

TabItem.cs

public class TabItem
{
    public TabItem(IViewModel viewModel)
    {
        Argument.IsNotNull(() => viewModel);

        ViewModel = viewModel;
        CanClose = true;

        if (!viewModel.IsClosed)
        {
            viewModel.ClosedAsync += OnViewModelClosed;
        }
    }

    public IViewModel ViewModel { get; private set; }

    public bool CanClose { get; set; }

    public object Tag { get; set; }

    public event EventHandler<EventArgs> Closed;

    private async Task OnViewModelClosed(object sender, ViewModelClosedEventArgs e)
    {
        var vm = ViewModel;
        if (vm != null)
        {
            vm.ClosedAsync -= OnViewModelClosed;
        }

        Closed.SafeInvoke(this);
    }
}

I want the TabItems to be AutomatorViewModels. The latter is initialised as such:

我希望表項是AutomatorViewModels。后者的初始化如下:

    public AutomatorViewModel(AutomatorModel automatorModel)
    {
        Title = "Test";
    }

But the above code throws a null Exception in TabItem.cs. If I omit the constructor argument, i.e., change it to public AutomatorViewModel() the tab(s) do get created with "Test" titles. Although in that case get the following error when manually closing these tab(s): System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=TabStripPlacement; DataItem=null; target element is 'TabItem' (Name=''); target property is 'NoTarget' (type 'Object')

但是上面的代碼在表項目.cs中拋出一個空異常。如果省略構造函數參數,即。,將其更改為public AutomatorViewModel(),選項卡將使用“Test”標題創建。雖然在這種情況下,當手動關閉這些選項卡時得到以下錯誤:System.Windows。數據錯誤:4:無法找到與引用'RelativeSource FindAncestor、AncestorType='System.Windows.Controls. controls .進行綁定的源。TabControl”,AncestorLevel =“1”。BindingExpression:路徑= TabStripPlacement;DataItem =零;目標元素為'TabItem' (Name= ");目標屬性為'NoTarget'(類型為'Object')

App.xaml.cs

App.xaml.cs

    protected override void OnStartup(StartupEventArgs e)
    {
        LogManager.AddDebugListener();

        Log.Info("Starting application");

        StyleHelper.CreateStyleForwardersForDefaultStyles();

        var serviceLocator = ServiceLocator.Default;
        serviceLocator.RegisterType<IAutomatorService, AutomatorService>();
        serviceLocator.RegisterType<ITabService, TabService>();

        // SOME THINGS I'VE TRIED/CURRENTLY TRYING.
        //var dependencyResolver = this.GetDependencyResolver();
        //var viewModelLocator = dependencyResolver.Resolve<IViewModelLocator>();
        //viewModelLocator.Register<AutomatorView, AutomatorViewModel>();
        //viewModelLocator.Register(typeof(AutomatorView), typeof(AutomatorViewModel));
        //viewModelLocator.Register<MainWindow, MainWindowViewModel>();

        Log.Info("Calling base.OnStartup");
        base.OnStartup(e);
    }

Catel debugging info:

Catel調試信息:

11:22:39:117 => [DEBUG] [Catel.MVVM.ViewModelBase] [8] Creating view model of type 'AutomatorViewModel' with unique identifier 2

11:22:39:117 = >[Catel.MVVM(調試)。[8]創建具有唯一標識符2的“AutomatorViewModel”類型的視圖模型

11:22:39:117 => [DEBUG] [Catel.MVVM.ViewModelCommandManager] [8] Creating a ViewModelCommandManager for view model 'AutoProgram.UI.ViewModels.AutomatorViewModel' with unique identifier '2'

11:22:39:117 = >[Catel.MVVM(調試)。為視圖模型的AutoProgram.UI.ViewModels創建一個ViewModelCommandManager。擁有唯一標識符“2”的AutomatorViewModel

11:22:39:118 => [DEBUG] [Catel.MVVM.ViewModelCommandManager] [8] Created a ViewModelCommandManager for view model 'AutoProgram.UI.ViewModels.AutomatorViewModel' with unique identifier '2'

11:22:39:118 = >[Catel.MVVM(調試)。[8]為視圖模型的AutoProgram.UI.ViewModels創建了一個ViewModelCommandManager。擁有唯一標識符“2”的AutomatorViewModel

11:22:39:119 => [DEBUG] [Catel.MVVM.ManagedViewModel] [8] Added view model instance, currently containing '1' instances of type 'AutoProgram.UI.ViewModels.AutomatorViewModel'

11:22:39:119 = >[Catel.MVVM(調試)。[8]增加了視圖模型實例,當前包含類型為“AutoProgram.UI.ViewModels.AutomatorViewModel”的“1”實例

11:22:39:123 => [DEBUG] [Catel.IoC.TypeFactory] [8] Creating instance of type 'AutoProgram.UI.ViewModels.AutomatorViewModel' using specific parameters. No constructor found in the cache, so searching for the right one

11:22:39:123 = >[Catel.IoC(調試)。[8]創建類型“AutoProgram.UI.ViewModels”的實例。AutomatorViewModel使用特定的參數。在緩存中沒有找到構造函數,所以要搜索正確的

11:22:39:124 => [DEBUG] [Catel.IoC.TypeFactory] [8] Checking if constructor 'public ctor(AutomatorModel automatorModel)' can be used

11:22:39:124 = >[Catel.IoC(調試)。[8]檢查是否可以使用構造函數的“public ctor(AutomatorModel AutomatorModel)”

11:22:39:126 => [DEBUG] [Catel.IoC.TypeFactory] [8] Constructor is not valid because value 'AutoProgram.UI.ViewModels.AutomatorViewModel' cannot be used for parameter 'AutoProgram.UI.ViewModels.AutomatorViewModel'

11:22:39:126 = >[Catel.IoC(調試)。[8]構造函數無效,因為值'AutoProgram.UI.ViewModels。“AutomatorViewModel”不能用於參數“AutoProgram.UI.ViewModels.AutomatorViewModel”

11:22:39:126 => [DEBUG] [Catel.IoC.TypeFactory] [8] The constructor is valid and can be used

11:22:39:126 = >[Catel.IoC(調試)。這個構造函數是有效的,可以使用

11:22:39:127 => [DEBUG] [Catel.IoC.TypeFactory] [8] No constructor could be used, cannot construct type 'AutoProgram.UI.ViewModels.AutomatorViewModel' with the specified parameters

11:22:39:127 = >[Catel.IoC(調試)。無法使用任何構造函數,無法構造類型'AutoProgram.UI.ViewModels。擁有指定參數的AutomatorViewModel

11:22:39:128 => [DEBUG] [Catel.IoC.TypeFactory] [8] Creating instance of type 'AutoProgram.UI.ViewModels.AutomatorViewModel' using specific parameters. No constructor found in the cache, so searching for the right one

11:22:39:128 = >[Catel.IoC(調試)。[8]創建類型“AutoProgram.UI.ViewModels”的實例。AutomatorViewModel使用特定的參數。在緩存中沒有找到構造函數,所以要搜索正確的

11:22:39:128 => [DEBUG] [Catel.IoC.TypeFactory] [8] Checking if constructor 'public ctor(AutomatorModel automatorModel)' can be used

11:22:39:128 = >[Catel.IoC(調試)。[8]檢查是否可以使用構造函數的“public ctor(AutomatorModel AutomatorModel)”

11:22:39:129 => [DEBUG] [Catel.IoC.TypeFactory] [8] Constructor is not valid because parameter 'automatorModel' cannot be resolved from the dependency resolver

11:22:39:129 = >[Catel.IoC(調試)。[8]構造函數無效,因為參數“automatorModel”不能從依賴解析器解析

11:22:39:129 => [DEBUG] [Catel.IoC.TypeFactory] [8] The constructor is valid and can be used

11:22:39:129 = >[Catel.IoC(調試)。這個構造函數是有效的,可以使用

11:22:39:130 => [DEBUG] [Catel.IoC.TypeFactory] [8] No constructor could be used, cannot construct type 'AutoProgram.UI.ViewModels.AutomatorViewModel' with the specified parameters

11:22:39:130 = >[Catel.IoC(調試)。無法使用任何構造函數,無法構造類型'AutoProgram.UI.ViewModels。擁有指定參數的AutomatorViewModel

11:22:39:130 => [DEBUG] [Catel.MVVM.ViewModelFactory] [8] Could not construct view model 'AutoProgram.UI.ViewModels.AutomatorViewModel' using injection of data context 'AutomatorViewModel' AutoProgram.UI.vshost.exe Error: 0 : 11:22:39:131 => [ERROR] [Catel.Argument] [8] Argument 'viewModel' cannot be null Exception thrown: 'System.ArgumentNullException' in Catel.Core.dll

11:22:39:130 = >[Catel.MVVM(調試)。[8]無法構建視圖模型的AutoProgram.UI.ViewModels。自動視圖模型,使用數據上下文自動視圖模型的自動簽名。ui.vshost。誤差:0:11:22:39:131 => [Error] [Catel]。參數[8]參數“viewModel”不能為空異常拋出:“系統”。Catel.Core.dll ArgumentNullException”


EDIT #1:

  • added debugging info (uncommented LogManager.AddDebugListener();)
  • 添加調試信息(未注釋的LogManager.AddDebugListener();)
  • added App.xaml.cs
  • 添加App.xaml.cs

EDIT #2:

Found a workaround by changing the view model's initialisation to a constructorless one, and setting the model property explicitly, as follows:

通過將視圖模型的初始化更改為無構造模型,並顯式設置模型屬性,找到一種解決方案,如下所示:

_tabService.AddAndActivate(new AutomatorViewModel { AutomatorModel = automatorModel }, false);

_tabService。AddAndActivate(新的AutomatorViewModel {AutomatorModel = AutomatorModel}, false);

1 个解决方案

#1


0  

You should pass in the DataContext object, not the ViewModel into the AddAndActivate. So this should work (and will construct & inject the vm for you):

您應該將DataContext對象傳遞給AddAndActivate,而不是ViewModel。所以這應該是可行的(並且將為您構造並注入vm):

_tabService.AddAndActivate<AutomatorViewModel>(automatorModel, true);

注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2017/02/03/72a35ad6efef0ddb812959a9a8bb4106.html



 
粤ICP备14056181号  © 2014-2020 ITdaan.com