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

时间:2024-05-01 06:47:56

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
}
}
运行, 效果是自己想要的

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

附上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, 强迫症的心情愉快了很多

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