6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C# + WPF + MEFを使ってアプリにプラグインを実装してみる その2

Posted at

はじめに

C# + WPF + MEFを使ってアプリにプラグインを実装してみる その1 の続きです。
今回は、UserControlをプラグインとして分離したいと思います。

GitHub : https://github.com/isuzu-shiranui/CalculationApplication2

読み込み側

View (MainWindow)

外観

24e811368140649951cdb5df777a1164.png
今回はUserControlだけ置いてあるため、真っ白。

コード

Views/MainWindow.xaml

<Window x:Class="CalculationApplication2.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CalculationApplication2.Views"
        xmlns:vm="clr-namespace:CalculationApplication2.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="326" Width="591">

    <Window.DataContext>
        <vm:MainWindowViewModel />
    </Window.DataContext>

    <Grid>
        <UserControl Content="{Binding Plugin}" />
    </Grid>
</Window>

Views/MainWindow.xaml.cs

using System.Windows;

namespace CalculationApplication2.Views
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

ViewModel (MainWindowViewModel)

ViewModels/MainWindowViewModel

using Plugin;
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;

namespace CalculationApplication2.ViewModels
{
    public class MainWindowViewModel
    {
        [Import(typeof(IPlugin))]
        public IPlugin Plugin { get; set; }

        public MainWindowViewModel()
        {
            LoadPlugins();
        }

        /// <summary>
        /// プラグインを全て読みこんで、[Import]がついているプロパティに格納する。
        /// </summary>
        private void LoadPlugins()
        {
            //フォルダがなければ作る。
            string pluginsPath = Directory.GetCurrentDirectory() + @"\plugins";
            if (!Directory.Exists(pluginsPath)) Directory.CreateDirectory(pluginsPath);

            //プラグイン読み込み
            using (var catalog = new DirectoryCatalog(pluginsPath, "SummationPlugin.dll"))
            using (var container = new CompositionContainer(catalog))
            {
                if (catalog.LoadedFiles.Count > 0) container.SatisfyImportsOnce(this);
            }
        }
    }
}

##App.xaml

StartupUri="MainWindow.xaml"StartupUri="Views/MainWindow.xaml"に変更

プラグイン用インターフェース作成

IPlugin

namespace Plugin
{
    public interface IPlugin
    {
    }
}

UserControlプラグイン作成

View (SummationView)

外観

dd2c473fbe2f6e0ec2fff02f5f0c5b49.png
前回の読み込み側のViewをそのままUserControlに移しただけです(;・∀・)

コード

Views/SummationView.xaml

<UserControl x:Class="SummationPlugin.Views.SummationView"
             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:vm="clr-namespace:SummationPlugin.ViewModels"
             xmlns:local="clr-namespace:SummationPlugin"
             mc:Ignorable="d" 
             d:DesignHeight="300" Width="522">

    <UserControl.DataContext>
        <vm:SummationViewModel />
    </UserControl.DataContext>
    
    <Grid Background="White">
        <StackPanel HorizontalAlignment="Left" Height="24" Margin="10,113,0,0" VerticalAlignment="Top" Width="499" Orientation="Horizontal">
            <Label Content="左辺" />
            <TextBox Width="212" Text="{Binding LeftValue, Mode=TwoWay}"/>
            <Label Content="左辺" />
            <TextBox Width="212" Text="{Binding RightValue, Mode=TwoWay}"/>
        </StackPanel>
        <Button Content="実行" HorizontalAlignment="Left" Margin="425,239,0,0" VerticalAlignment="Top" Width="75" Command="{Binding CalculationCommand, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="44,172,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="211" Text="{Binding ResultValue}"/>
        <Label x:Name="label" Content="結果" HorizontalAlignment="Left" Margin="10,172,0,0" VerticalAlignment="Top"/>
    </Grid>
</UserControl>

Views/SummationView.xaml.cs

using Plugin;
using System.ComponentModel.Composition;
using System.Windows.Controls;

namespace SummationPlugin.Views
{
    /// <summary>
    /// SummationView.xaml の相互作用ロジック
    /// </summary>
    [Export(typeof(IPlugin))]
    public partial class SummationView : UserControl, IPlugin
    {
        public SummationView()
        {
            InitializeComponent();
        }
    }
}

ViewModel (SummationViewModel)

ViewModels/SummationViewModel.cs

using Prism.Commands;
using System.ComponentModel.DataAnnotations;

namespace SummationPlugin.ViewModels
{
    class SummationViewModel : Commons.ViewModelBase
    {
        private double leftValue;
        [Required]
        [Range(0, double.MaxValue)]
        public double LeftValue
        {
            get { return leftValue; }
            set { SetProperty(ref leftValue, value); CalculationCommand.RaiseCanExecuteChanged(); }
        }

        private double rightValue;
        [Required]
        [Range(0, double.MaxValue)]
        public double RightValue
        {
            get { return rightValue; }
            set { SetProperty(ref rightValue, value); CalculationCommand.RaiseCanExecuteChanged(); }
        }

        private double resultValue;
        public double ResultValue
        {
            get { return resultValue; }
            set { SetProperty(ref resultValue, value); }
        }

        private DelegateCommand calculationCommand;
        public DelegateCommand CalculationCommand => calculationCommand ?? (new DelegateCommand(CalculationExecute, () => !HasErrors));

        private void CalculationExecute()
        {
            ResultValue = LeftValue + RightValue;
        }
    }
}

ViewModelBase

前回と一緒...のはず

using Prism.Mvvm;
using System;
using System.Runtime.CompilerServices;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace SummationPlugin.Commons
{
    public class ViewModelBase : BindableBase, INotifyDataErrorInfo
    {
        private ErrorsContainer<string> errorsContainer;

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        public ViewModelBase()
        {
            errorsContainer = new ErrorsContainer<string>(propertyName => ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)));
        }

        public bool HasErrors => errorsContainer.HasErrors;

        public IEnumerable GetErrors(string propertyName)
        {
            return errorsContainer.GetErrors(propertyName);
        }

        protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if(!base.SetProperty(ref storage, value, propertyName)) return false;

            var context = new ValidationContext(this) { MemberName = propertyName };
            var errors = new List<ValidationResult>();
            if(!Validator.TryValidateProperty(value, context, errors))
            {
                errorsContainer.SetErrors(propertyName, errors.Select(error => error.ErrorMessage).ToArray());
            }
            else
            {
                errorsContainer.ClearErrors(propertyName);
            }
            return true;
        }
    }
}

動かしてみる

プラグインを配置する

まずは作ったプラグインをビルドします。
すると、プラグインのDebugまたはReleaseフォルダに、(プラグイン名).dllができていると思います。
0ea13532f170ded53995de29c71ad362.png

これを、読み込みアプリのDebugまたはReleaseフォルダにpluginフォルダがなければ作って貼り付けます。
今回は、プラグイン側にもPrismを使っているので、Prism.dllとPrism.Wpf.dllも追加します。
c7bce116c51320987f29c99ae98c689c.png

読み込み側アプリの動作確認

実際にアプリを起動して、計算が行われれば完成です。見た目上は前回と同じ動きになります。
45ba1eefcb45d4df5e0c684388cf4b84.gif

さいごに

見てもらって分かったと思いますが、基本的には前回と変わりません。

次は複数プラグインへの対応をやっていこうかなと思ってます。

冒頭にも乗っけてますが、GitHubにありますので試してみてください。
GitHub : https://github.com/isuzu-shiranui/CalculationApplication2

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?