从单独的类文件中调用命令

huangapple go评论71阅读模式
英文:

Calling a Command from a Separate Class File

问题

以下是您要翻译的内容:

"So I have been at it for days, and for the life of me cannot find any documentation that fits my situation exactly here.

I have essentially set up a custom navigation service and would like to call the command from my ViewModel Class directly from my User Control.

I think I'm on the edge of having it here, but my lack of experience with C# is shooting me in the foot.

Here is the section of code from my Login.xaml.cs in question:

private LoginViewModel _loginViewModel;
public Login(LoginViewModel loginViewModel)
{
_loginViewModel = loginViewModel;
}
private void GrantAccess()
{
int userAccess = Int16.Parse(User.Access);
if (userAccess == 1)
{
MessageBox.Show("The bottom man");
}
if (userAccess == 2)
{
MessageBox.Show("The little boss");
}
if (userAccess == 3)
{
MessageBox.Show("The little big boss");
}
if (userAccess == 4)
{
{
_loginViewModel.NavigateMM1Command.Execute(null);
}
}
}"

"and here is the command I'm trying to reference from the ViewModel:

public class LoginViewModel : BaseViewModel
{
public ICommand NavigateMM1Command { get; }
public LoginViewModel(NavigationStore navigationStore)
{
NavigateMM1Command = new NavigateCommand(new NavigationService(navigationStore, () => new MM1ViewModel(navigationStore)));
}
}"

"Basically I've been going through tutorial after tutorial trying to apply what they teach to what I need and its worked for the most part but now _loginViewModel is throwing a null reference exception and I'm not sure why.

I have tried:

LoginViewModel loginViewModel = new loginViewModel();

but its asking me to pass a navigationStore argument through it and that feels wrong.
Any help here will cure my temporary insanity XD"

英文:

So I have been at it for days, and for the life of me cannot find any documentation that fits my situation exactly here.

I have essentially set up a custom navigation service and would like to call the command from my ViewModel Class directly from my User Control.

I think I'm on the edge of having it here, but my lack of experience with C# is shooting me in the foot.

Here is the section of code from my Login.xaml.cs in question:

private LoginViewModel _loginViewModel;
        public Login(LoginViewModel loginViewModel)
        {
            _loginViewModel = loginViewModel;
        }
        private void GrantAccess()
        {
            int userAccess = Int16.Parse(User.Access);
            if (userAccess == 1)
            {
                MessageBox.Show("The bottom man");
            }
            if (userAccess == 2)
            {
                MessageBox.Show("The little boss");
            }
            if (userAccess == 3)
            {
                MessageBox.Show("The little big boss");
            }
            if (userAccess == 4)
            {
                {
                    _loginViewModel.NavigateMM1Command.Execute(null);
                }
            }

        }

and here is the command I'm trying to reference from the ViewModel:

public class LoginViewModel : BaseViewModel
    {
        public ICommand NavigateMM1Command { get; }
        public LoginViewModel(NavigationStore navigationStore)
        {
            NavigateMM1Command = new NavigateCommand<MM1ViewModel>(new NavigationService<MM1ViewModel>(navigationStore, () => new MM1ViewModel(navigationStore)));
        }
    }

Basically I've been going through tutorial after tutorial trying to apply what they teach to what I need and its worked for the most part but now _loginViewModel is throwing a null reference exception and I'm not sure why.

I have tried:

LoginViewModel loginViewModel = new loginViewModel();

but its asking me to pass a navigationStore argument through it and that feels wrong.
Any help here will cure my temporary insanity XD

答案1

得分: 1

你之所以收到“Null Object Reference”错误是因为在构建LoginViewModel时,navigationStorenull。也就是说,在构建LoginViewModel时,你没有配置一种方法来实例化navigationStore类型。


依赖注入(DI)或控制反转(IoC)是一个比较全面的主题,无法在此回答中详细涵盖。

说了这么多,我将提供要审查的代码。这段代码表示一种配置服务提供程序的方式,使用一组类型映射。

在这个完整的ConsoleApp示例中,我们将显式实例化一个ServiceCollection,添加服务类型(在应用程序中指定映射),然后构建ServiceProvider。使用该提供程序,我们将使用GetService来解析和实例化Login类型,从而实例化所有类型;

这些类型本质上是您指定的类型的模拟,但我修改了一些方面(例如,您的Execute方法和对NavigationStore的使用)。

DemoNavTypes

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleDemo.NavLoginDemo
{
    public interface ICommand 
    {
        void Execute(string? userName);
    }

    public interface INavigationStore {
        public bool this[string index] { get;set; }
    }

    public interface INavigationService {
        void GrantAccessToUser(string userName);
    }
    public interface INavigationViewModel { }

    internal class NavigationStore : INavigationStore
    {
        private Dictionary<string, bool> userAccessDict;

        public NavigationStore() { 
            userAccessDict = new Dictionary<string, bool>();
        }

        public bool this[string index] { 
            get => userAccessDict.TryGetValue(index, out var val) && val; 
            set => userAccessDict[index] = value; 
        }
    }

    // ...(其它接口和类的定义)
}

Program.cs(使用Microsoft.Extensions.DependencyInjection)

using Microsoft.Extensions.DependencyInjection;
using System.Collections.Immutable;
using System.ComponentModel.Design;
using System.Linq;
using ConsoleDemo.NavLoginDemo;

internal class Program
{

    private static async Task Main(string[] args)
    {
        var services = new ServiceCollection();
        var provder = ConfigureServices(services);

        var login = provder.GetService<Login>();

        if (login != null)
        {
            await login.GrantAccessAsync();
            login.SetAccess(2);
            await login.GrantAccessAsync();
            login.SetAccess(3);
            await login.GrantAccessAsync();
            login.SetUserName("James Bond");
            login.SetAccess(4);
            await login.GrantAccessAsync();
        }

    }

    private static IServiceProvider ConfigureServices(IServiceCollection services)
    {
        return services
            .AddScoped<INavigationStore, NavigationStore>()
            .AddScoped<INavigationService, NavigationService>()
            .AddScoped<ICommand, NavigationCommand>()
            .AddScoped<LoginViewModel>()
            .AddScoped<Login>()
            .BuildServiceProvider();
    }
}

注意

在您的应用程序中,您可能已经在Program.cs或Startup.cs文件中拥有一个ServiceCollection实例。ServiceProvider(或HostProvider)将由应用程序进行管理;因此,您可能不需要显式解析(或GetService<T>) - 只需在ServiceCollection中添加服务类型(映射),这些参数类型将被实例化并“注入”到正在实例化的类型的构造函数中。

英文:

You're receiving a Null Object Reference because navigationStore is null when LoginViewModel is being constructed.

That is, you have not configured a means to instantiate the type navigationStore when constructing LoginViewModel.


Dependency Injection (DI), or Invocation of Control (IoC) is bit more comprehensive a subject to cover in this answer.

Having said that,
I'll provide code to review here. It represents a means to configure a service provider using a collection of type mappings.

In this complete, ConsoleApp example, we'll explicitly instantiate a ServiceCollection, add Service Types (specifying mapping where application), and Build the ServiceProvider; With that provider, we'll resolve and instantiate Login type using GetService -- instantiating all the types;

The Types are essentially mockups of the types you've specified, but I've modified some aspects (an made up notioned like what your Execute method and usage of NavigationStore was).

DemoNavTypes

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleDemo.NavLoginDemo
{
    public interface ICommand 
    {
        void Execute(string? userName);
    }

    public interface INavigationStore {
        public bool this[string index] { get;set; }
    }

    public interface INavigationService {
        void GrantAccessToUser(string userName);
    }
    public interface INavigationViewModel { }

    internal class NavigationStore : INavigationStore
    {
        private Dictionary&lt;string, bool&gt; userAccessDict;

        public NavigationStore() { 
            userAccessDict = new Dictionary&lt;string, bool&gt;();
        }

        public bool this[string index] { 
            get =&gt; userAccessDict.TryGetValue(index, out var val) &amp;&amp; val; 
            set =&gt; userAccessDict[index] = value; 
        }
    }

    internal class NavigationService : INavigationService
    {
        private readonly INavigationStore _navigationStore;

        public NavigationService(INavigationStore navigationStore) 
        {
            _navigationStore = navigationStore;
        }

        public void GrantAccessToUser(string? userName)
        {
            if (string.IsNullOrWhiteSpace(userName))
                throw new ArgumentException(nameof(userName));

            _navigationStore[userName!] = true;
        }
    }

    internal class NavigationCommand : ICommand
    {
        private readonly INavigationService _navigationService;
        public NavigationCommand(INavigationService navigationService) 
        {
            _navigationService = navigationService;
        }

        public void Execute(string? userName)
        {
            if (userName != null)
            {
                _navigationService.GrantAccessToUser(userName);
            }
        }
    }

    internal class User
    {
        public string? Name { get; set; }
        public string Access { get; set; } = &quot;1&quot;;
    }

    public abstract class BaseViewModel
    {
        internal User User { get; set; } = new User();

        protected BaseViewModel() { }
    }

    internal class LoginViewModel : BaseViewModel, INavigationViewModel
    {
        private readonly ICommand _command;

        public LoginViewModel(ICommand command) : base()
        {
            _command = command;
        }

        internal ICommand NavigateMM1Command =&gt; _command;
    }

    internal class Login
    {

        private User User =&gt; _loginViewModel.User;

        private readonly LoginViewModel _loginViewModel;
        public Login(LoginViewModel loginViewModel) 
        { 
            _loginViewModel = loginViewModel;   
        }

        internal void SetAccess(int access)
        {
            SetAccess($&quot;{access}&quot;);
        }
        internal void SetAccess(string access)
        {
            User.Access = access; 
        }

        internal void SetUserName(string userName) { User.Name = userName; }

        internal async Task GrantAccessAsync()
        {
            await Task.Yield();

            int userAccess = Int16.Parse(User.Access);
            switch (userAccess)
            {
                case 1:
                    Console.WriteLine(&quot;The bottom man&quot;);
                    break;
                case 2:
                    Console.WriteLine(&quot;The little boss&quot;);
                    break;
                case 3:
                    Console.WriteLine(&quot;The little big boss&quot;);
                    break;
                case 4:
                    _loginViewModel.NavigateMM1Command.Execute(User.Name);
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

    }
}

Program.cs (using Microsoft.Extensions.DependencyInjection)

using Microsoft.Extensions.DependencyInjection;
using System.Collections.Immutable;
using System.ComponentModel.Design;
using System.Linq;
using ConsoleDemo.NavLoginDemo;

internal class Program
{

    private static async Task Main(string[] args)
    {
        var services = new ServiceCollection();
        var provder = ConfigureServices(services);

        var login = provder.GetService&lt;Login&gt;();

        if (login != null)
        {
            await login.GrantAccessAsync();
            login.SetAccess(2);
            await login.GrantAccessAsync();
            login.SetAccess(3);
            await login.GrantAccessAsync();
            login.SetUserName(&quot;James Bond&quot;);
            login.SetAccess(4);
            await login.GrantAccessAsync();
        }

    }

    private static IServiceProvider ConfigureServices(IServiceCollection services)
    {
        return services
            .AddScoped&lt;INavigationStore, NavigationStore&gt;()
            .AddScoped&lt;INavigationService, NavigationService&gt;()
            .AddScoped&lt;ICommand, NavigationCommand&gt;()
            .AddScoped&lt;LoginViewModel&gt;()
            .AddScoped&lt;Login&gt;()
            .BuildServiceProvider();
    }
}

Note

-- However,
In your application, you'll probably already have a ServiceCollection instance in your Program.cs or Startup.cs file. And the ServiceProvider (or HostProvider) will be managed over by the application; So, you probably won't need to explicitly resolve (or GetService&lt;T&gt;) -- just add the Service Type (mappings) in ServiceCollection. Those parameter types will be instantiated and 'injected' into the constructor of Type that is itself being instantiated.

huangapple
  • 本文由 发表于 2023年1月9日 01:00:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75049716.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定