--- 文件: App.xaml.cs ---
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace _2024_播放器
{
    /// <summary>
    /// App.xaml 的交互逻辑
    /// </summary>
    public partial class App : Application
    {
    }
}


--- 文件: MainWindow.xaml.cs ---
// WpfMediaPlayer/MainWindow.xaml.cs
using Microsoft.Win32;
using SharedInterfaces;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using WpfMediaPlayer.ViewModels;

namespace WpfMediaPlayer
{
    public partial class MainWindow : Window
    {
        private readonly DispatcherTimer timer;  // 用于更新进度条和时间显示
        private bool isDraggingSlider = false;   // 判断用户是否正在拖动进度条
        private readonly PluginManager pluginManager; // 插件管理器

        public MainWindow()
        {
            InitializeComponent();

            // 初始化插件管理器
            pluginManager = new PluginManager();

            // 设置 Popup 的 DataContext 为插件管理器
            pluginListPopup.DataContext = pluginManager;

            // 初始化计时器
            timer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(33) // 大约每秒30帧
            };
            timer.Tick += Timer_Tick;

            // 初始化音量
            volumeSlider.Value = 0.5;
            mediaElement.Volume = volumeSlider.Value;

            // 自动加载 Plugins 文件夹中的所有插件
            LoadAllPlugins();

            // 添加全局点击事件
            this.PreviewMouseDown += MainWindow_PreviewMouseDown;
        }

        #region 控件事件处理

        // 播放按钮事件
        private void PlayButton_Click(object sender, RoutedEventArgs e)
        {
            if (mediaElement.Source != null)
            {
                mediaElement.Play();
                timer.Start();
            }
            else
            {
                OpenFileDialog openFileDialog = new OpenFileDialog
                {
                    Filter = "媒体文件 (*.mpg;*.mp4;*.mp3;*.avi;*.mkv;*.flv)|*.mp4;*.mp3;*.avi;*.mkv;*.flv|所有文件 (*.*)|*.*"
                };

                if (openFileDialog.ShowDialog() == true)
                {
                    LoadAndPlayMedia(openFileDialog.FileName);
                }
            }
        }

        // 暂停按钮事件
        private void PauseButton_Click(object sender, RoutedEventArgs e)
        {
            mediaElement.Pause();
            timer.Stop();  // 暂停计时器
        }

        // 停止按钮事件
        private void StopButton_Click(object sender, RoutedEventArgs e)
        {
            mediaElement.Stop();
            timer.Stop();  // 停止计时器
            progressSlider.Value = 0;
            if (mediaElement.NaturalDuration.HasTimeSpan)
            {
                timeDisplay.Text = $"00:00:00 / {mediaElement.NaturalDuration.TimeSpan:hh\\:mm\\:ss}";
            }
            else
            {
                timeDisplay.Text = "00:00:00 / 00:00:00";
            }

            // 清空插件显示内容
            foreach (var plugin in pluginManager.IPlugins)
            {
                try
                {
                    // 如果插件需要处理null帧（如重置状态），调用其ProcessFrame
                    plugin.ProcessFrame(null);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"插件 {plugin.PluginName} 处理异常：{ex.Message}");
                }
            }
        }

        // 快退10秒按钮事件
        private void RewindButton_Click(object sender, RoutedEventArgs e)
        {
            if (mediaElement.Position.TotalSeconds > 10)
            {
                mediaElement.Position -= TimeSpan.FromSeconds(10);
            }
            else
            {
                mediaElement.Position = TimeSpan.Zero;
            }
        }

        // 快进10秒按钮事件
        private void ForwardButton_Click(object sender, RoutedEventArgs e)
        {
            if (mediaElement.NaturalDuration.HasTimeSpan)
            {
                if (mediaElement.Position.TotalSeconds + 10 < mediaElement.NaturalDuration.TimeSpan.TotalSeconds)
                {
                    mediaElement.Position += TimeSpan.FromSeconds(10);
                }
                else
                {
                    mediaElement.Position = mediaElement.NaturalDuration.TimeSpan;
                }
            }
        }

        // 音量滑块事件
        private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (mediaElement != null)
            {
                mediaElement.Volume = volumeSlider.Value;
            }
        }

        // 进度条滑块开始拖动事件
        private void ProgressSlider_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            isDraggingSlider = true;
            timer.Stop();
        }

        // 进度条滑块拖动结束事件
        private void ProgressSlider_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            isDraggingSlider = false;
            UpdateMediaPosition();
            timer.Start();
        }

        // 插件加载按钮点击事件，加载 IPluginBase 或 IPluginWithUI 类型的 DLL 并设置其处理逻辑
        private void PlugButton_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "DLL文件 (*.dll)|*.dll"
            };

            if (openFileDialog.ShowDialog() == true)
            {
                try
                {
                    // 加载插件
                    Assembly assembly = Assembly.LoadFile(openFileDialog.FileName);

                    // 获取实现了IPluginWithUI接口的类型
                    foreach (Type type in assembly.GetTypes())
                    {
                        if (typeof(IPluginWithUI).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                        {
                            IPluginWithUI pluginWithUI = (IPluginWithUI)Activator.CreateInstance(type);
                            pluginWithUI.Initialize();
                            pluginManager.AddIPluginWithUI(pluginWithUI);
                            MessageBox.Show($"插件 {pluginWithUI.PluginName} 加载成功，并设置为处理帧。");
                        }
                        else if (typeof(IPluginBase).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                        {
                            // 获取仅实现了IPluginBase接口的插件
                            IPluginBase pluginBase = (IPluginBase)Activator.CreateInstance(type);
                            pluginBase.Initialize();
                            pluginManager.AddIPlugin(pluginBase);
                            MessageBox.Show($"插件 {pluginBase.PluginName} 加载成功，并设置为处理帧。");
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"加载插件失败: {ex.Message}");
                }
            }
        }

        // 进度条滑块值变化事件
        private void ProgressSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (mediaElement != null && mediaElement.NaturalDuration.HasTimeSpan && !isDraggingSlider)
            {
                mediaElement.Position = TimeSpan.FromSeconds(progressSlider.Value);
                UpdateTimeDisplay();
            }
        }

        #endregion

        #region 媒体事件处理

        // 当媒体文件加载完成后
        private void MediaElement_MediaOpened(object sender, RoutedEventArgs e)
        {
            if (mediaElement.NaturalDuration.HasTimeSpan)
            {
                progressSlider.IsEnabled = true;
                progressSlider.Minimum = 0;
                progressSlider.Maximum = mediaElement.NaturalDuration.TimeSpan.TotalSeconds;
                progressSlider.Value = mediaElement.Position.TotalSeconds;

                // 设置时间显示
                timeDisplay.Text = $"{mediaElement.Position:hh\\:mm\\:ss} / {mediaElement.NaturalDuration.TimeSpan:hh\\:mm\\:ss}";
                Console.WriteLine("媒体文件已打开。");
            }
        }

        // 当媒体播放结束
        private void MediaElement_MediaEnded(object sender, RoutedEventArgs e)
        {
            mediaElement.Stop();
            timer.Stop();
            progressSlider.Value = 0;
            if (mediaElement.NaturalDuration.HasTimeSpan)
            {
                timeDisplay.Text = "00:00:00 / " + mediaElement.NaturalDuration.TimeSpan.ToString(@"hh\:mm\:ss");
            }
            else
            {
                timeDisplay.Text = "00:00:00 / 00:00:00";
            }

            // 清空插件显示内容
            foreach (var plugin in pluginManager.IPlugins)
            {
                try
                {
                    // 如果插件需要处理null帧（如重置状态），调用其ProcessFrame
                    plugin.ProcessFrame(null);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"插件 {plugin.PluginName} 处理异常：{ex.Message}");
                }
            }
        }

        #endregion

        #region 计时器和进度更新

        // 计时器更新视频帧及进度条
        private void Timer_Tick(object sender, EventArgs e)
        {
            if (mediaElement.NaturalDuration.HasTimeSpan && !isDraggingSlider)
            {
                progressSlider.Maximum = mediaElement.NaturalDuration.TimeSpan.TotalSeconds;
                progressSlider.Value = mediaElement.Position.TotalSeconds;

                // 更新时间显示
                UpdateTimeDisplay();

                // 捕获并处理当前帧
                CaptureAndProcessFrame();
            }
        }

        // 更新时间显示
        private void UpdateTimeDisplay()
        {
            if (mediaElement.NaturalDuration.HasTimeSpan)
            {
                timeDisplay.Text = $"{mediaElement.Position:hh\\:mm\\:ss} / {mediaElement.NaturalDuration.TimeSpan:hh\\:mm\\:ss}";
            }
            else
            {
                timeDisplay.Text = $"{mediaElement.Position:hh\\:mm\\:ss} / 00:00:00";
            }
        }

        // 捕获当前视频帧并处理
        private void CaptureAndProcessFrame()
        {
            BitmapSource frame = CaptureCurrentFrame();
            if (frame != null)
            {
                Console.WriteLine("捕获到一帧视频。");

                // 在 UI 线程上处理帧
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    BitmapSource processedFrame = frame;

                    // 依次应用所有启用的 IPluginBase 插件
                    foreach (var plugin in pluginManager.IPlugins.Where(p => p.IsEnabled))
                    {
                        try
                        {
                            processedFrame = plugin.ProcessFrame(processedFrame);
                            Console.WriteLine($"插件 {plugin.PluginName} 处理完成。");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"插件 {plugin.PluginName} 处理异常：{ex.Message}");
                        }
                    }

                    // 更新图像显示
                    imageDisplay.Source = processedFrame;
                    Console.WriteLine("更新 imageDisplay.Source 成功。");
                }), DispatcherPriority.Background);
            }
            else
            {
                Console.WriteLine("帧捕获返回null。");
            }
        }

        // 捕获当前视频帧的方法
        private BitmapSource CaptureCurrentFrame()
        {
            double width = mediaElement.ActualWidth;
            double height = mediaElement.ActualHeight;

            if (width == 0 || height == 0)
                return null;

            try
            {
                RenderTargetBitmap rtb = new RenderTargetBitmap(
                    (int)width, (int)height,
                    96, 96,
                    PixelFormats.Pbgra32); // 确保正确的像素格式

                DrawingVisual dv = new DrawingVisual();
                using (DrawingContext dc = dv.RenderOpen())
                {
                    dc.DrawRectangle(new VisualBrush(mediaElement), null, new Rect(new Point(), new Size(width, height)));
                }
                rtb.Render(dv);
                return rtb;
            }
            catch
            {
                return null;
            }
        }

        #endregion

        #region 插件管理

        // 自动加载 Plugins 文件夹中的所有插件
        private void LoadAllPlugins()
        {
            string pluginsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");

            if (Directory.Exists(pluginsPath))
            {
                pluginManager.LoadPlugins(pluginsPath);
            }
            else
            {
                // 如果 Plugins 文件夹不存在，创建一个
                Directory.CreateDirectory(pluginsPath);
                Console.WriteLine("Plugins 文件夹已创建。");
            }
        }

        #endregion

        #region 拖放处理

        // 统一处理拖放事件
        private void Window_DragOver(object sender, DragEventArgs e)
        {
            HandleDragOver(e, allowDll: false);
        }

        private void Window_Drop(object sender, DragEventArgs e)
        {
            HandleDrop(e, allowDll: false);
        }

        private void PluginListPopup_DragOver(object sender, DragEventArgs e)
        {
            HandleDragOver(e, allowDll: true);
        }

        private void PluginListPopup_Drop(object sender, DragEventArgs e)
        {
            HandleDrop(e, allowDll: true);
        }

        // 统一处理拖放事件的逻辑
        private void HandleDragOver(DragEventArgs e, bool allowDll = false)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                var files = (string[])e.Data.GetData(DataFormats.FileDrop);
                bool hasValidFile = false;

                foreach (var file in files)
                {
                    string extension = Path.GetExtension(file).ToLower();
                    if (allowDll && extension == ".dll")
                    {
                        hasValidFile = true;
                        break;
                    }
                    else if (!allowDll && IsMediaFile(extension))
                    {
                        hasValidFile = true;
                        break;
                    }
                }

                e.Effects = hasValidFile ? DragDropEffects.Copy : DragDropEffects.None;
            }
            else
            {
                e.Effects = DragDropEffects.None;
            }

            e.Handled = true;
        }

        private void HandleDrop(DragEventArgs e, bool allowDll = false)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                foreach (string filePath in files)
                {
                    string extension = Path.GetExtension(filePath).ToLower();

                    if (allowDll && extension == ".dll")
                    {
                        // 加载插件 DLL
                        try
                        {
                            Assembly assembly = Assembly.LoadFile(filePath);
                            foreach (Type type in assembly.GetTypes())
                            {
                                if (typeof(IPluginWithUI).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                                {
                                    IPluginWithUI pluginWithUI = (IPluginWithUI)Activator.CreateInstance(type);
                                    pluginWithUI.Initialize();
                                    pluginManager.AddIPluginWithUI(pluginWithUI);
                                    Console.WriteLine($"插件 {pluginWithUI.PluginName} 加载成功。");
                                }
                                else if (typeof(IPluginBase).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                                {
                                    // 获取仅实现了IPluginBase接口的插件
                                    IPluginBase pluginBase = (IPluginBase)Activator.CreateInstance(type);
                                    pluginBase.Initialize();
                                    pluginManager.AddIPlugin(pluginBase);
                                    Console.WriteLine($"插件 {pluginBase.PluginName} 加载成功，无UI控件。");
                                }
                            }

                            MessageBox.Show($"插件加载成功: {filePath}");
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show($"加载插件失败: {ex.Message}");
                            Console.WriteLine($"加载插件失败: {ex.Message}");
                        }
                    }
                    else if (!allowDll && IsMediaFile(extension))
                    {
                        // 加载并播放媒体文件
                        LoadAndPlayMedia(filePath);
                    }
                }
            }
        }

        // 判断是否为支持的媒体文件
        private bool IsMediaFile(string extension)
        {
            string[] supportedExtensions = { ".mp4", ".mp3", ".avi", ".mkv", ".flv" };
            return supportedExtensions.Contains(extension);
        }

        #endregion

        #region 外部点击关闭 Popup

        private void MainWindow_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (pluginListPopup.IsOpen)
            {
                // 获取点击的元素
                DependencyObject clickedElement = e.OriginalSource as DependencyObject;

                // 检查点击是否在 Popup 内部
                if (!IsDescendant(pluginListPopup.Child, clickedElement))
                {
                    pluginListPopup.IsOpen = false;

                    // 如果使用 ToggleButton，取消选中状态
                    pluginListButton.IsChecked = false;
                }
            }
        }

        // 递归检查是否为子孙元素
        private bool IsDescendant(DependencyObject parent, DependencyObject child)
        {
            if (child == null) return false;
            if (parent == child) return true;

            DependencyObject current = child;
            while (current != null)
            {
                if (current == parent)
                    return true;
                current = VisualTreeHelper.GetParent(current);
            }
            return false;
        }

        #endregion

        #region 媒体播放

        // 加载并播放媒体文件
        private void LoadAndPlayMedia(string filePath)
        {
            try
            {
                mediaElement.Source = new Uri(filePath);
                mediaElement.Play();
                timer.Start(); // 启动计时器以更新进度条
                Console.WriteLine($"播放媒体文件: {filePath}");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"无法播放文件: {ex.Message}", "播放错误", MessageBoxButton.OK, MessageBoxImage.Error);
                Console.WriteLine($"无法播放文件: {ex.Message}");
            }
        }

        // 更新视频播放位置
        private void UpdateMediaPosition()
        {
            if (mediaElement != null && mediaElement.NaturalDuration.HasTimeSpan)
            {
                mediaElement.Position = TimeSpan.FromSeconds(progressSlider.Value);
            }
        }

        #endregion

        #region Expander Toggle

        private void ExpanderToggle_Click(object sender, RoutedEventArgs e)
        {
            Button button = sender as Button;
            if (button != null)
            {
                Expander expander = button.Parent as Expander;
                if (expander != null)
                {
                    expander.IsExpanded = !expander.IsExpanded;
                }
            }
        }

        #endregion
    }
}


--- 文件: PluginManager.cs ---
using SharedInterfaces;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
using WpfMediaPlayer.ViewModels;

namespace WpfMediaPlayer
{
    public class PluginManager
    {
        public ObservableCollection<IPluginBase> IPlugins { get; private set; } = new ObservableCollection<IPluginBase>();
        public ObservableCollection<IPluginWithUI> IPluginsWithUI { get; private set; } = new ObservableCollection<IPluginWithUI>();
        public ObservableCollection<PluginViewModel> PluginViewModels { get; private set; } = new ObservableCollection<PluginViewModel>();

        /// <summary>
        /// 加载指定路径中的所有插件DLL
        /// </summary>
        /// <param name="pluginsPath">插件文件夹路径</param>
        public void LoadPlugins(string pluginsPath)
        {
            if (!Directory.Exists(pluginsPath))
                return;

            var dllFiles = Directory.GetFiles(pluginsPath, "*.dll");
            foreach (var dll in dllFiles)
            {
                try
                {
                    Assembly assembly = Assembly.LoadFrom(dll);
                    var types = assembly.GetTypes();

                    foreach (var type in types)
                    {
                        if (typeof(IPluginWithUI).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                        {
                            // 插件实现了IPluginWithUI
                            IPluginWithUI pluginWithUI = (IPluginWithUI)Activator.CreateInstance(type);
                            pluginWithUI.Initialize();
                            IPlugins.Add(pluginWithUI);
                            IPluginsWithUI.Add(pluginWithUI);
                            PluginViewModels.Add(new PluginViewModel(pluginWithUI));
                            Console.WriteLine($"Loaded IPluginWithUI: {pluginWithUI.PluginName}");
                        }
                        else if (typeof(IPluginBase).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                        {
                            // 插件仅实现了IPluginBase
                            IPluginBase pluginBase = (IPluginBase)Activator.CreateInstance(type);
                            pluginBase.Initialize();
                            IPlugins.Add(pluginBase);
                            PluginViewModels.Add(new PluginViewModel(pluginBase));
                            Console.WriteLine($"Loaded IPluginBase: {pluginBase.PluginName}");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Failed to load plugin from {dll}: {ex.Message}");
                }
            }
        }

        /// <summary>
        /// 卸载所有插件
        /// </summary>
        public void UnloadPlugins()
        {
            // 卸载IPluginWithUI插件
            foreach (var pluginWithUI in IPluginsWithUI)
            {
                pluginWithUI.GetControl()?.Dispatcher.Invoke(() => pluginWithUI.GetControl().ClearValue(UserControl.ContentProperty));
            }
            IPluginsWithUI.Clear();

            // 卸载IPluginBase插件
            IPlugins.Clear();
            PluginViewModels.Clear();
        }

        /// <summary>
        /// 添加一个IPluginBase插件
        /// </summary>
        /// <param name="plugin">插件实例</param>
        public void AddIPlugin(IPluginBase plugin)
        {
            if (plugin != null)
            {
                plugin.Initialize();
                IPlugins.Add(plugin);
                PluginViewModels.Add(new PluginViewModel(plugin));
                Console.WriteLine($"Loaded IPluginBase: {plugin.PluginName}");
            }
        }

        /// <summary>
        /// 添加一个IPluginWithUI插件
        /// </summary>
        /// <param name="plugin">插件实例</param>
        public void AddIPluginWithUI(IPluginWithUI plugin)
        {
            if (plugin != null)
            {
                plugin.Initialize();
                IPlugins.Add(plugin);
                IPluginsWithUI.Add(plugin);
                PluginViewModels.Add(new PluginViewModel(plugin));
                Console.WriteLine($"Loaded IPluginWithUI: {plugin.PluginName}");
            }
        }
    }
}


--- 文件: PluginParameterViewModel.cs ---
using System.ComponentModel;
using SharedInterfaces;

namespace WpfMediaPlayer.ViewModels
{
    public class PluginParameterViewModel : INotifyPropertyChanged
    {
        private double _value;

        public string Name { get; set; }
        public string DisplayName { get; set; }
        public double Value
        {
            get => _value;
            set
            {
                if (_value != value)
                {
                    _value = value;
                    OnPropertyChanged(nameof(Value));
                    // 更新插件的参数
                    if (Plugin != null)
                    {
                        Plugin.Parameters[Name] = value;
                    }
                }
            }
        }
        public double Minimum { get; set; }
        public double Maximum { get; set; }

        public IPluginWithUI Plugin { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}


--- 文件: PluginViewModel.cs ---
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using SharedInterfaces;
using System.Windows.Controls;

namespace WpfMediaPlayer.ViewModels
{
    public class PluginViewModel : INotifyPropertyChanged
    {
        private bool _isEnabled;

        public string PluginName => Plugin.PluginName;

        public bool IsEnabled
        {
            get => _isEnabled;
            set
            {
                if (_isEnabled != value)
                {
                    _isEnabled = value;
                    Plugin.IsEnabled = value;
                    OnPropertyChanged(nameof(IsEnabled));
                }
            }
        }

        public ObservableCollection<PluginParameterViewModel> Parameters { get; set; } = new ObservableCollection<PluginParameterViewModel>();

        public IPluginBase Plugin { get; set; }

        public UserControl Control
        {
            get
            {
                if (Plugin is IPluginWithUI pluginWithUI)
                {
                    return pluginWithUI.GetControl();
                }
                return null;
            }
        }

        public PluginViewModel(IPluginBase plugin)
        {
            Plugin = plugin;
            IsEnabled = plugin.IsEnabled;

            if (plugin is IPluginWithUI pluginWithUI)
            {
                foreach (var param in pluginWithUI.Parameters)
                {
                    Parameters.Add(new PluginParameterViewModel
                    {
                        Name = param.Key,
                        DisplayName = param.Key, // 可以根据需要更改显示名称
                        Value = Convert.ToDouble(param.Value),
                        Minimum = 0, // 根据插件定义设置
                        Maximum = 2, // 根据插件定义设置
                        Plugin = pluginWithUI
                    });
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}


--- 文件: IPluginbase.cs ---
// SharedInterfaces/IPluginBase.cs
using System.Windows.Media.Imaging;

namespace SharedInterfaces
{
    public interface IPluginBase
    {
        /// <summary>
        /// 插件名称
        /// </summary>
        string PluginName { get; }

        /// <summary>
        /// 插件是否启用
        /// </summary>
        bool IsEnabled { get; set; }

        /// <summary>
        /// 初始化插件
        /// </summary>
        void Initialize();

        /// <summary>
        /// 处理视频帧数据，并返回处理后的帧
        /// </summary>
        /// <param name="frame">当前视频帧</param>
        /// <returns>处理后的 BitmapSource 视频帧</returns>
        BitmapSource ProcessFrame(BitmapSource frame);
    }
}


--- 文件: IPluginWithUI.cs ---
// SharedInterfaces/IPluginWithUI.cs
using System.Collections.Generic;
using System.Windows.Controls;

namespace SharedInterfaces
{
    public interface IPluginWithUI : IPluginBase
    {
        /// <summary>
        /// 获取插件的 UserControl 界面
        /// </summary>
        /// <returns>UserControl 用于显示</returns>
        UserControl GetControl();

        /// <summary>
        /// 获取插件的参数字典
        /// </summary>
        IDictionary<string, object> Parameters { get; }
    }
}


--- 文件: PluginParameter.cs ---
// SharedInterfaces/PluginParameter.cs
using System.ComponentModel;

namespace SharedInterfaces
{
    public class PluginParameter : INotifyPropertyChanged
    {
        private string name;
        private double value;
        private double min;
        private double max;

        public string Name
        {
            get => name;
            set { name = value; OnPropertyChanged(nameof(Name)); }
        }

        public double Value
        {
            get => value;
            set { this.value = value; OnPropertyChanged(nameof(Value)); }
        }

        public double Min
        {
            get => min;
            set { min = value; OnPropertyChanged(nameof(Min)); }
        }

        public double Max
        {
            get => max;
            set { max = value; OnPropertyChanged(nameof(Max)); }
        }

        public PluginParameter(string name, double value, double min, double max)
        {
            Name = name;
            Value = value;
            Min = min;
            Max = max;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}


--- 文件: IComponent.cs ---
// SharedInterfaces/IComponent.cs
using System.Windows.Controls;

namespace SharedInterfaces
{
    public interface IComponent
    {
        /// <summary>
        /// 组件的名称
        /// </summary>
        string ComponentName { get; }

        /// <summary>
        /// 初始化组件
        /// </summary>
        void Initialize();

        /// <summary>
        /// 获取组件的 UserControl 界面
        /// </summary>
        /// <returns>UserControl用于显示</returns>
        UserControl GetControl();

        /// <summary>
        /// 释放组件资源
        /// </summary>
        void Unload();
    }
}

老板帮我评估这个软件 能不能支持 外部经过 WhiteEnablEBIT 后 通过DISpater 更新处理的视频帧输出到UI的右边输出区域 并且可以启动逐行扫描？比如我要进行视频的个别像素移动一定要开启CAPTURE不能用其他方法吗