• MvvmLight + Microsoft.Extensions.DependencyInjection + WpfApp(.NetCore3.1)


    git clone MvvmLight失败,破网络, 就没有直接修改源码的方式来使用了

    Nuget安装MvvmLightLibsStd10

    使用GalaSoft.MvvmLight.Command命名空间下的RelayCommand会有一个Bug, CanExecute的返回不会更新UI, 在GalaSoft.MvvmLight.CommandWpf中进行了Fixed, 然而MvvmLightLibsStd10并没有GalaSoft.MvvmLight.CommandWpf, 直接粗暴的把GalaSoft.MvvmLight.CommandWpf下面的RelayCommand提到工程中

    using GalaSoft.MvvmLight.Helpers;
    using System;
    using System.Threading;
    using System.Windows.Input;
    
    namespace TraceApp.LinkLib.Wpf.Extras
    {
        /// <summary>
        /// A command whose sole purpose is to relay its functionality to other
        /// objects by invoking delegates. The default return value for the CanExecute
        /// method is 'true'.  This class does not allow you to accept command parameters in the
        /// Execute and CanExecute callback methods.
        /// </summary>
        /// <remarks>If you are using this class in WPF4.5 or above, you need to use the
        /// GalaSoft.MvvmLight.CommandWpf namespace (instead of GalaSoft.MvvmLight.Command).
        /// This will enable (or restore) the CommandManager class which handles
        /// automatic enabling/disabling of controls based on the CanExecute delegate.</remarks>
        public class RelayCommand : ICommand
        {
            private readonly WeakAction _execute;
            private readonly WeakFunc<bool> _canExecute;
            private EventHandler _requerySuggestedLocal;
    
            /// <summary>
            /// Initializes a new instance of the RelayCommand class that
            /// can always execute.
            /// </summary>
            /// <param name="execute">The execution logic. IMPORTANT: If the action causes a closure,
            /// you must set keepTargetAlive to true to avoid side effects. </param>
            /// <param name="keepTargetAlive">If true, the target of the Action will
            /// be kept as a hard reference, which might cause a memory leak. You should only set this
            /// parameter to true if the action is causing a closure. See
            /// http://galasoft.ch/s/mvvmweakaction. </param>
            /// <exception cref="T:System.ArgumentNullException">If the execute argument is null.</exception>
            public RelayCommand(Action execute, bool keepTargetAlive = false)
              : this(execute, (Func<bool>)null, keepTargetAlive)
            {
            }
    
            /// <summary>Initializes a new instance of the RelayCommand class.</summary>
            /// <param name="execute">The execution logic. IMPORTANT: If the action causes a closure,
            /// you must set keepTargetAlive to true to avoid side effects. </param>
            /// <param name="canExecute">The execution status logic.  IMPORTANT: If the func causes a closure,
            /// you must set keepTargetAlive to true to avoid side effects. </param>
            /// <param name="keepTargetAlive">If true, the target of the Action will
            /// be kept as a hard reference, which might cause a memory leak. You should only set this
            /// parameter to true if the action is causing a closures. See
            /// http://galasoft.ch/s/mvvmweakaction. </param>
            /// <exception cref="T:System.ArgumentNullException">If the execute argument is null.</exception>
            public RelayCommand(Action execute, Func<bool> canExecute, bool keepTargetAlive = false)
            {
                if (execute == null)
                    throw new ArgumentNullException(nameof(execute));
                this._execute = new WeakAction(execute, keepTargetAlive);
                if (canExecute == null)
                    return;
                this._canExecute = new WeakFunc<bool>(canExecute, keepTargetAlive);
            }
    
            /// <summary>
            /// Occurs when changes occur that affect whether the command should execute.
            /// </summary>
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    if (this._canExecute == null)
                        return;
                    EventHandler eventHandler = this._requerySuggestedLocal;
                    EventHandler comparand;
                    do
                    {
                        comparand = eventHandler;
                        eventHandler = Interlocked.CompareExchange<EventHandler>(ref this._requerySuggestedLocal, comparand + value, comparand);
                    }
                    while (eventHandler != comparand);
                    CommandManager.RequerySuggested += value;
                }
                remove
                {
                    if (this._canExecute == null)
                        return;
                    EventHandler eventHandler = this._requerySuggestedLocal;
                    EventHandler comparand;
                    do
                    {
                        comparand = eventHandler;
                        eventHandler = Interlocked.CompareExchange<EventHandler>(ref this._requerySuggestedLocal, comparand - value, comparand);
                    }
                    while (eventHandler != comparand);
                    CommandManager.RequerySuggested -= value;
                }
            }
    
            /// <summary>
            /// Raises the <see cref="E:GalaSoft.MvvmLight.CommandWpf.RelayCommand.CanExecuteChanged" /> event.
            /// </summary>
            public void RaiseCanExecuteChanged()
            {
                CommandManager.InvalidateRequerySuggested();
            }
    
            /// <summary>
            /// Defines the method that determines whether the command can execute in its current state.
            /// </summary>
            /// <param name="parameter">This parameter will always be ignored.</param>
            /// <returns>true if this command can be executed; otherwise, false.</returns>
            public bool CanExecute(object parameter)
            {
                if (this._canExecute == null)
                    return true;
                if (this._canExecute.IsStatic || this._canExecute.IsAlive)
                    return this._canExecute.Execute();
                return false;
            }
    
            /// <summary>
            /// Defines the method to be called when the command is invoked.
            /// </summary>
            /// <param name="parameter">This parameter will always be ignored.</param>
            public virtual void Execute(object parameter)
            {
                if (!this.CanExecute(parameter) || this._execute == null || !this._execute.IsStatic && !this._execute.IsAlive)
                    return;
                this._execute.Execute();
            }
        }
    
        /// <summary>
        /// A generic command whose sole purpose is to relay its functionality to other
        /// objects by invoking delegates. The default return value for the CanExecute
        /// method is 'true'. This class allows you to accept command parameters in the
        /// Execute and CanExecute callback methods.
        /// </summary>
        /// <typeparam name="T">The type of the command parameter.</typeparam>
        /// <remarks>If you are using this class in WPF4.5 or above, you need to use the
        /// GalaSoft.MvvmLight.CommandWpf namespace (instead of GalaSoft.MvvmLight.Command).
        /// This will enable (or restore) the CommandManager class which handles
        /// automatic enabling/disabling of controls based on the CanExecute delegate.</remarks>
        public class RelayCommand<T> : ICommand
        {
            private readonly WeakAction<T> _execute;
            private readonly WeakFunc<T, bool> _canExecute;
    
            /// <summary>
            /// Initializes a new instance of the RelayCommand class that
            /// can always execute.
            /// </summary>
            /// <param name="execute">The execution logic. IMPORTANT: If the action causes a closure,
            /// you must set keepTargetAlive to true to avoid side effects. </param>
            /// <param name="keepTargetAlive">If true, the target of the Action will
            /// be kept as a hard reference, which might cause a memory leak. You should only set this
            /// parameter to true if the action is causing a closure. See
            /// http://galasoft.ch/s/mvvmweakaction. </param>
            /// <exception cref="T:System.ArgumentNullException">If the execute argument is null.</exception>
            public RelayCommand(Action<T> execute, bool keepTargetAlive = false)
              : this(execute, (Func<T, bool>)null, keepTargetAlive)
            {
            }
    
            /// <summary>Initializes a new instance of the RelayCommand class.</summary>
            /// <param name="execute">The execution logic. IMPORTANT: If the action causes a closure,
            /// you must set keepTargetAlive to true to avoid side effects. </param>
            /// <param name="canExecute">The execution status logic.  IMPORTANT: If the func causes a closure,
            /// you must set keepTargetAlive to true to avoid side effects. </param>
            /// <param name="keepTargetAlive">If true, the target of the Action will
            /// be kept as a hard reference, which might cause a memory leak. You should only set this
            /// parameter to true if the action is causing a closure. See
            /// http://galasoft.ch/s/mvvmweakaction. </param>
            /// <exception cref="T:System.ArgumentNullException">If the execute argument is null.</exception>
            public RelayCommand(Action<T> execute, Func<T, bool> canExecute, bool keepTargetAlive = false)
            {
                if (execute == null)
                    throw new ArgumentNullException(nameof(execute));
                this._execute = new WeakAction<T>(execute, keepTargetAlive);
                if (canExecute == null)
                    return;
                this._canExecute = new WeakFunc<T, bool>(canExecute, keepTargetAlive);
            }
    
            /// <summary>
            /// Occurs when changes occur that affect whether the command should execute.
            /// </summary>
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    if (this._canExecute == null)
                        return;
                    CommandManager.RequerySuggested += value;
                }
                remove
                {
                    if (this._canExecute == null)
                        return;
                    CommandManager.RequerySuggested -= value;
                }
            }
    
            /// <summary>
            /// Raises the <see cref="E:GalaSoft.MvvmLight.CommandWpf.RelayCommand`1.CanExecuteChanged" /> event.
            /// </summary>
            public void RaiseCanExecuteChanged()
            {
                CommandManager.InvalidateRequerySuggested();
            }
    
            /// <summary>
            /// Defines the method that determines whether the command can execute in its current state.
            /// </summary>
            /// <param name="parameter">Data used by the command. If the command does not require data
            /// to be passed, this object can be set to a null reference</param>
            /// <returns>true if this command can be executed; otherwise, false.</returns>
            public bool CanExecute(object parameter)
            {
                if (this._canExecute == null)
                    return true;
                if (this._canExecute.IsStatic || this._canExecute.IsAlive)
                {
                    if (parameter == null && typeof(T).IsValueType)
                        return this._canExecute.Execute(default(T));
                    if (parameter == null || parameter is T)
                        return this._canExecute.Execute((T)parameter);
                }
                return false;
            }
    
            /// <summary>
            /// Defines the method to be called when the command is invoked.
            /// </summary>
            /// <param name="parameter">Data used by the command. If the command does not require data
            /// to be passed, this object can be set to a null reference</param>
            public virtual void Execute(object parameter)
            {
                object parameter1 = parameter;
                if (parameter != null && parameter.GetType() != typeof(T) && parameter is IConvertible)
                    parameter1 = Convert.ChangeType(parameter, typeof(T), (IFormatProvider)null);
                if (!this.CanExecute(parameter1) || this._execute == null || !this._execute.IsStatic && !this._execute.IsAlive)
                    return;
                if (parameter1 == null)
                {
                    if (typeof(T).IsValueType)
                        this._execute.Execute(default(T));
                    else
                        this._execute.Execute(default(T));
                }
                else
                    this._execute.Execute((T)parameter1);
            }
        }
    }
    

    一不做二不休, 发现MvvmLightLibsStd10中也没有DispatcherHelper, 也提取出来

    using System;
    using System.Text;
    using System.Windows.Threading;
    
    namespace TraceApp.LinkLib.Wpf.Extras
    {
        /// <summary>
        /// Helper class for dispatcher operations on the UI thread.
        /// </summary>
        public static class DispatcherHelper
        {
            /// <summary>
            /// Gets a reference to the UI thread's dispatcher, after the
            /// <see cref="M:GalaSoft.MvvmLight.Threading.DispatcherHelper.Initialize" /> method has been called on the UI thread.
            /// </summary>
            public static Dispatcher UIDispatcher { get; private set; }
    
            /// <summary>
            /// Executes an action on the UI thread. If this method is called
            /// from the UI thread, the action is executed immendiately. If the
            /// method is called from another thread, the action will be enqueued
            /// on the UI thread's dispatcher and executed asynchronously.
            /// <para>For additional operations on the UI thread, you can get a
            /// reference to the UI thread's dispatcher thanks to the property
            /// <see cref="P:GalaSoft.MvvmLight.Threading.DispatcherHelper.UIDispatcher" /></para>.
            /// </summary>
            /// <param name="action">The action that will be executed on the UI
            /// thread.</param>
            public static void CheckBeginInvokeOnUI(Action action)
            {
                if (action == null)
                    return;
                DispatcherHelper.CheckDispatcher();
                if (DispatcherHelper.UIDispatcher.CheckAccess())
                    action();
                else
                    DispatcherHelper.UIDispatcher.BeginInvoke((Delegate)action);
            }
    
            private static void CheckDispatcher()
            {
                if (DispatcherHelper.UIDispatcher == null)
                {
                    StringBuilder stringBuilder = new StringBuilder("The DispatcherHelper is not initialized.");
                    stringBuilder.AppendLine();
                    stringBuilder.Append("Call DispatcherHelper.Initialize() in the static App constructor.");
                    throw new InvalidOperationException(stringBuilder.ToString());
                }
            }
    
            /// <summary>Invokes an action asynchronously on the UI thread.</summary>
            /// <param name="action">The action that must be executed.</param>
            /// <returns>An object, which is returned immediately after BeginInvoke is called, that can be used to interact
            /// with the delegate as it is pending execution in the event queue.</returns>
            public static DispatcherOperation RunAsync(Action action)
            {
                DispatcherHelper.CheckDispatcher();
                return DispatcherHelper.UIDispatcher.BeginInvoke((Delegate)action);
            }
    
            /// <summary>
            /// This method should be called once on the UI thread to ensure that
            /// the <see cref="P:GalaSoft.MvvmLight.Threading.DispatcherHelper.UIDispatcher" /> property is initialized.
            /// <para>In a Silverlight application, call this method in the
            /// Application_Startup event handler, after the MainPage is constructed.</para>
            /// <para>In WPF, call this method on the static App() constructor.</para>
            /// </summary>
            public static void Initialize()
            {
                if (DispatcherHelper.UIDispatcher != null && DispatcherHelper.UIDispatcher.Thread.IsAlive)
                    return;
                DispatcherHelper.UIDispatcher = Dispatcher.CurrentDispatcher;
            }
    
            /// <summary>
            /// Resets the class by deleting the <see cref="P:GalaSoft.MvvmLight.Threading.DispatcherHelper.UIDispatcher" />
            /// </summary>
            public static void Reset()
            {
                DispatcherHelper.UIDispatcher = (Dispatcher)null;
            }
        }
    }
    
    Nuget安装Microsoft.Extensions.DependencyInjection
    创建一个类AppLocator, 在App.Xaml中申明为全局资源来做Service的注册来View的绑定

    AppLocator

    using System;
    using System.IO;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using Trace.Model;
    using TraceApp.DB;
    using TraceApp.ViewModel;
    
    namespace TraceApp
    {
        public class AppLocator
        {
            public IServiceProvider ServiceProvider { get; private set; }
            public IConfiguration Configuration { get; private set; }
    
            public AppLocator()
            {
                var builder = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", true, true);
                Configuration = builder.Build();
    
                var collection = new ServiceCollection();
                ConfigureServices(collection);
                ServiceProvider = collection.BuildServiceProvider();
            }
    
            private void ConfigureServices(IServiceCollection services)
            {
                // Configuration
                services.Configure<AppSettings>(Configuration.GetSection(nameof(AppSettings)));
                // Database
                services.AddDbContext<MyContext>(options =>
                {
                    options.UseSqlite("Data Source=TraceApp.db3");
                    options.UseLoggerFactory(LoggerFactory.Create(builder =>
                    {
                        builder.AddFilter((category, level) =>
                            category == DbLoggerCategory.Database.Command.Name
                            && level == LogLevel.Information);
                        builder.AddConsole();
                    }));
                });
    
                #region Register ViewModel
                services.AddSingleton<MainWindowViewModel>();
                #endregion
            }
    
            #region ViewModel's DataContexts
            public MainWindowViewModel MainWindow => ServiceProvider.GetService<MainWindowViewModel>();
            #endregion
        }
    }
    
    运行, 效果是自己想要的

    附上App.Xaml、MainWindow.Xaml、MainWindowViewModel.cs的代码

    App.Xaml

    <Application  x:Class="TraceApp.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:TraceApp">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <!--Handy Control-->
                    <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
                    <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
                </ResourceDictionary.MergedDictionaries>
                <local:AppLocator x:Key="AppLocator"/>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    MainWindow.Xaml

    <Window x:Class="TraceApp.View.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"
            mc:Ignorable="d"
            DataContext="{Binding Source={StaticResource AppLocator}, Path=MainWindow}"
            Title="MainWindow" d:DesignHeight="500" d:DesignWidth="800">
        <Grid>
            <WrapPanel>
                <TextBlock Text="{Binding Title}"/>
                <Button Content="测试命令" Command="{Binding TestCommand}"/>
                <CheckBox IsChecked="{Binding Enable, Mode=TwoWay}"/>
            </WrapPanel>
        </Grid>
    </Window>
    

    MainWindowViewModel

    using GalaSoft.MvvmLight;
    using HandyControl.Controls;
    using TraceApp.LinkLib.Wpf.Extras;
    
    namespace TraceApp.ViewModel
    {
        public class MainWindowViewModel : ViewModelBase
        {
            private string _title = "Wpf Mvvm Application";
            public string Title
            {
                get => _title;
                set => Set(ref _title, value);
            }
    
            private bool _enable = false;
            public bool Enable { get => _enable; set => Set(ref _enable, value); }
    
            public RelayCommand TestCommand => new RelayCommand(() => { MessageBox.Show("Test!"); }, 
                () => Enable);
    
            public MainWindowViewModel()
            {
                
            }
        }
    }
    
    没有了Nuget安装MvvmLight在.NetCore中使用的Warnning, 强迫症的心情愉快了很多

  • 相关阅读:
    JS框架设计读书笔记之-选择器引擎02
    JS框架设计读书笔记之-选择器引擎01
    JS框架设计读书笔记之-小知识
    JS框架设计读书笔记之-函数
    JS框架设计读书笔记之-核心模块
    7.19 NOIP模拟6
    一 网络基础之网络协议篇
    Socket 网络编程
    Python 常用模块
    类的特殊成员
  • 原文地址:https://www.cnblogs.com/linxmouse/p/12988225.html
Copyright © 2020-2023  润新知