PRISM Просмотр инъекций внутри ItemsControl - PullRequest
1 голос
/ 16 декабря 2009

ОБНОВЛЕНО (17.12.2009): Теперь отражает последний прогресс, который я сделал.

Это первое приложение, которое я и коллега разрабатываем с использованием Prism и MVVM в Silverlight 3.0. Я работаю над оболочкой / фреймворком для проекта, который будет иметь список «плагинов», которые можно добавить в «рабочее пространство».

Плагины регистрируются в классе WorkSpaceManager во время их определенных методов PRISM IModule.Initialize (), например:

workspace.RegisterPlugin(new PluginInfo() { Name = "MyPlugin", ViewType = typeof(MyPluginView), SettingsViewType = null });

Метод RegisterPlugin () просто добавляет объект PluginInfo в словарь с ключом в свойстве «Имя». Затем, когда я хочу добавить плагин в рабочую область, я делаю следующее:

workspace.AddPluginToWorkspace("MyPlugin");

Метод AddPluginToWorkspace класса WorkspaceManager выглядит следующим образом:

public void AddPluginToWorkspace(string pluginName)
    {
        if (AvailablePlugins.ContainsKey(pluginName))
        {
            PluginInfo pi = AvailablePlugins[pluginName];
            WorkspacePlugin wsp = new WorkspacePlugin();

            // Create the View
            wsp.View = (Control)this.unityContainer.Resolve(pi.ViewType);
            wsp.Name = pi.Name;

            // Wire up the CloseCommand to WorkspaceManager's PluginClosing handler
            wsp.CloseCommand = new DelegateCommand<WorkspacePlugin>(this.PluginClosing);

            // Add the plugin to the active plugins (modules) collection
            this.modules.Add(wsp);

            // FIX: This should notify anyone listening that the ActivePlugins have changed. When enabled, this causes the same error that will be mentioned further on when attempting to close a plugin.
            //this.eventAggregator.GetEvent<ActivePluginsChanged>().Publish(wsp);
        }

    }

Рабочая область ViewModel просто предоставляет коллекцию модулей службы WorkspaceManager, которая представляет собой текстовый текст представления рабочей области, как показано здесь:

<Grid x:Name="LayoutRoot"
      Background="White">
    <ListBox x:Name="ModuleListBox"
             Grid.Row="1"
             rgn:RegionManager.RegionName="Workspace"
             Background="Yellow"
             ItemsSource="{Binding Plugins}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.Template>
            <ControlTemplate>
                <Grid x:Name="ListBoxGrid">
                    <ItemsPresenter></ItemsPresenter>
                </Grid>
            </ControlTemplate>
        </ListBox.Template>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Border BorderBrush="Black"
                        BorderThickness="2"
                        Margin="0"
                        Padding="0">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="20"></RowDefinition>
                            <RowDefinition Height="*"></RowDefinition>
                            <RowDefinition Height="5"></RowDefinition>
                        </Grid.RowDefinitions>
                        <Grid Grid.Row="0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"></ColumnDefinition>
                                <ColumnDefinition Width=".05*"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <Button Content="X"
                                    HorizontalAlignment="Right"
                                    Grid.Column="1"
                                    cmd:Click.Command="{Binding CloseCommand}"
                                    cmd:Click.CommandParameter="{Binding}"></Button>
                        </Grid>
                        <Border BorderBrush="Black"
                                BorderThickness="2"
                                Margin="0"
                                VerticalAlignment="Center"
                                HorizontalAlignment="Center"
                                Grid.Row="1">
                            <tk:Viewbox Stretch="Uniform"
                                        StretchDirection="Both">
                                <ContentControl Content="{Binding View}"></ContentControl>                    
                            </tk:Viewbox>
                        </Border>                            
                    </Grid>
                </Border>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

Обратите внимание на элемент управления Content, который привязан к свойству «View» WorkspacePlugin, и кнопку, у которой Click.Command привязан к «CloseCommand». Это то место, где я застрял изначально, но для по большей части это работает. Представление плагина загружается внутри других элементов управления, и я все еще могу связать команду закрытия (и другие команды, которые будут добавлены позже) с базовой моделью.

Проблема теперь в том, что всякий раз, когда я нажимаю кнопку закрытия и WorkspacePlugin удаляется из коллекции модулей, в ViewModel запускается событие изменения свойства, чтобы сообщить списку об обновлении. Я получаю следующее ошибка (это также происходит, если я раскомментирую строку под комментарием «FIX» выше:

System.ArgumentException: значение не попадает в ожидаемый диапазон. в MS.Internal.XcpImports.CheckHResult (UInt32 ч) в MS.Internal.XcpImports.SetValue (INativeCoreTypeWrapper obj, свойство DependencyProperty, DependencyObject doh) в MS.Internal.XcpImports.SetValue (INativeCoreTypeWrapper doh, свойство DependencyProperty, Object obj) в System.Windows.DependencyObject.SetObjectValueToCore (DependencyProperty dp, значение объекта) в System.Windows.DependencyObject.RefreshExpression (DependencyProperty dp) в System.Windows.Data.BindingExpression.RefreshExpression () в System.Windows.Data.BindingExpression.SendDataToTarget () в System.Windows.Data.BindingExpression.SourceAquired () в System.Windows.Data.BindingExpression.DataContextChanged (Object o, DataContextChangedEventArgs e) в System.Windows.FrameworkElement.OnDataContextChanged (DataContextChangedEventArgs e) в System.Windows.FrameworkElement.OnTreeParentUpdated (DependencyObject newParent, логический bIsNewParentAlive) в System.Windows.DependencyObject.UpdateTreeParent (IManagedPeer oldParent, IManagedPeer newParent, логический bIsNewParentAlive, логический keepReferenceToParent) в MS.Internal.FrameworkCallbacks.ManagedPeerTreeUpdate (IntPtr oldParentElement, IntPtr parentElement, IntPtr childElement, Байт bIsParentAlive, Байт bKeepReferenceToParent)

Из того, что я нашел, просматривая онлайн, это обычно означает, что визуальный элемент, который уже был добавлен в визуальное дерево, пытается быть добавлен снова. Этот вид делает, поскольку, если у меня есть только 1 плагин, отображаемый и закрывающий его, он исчезает и ошибки нет. Я вполне уверен, что ошибка связана с тем, что свойство WorkspacePlugin.View является визуальным элементом управления, а обновление привязки пытается повторно добавить его в визуальное дерево.

Как я могу обойти это или достичь желаемого результата без сообщения об ошибке?

Ответы [ 2 ]

0 голосов
/ 06 января 2010

Я получил эту работу, выполнив следующее:

Я создал представление WorkspaceItemView и ViewModel, которые выглядят примерно так:

<UserControl>
<Grid  x:Name="ResizeGrid"
       MouseEnter="Plugin_MouseEnter"
       MouseLeave="Plugin_MouseLeave">
    <Grid.RowDefinitions>
        <RowDefinition Height="20" />
        <RowDefinition Height="*" />
        <RowDefinition Height="5" />
    </Grid.RowDefinitions>

    <Border x:Name="border"
            BorderBrush="Black"
            BorderThickness="2"
            Padding="0"
            Margin="-1,-1,-1,-1">
    </Border>

    <Grid x:Name="grid"
          Grid.Row="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width=".05*" />
        </Grid.ColumnDefinitions>
        <Thumb HorizontalAlignment="Stretch"
               Background="Green"
               DragDelta="Thumb_DragDelta"
               />
        <Button Content="X"
                HorizontalAlignment="Right"
                Grid.Column="1"
                cmd:Click.Command="{Binding CloseCommand}"
                cmd:Click.CommandParameter="{Binding PluginID}" />
    </Grid>

    <Border BorderBrush="Black"
            BorderThickness="2"
            Margin="0"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            Grid.Row="1">
        <tk:Viewbox Stretch="Uniform"
                    StretchDirection="Both">
            <ContentControl rgn:RegionManager.RegionName="PluginViewRegion" />

        </tk:Viewbox>
    </Border>
    <Thumb x:Name="SizeGrip"
           Grid.Row="1"
           VerticalAlignment="Bottom"
           HorizontalAlignment="Right"
           Width="10"
           Height="10"
           Margin="0,0,-7,-7"
           Style="{StaticResource SizeGrip}"
           DragDelta="SizeGrip_DragDelta"
           DragStarted="SizeGrip_DragStarted"
           DragCompleted="SizeGrip_DragCompleted" />

</Grid>
</UserControl>  

public class WorkspaceItemViewModel : INotifyPropertyChanged
{
    private IWorkspaceManager workspaceManager;
    private IRegionManager regionManager;
    private Guid pluginID;

    public WorkspaceItemViewModel(IWorkspaceManager workspaceManager, IRegionManager regionManager)
    {
        this.workspaceManager = workspaceManager;
        this.regionManager = regionManager;
    }

    public DelegateCommand<object> CloseCommand 
    {
        get
        {
            return workspaceManager.CloseCommand;
        }    
    }

    public DelegateCommand<object> SelectCommand
    {
        get
        {
            return workspaceManager.SelectCommand;
        }
    }

    public object CloseCommandParameter
    {
        get
        {
            return this;
        }
    }

    public Guid PluginID
    {
        get
        {
            return this.pluginID;
        }
        set
        {
            this.pluginID = value;
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs("PluginID"));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

}

Код WorkspaceManager для добавления плагина в рабочее пространство выглядит следующим образом:

public void AddPluginToWorkspace(string pluginName)
    {
        PluginInfo pi = AvailablePlugins[pluginName];
        WorkspacePlugin wsp = new WorkspacePlugin();
        wsp.Name = pi.Name;
        wsp.CloseCommand = new DelegateCommand<object>(this.PluginClosing);
        wsp.SelectCommand = new DelegateCommand<object>(this.PluginSelected);
        wsp.id = System.Guid.NewGuid();
        this.modules.Add(wsp.id, wsp);

        var view = this.unityContainer.Resolve(pluginWindowType);
        if (view is IWorkspacePlugin)
        {
            var iwsp = view as IWorkspacePlugin;
            if (iwsp != null)
            {
                iwsp.PluginID = wsp.id;
            }
        }
        else
        {
            throw new ArgumentException("Plugin view containers must implement IWorkspacePlugin.");
        }

        var workspaceRegion = regionManager.Regions["Workspace"];
        var pluginRegion = workspaceRegion.Add(view, wsp.id.ToString(), true);
        this.unityContainer.RegisterInstance<IRegionManager>(wsp.id.ToString(), pluginRegion);
        pluginRegion.Regions["PluginViewRegion"].Context = view;
        pluginRegion.Regions["PluginViewRegion"].Add(this.unityContainer.Resolve(pi.ViewType));

        this.eventAggregator.GetEvent<ActivePluginsChanged>().Publish(wsp);

}

Это, по сути, создает область области действия, добавляет WorkspaceItemView в область рабочей области, а затем разрешает и добавляет представление фактического плагина в PluginViewRegion недавно добавленного WorkspaceItemView. У меня есть небольшая работа по уборке, но я думаю, что она работает довольно хорошо.

Спасибо за вашу помощь.

0 голосов
/ 17 декабря 2009

Скорее всего, вам не следует использовать как регион Prism, так и привязывать представления к вашему ListView через ItemsSource. Обычно люди выбирают одно или другое. Я думаю, что вы можете видеть какое-то странное поведение, потому что у вас есть оба.

Я бы предложил, чтобы ваши Модули добавляли «Плагины» в свой метод инициализации.

public MyModule : IModule
{
     IRegionManager _mgr;
     public MyModule(IRegionManager mgr)
     {
          _mgr = mgr;
     }

     public void Initialize()
     {
          _mgr.RegisterViewWithRegion("Workspace", typeof(MyPlugin));
     }

}

Вы сможете позвонить через день после этого. Вам не нужно собирать и предоставлять коллекцию плагинов из вашей оболочки для региона, в котором вы хотите их показать ... вы должны позволить своим модулям вносить их в регион самостоятельно. Это позволит вам поддерживать определенное количество абстракций и сделать ваши модули более автономными.

Удачи.

...