未验证 提交 8f217dd1 编写于 作者: D Dan Siegel 提交者: GitHub

Merge pull request #2886 from PrismLibrary/dev/ds/vml-update

Move ViewRegistry to Prism.Core
using Prism.Common;
using Prism.Mvvm;
using Prism.Services;
namespace Prism.Ioc;
......
using Prism.Common;
using Prism.Mvvm;
namespace Prism.Ioc;
......
using Prism.Common;
using Prism.Mvvm;
namespace Prism.Ioc;
......
using Microsoft.Maui.Controls.Compatibility;
using Prism.Common;
using Prism.Mvvm;
using Prism.Regions;
using Prism.Regions.Adapters;
using Prism.Regions.Behaviors;
......
using Prism.Common;
using Prism.Ioc;
namespace Prism.Mvvm;
public interface IViewRegistry
{
IEnumerable<ViewRegistration> Registrations { get; }
object CreateView(IContainerProvider container, string name);
Type GetViewType(string name);
string GetViewModelNavigationKey(Type viewModelType);
IEnumerable<ViewRegistration> ViewsOfType(Type baseType);
bool IsRegistered(string name);
}
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Prism.Common;
using Prism.Ioc;
using Prism.Ioc;
using Prism.Navigation.Xaml;
namespace Prism.Mvvm;
public abstract class ViewRegistryBase : IViewRegistry
public abstract class ViewRegistryBase : ViewRegistryBase<BindableObject>
{
private readonly IEnumerable<ViewRegistration> _registrations;
private readonly ViewType _registryType;
protected ViewRegistryBase(ViewType registryType, IEnumerable<ViewRegistration> registrations)
: base(registryType, registrations)
{
_registrations = registrations;
_registryType = registryType;
}
public IEnumerable<ViewRegistration> Registrations =>
_registrations.Where(x => x.Type == _registryType);
public Type GetViewType(string name) =>
GetRegistration(name)?.View;
public object CreateView(IContainerProvider container, string name)
protected override void Autowire(BindableObject view)
{
try
{
var registration = GetRegistration(name);
if (registration is null)
throw new KeyNotFoundException($"No view with the name '{name}' has been registered");
var view = container.Resolve(registration.View) as BindableObject;
view.SetValue(ViewModelLocator.NavigationNameProperty, registration.Name);
view.SetContainerProvider(container);
ConfigureView(view, container);
if (registration.ViewModel is not null)
view.SetValue(ViewModelLocator.ViewModelProperty, registration.ViewModel);
Autowire(view);
if (view.BindingContext is not null)
return;
return view;
}
catch (KeyNotFoundException)
{
throw;
}
catch (ViewModelCreationException)
{
throw;
}
catch (Exception ex)
{
throw new ViewCreationException(name, _registryType, ex);
}
ViewModelLocator.Autowire(view);
}
private IEnumerable<Type> GetCandidates(Type viewModelType)
protected override void SetContainerProvider(BindableObject view, IContainerProvider container)
{
var names = new List<string>
{
Regex.Replace(viewModelType.Name, @"ViewModel$", string.Empty),
Regex.Replace(viewModelType.Name, @"Model$", string.Empty),
};
if (_registryType == ViewType.Page)
names.Add(Regex.Replace(viewModelType.Name, @"ViewModel$", "Page"));
else if (_registryType == ViewType.Region)
names.Add(Regex.Replace(viewModelType.Name, @"ViewModel$", "Region"));
else if (_registryType == ViewType.Dialog)
names.Add(Regex.Replace(viewModelType.Name, @"ViewModel$", "Dialog"));
names = names.Where(x => !x.EndsWith("PagePage")).ToList();
var namespaces = _registryType switch
{
ViewType.Page => new[]
{
viewModelType.Namespace.Replace("ViewModels", "Views"),
viewModelType.Namespace.Replace("ViewModels", "Pages")
},
ViewType.Region => new[]
{
viewModelType.Namespace.Replace("ViewModels", "Views"),
viewModelType.Namespace.Replace("ViewModels", "Regions")
},
ViewType.Dialog => new[]
{
viewModelType.Namespace.Replace("ViewModels", "Views"),
viewModelType.Namespace.Replace("ViewModels", "Dialogs")
},
_ => new[]
{
viewModelType.Namespace.Replace("ViewModels", "Views"),
}
};
var candidates = namespaces.Select(@namespace => names.Select(name => $"{@namespace}.{name}"))
.SelectMany(x => x)
.Select(x => viewModelType.AssemblyQualifiedName.Replace(viewModelType.FullName, x));
return candidates
.Select(x => Type.GetType(x, false))
.Where(x => x is not null);
view.SetContainerProvider(container);
}
public string GetViewModelNavigationKey(Type viewModelType)
protected override void SetNavigationNameProperty(BindableObject view, string name)
{
var registration = Registrations.LastOrDefault(x => x.ViewModel == viewModelType);
if (registration is not null)
return registration.Name;
var candidates = GetCandidates(viewModelType);
registration = Registrations.LastOrDefault(x => candidates.Any(c => c == x.View));
if (registration is not null)
{
return registration.Name;
}
throw new KeyNotFoundException($"No View with the ViewModel '{viewModelType.Name}' has been registered");
view.SetValue(ViewModelLocator.NavigationNameProperty, name);
}
public IEnumerable<ViewRegistration> ViewsOfType(Type baseType) =>
Registrations.Where(x => x.View == baseType || x.View.IsAssignableTo(baseType));
public bool IsRegistered(string name) =>
GetRegistration(name) is not null;
protected ViewRegistration GetRegistration(string name) =>
Registrations.LastOrDefault(x => x.Name == name);
protected abstract void ConfigureView(BindableObject bindable, IContainerProvider container);
protected void Autowire(BindableObject view)
protected override void SetViewModelProperty(BindableObject view, Type viewModelType)
{
if (view.BindingContext is not null)
return;
ViewModelLocator.Autowire(view);
view.SetValue(ViewModelLocator.ViewModelProperty, viewModelType);
}
//public static Type GetPageType(string name)
//{
// var registrations = _registrations.Where(x => x.Name == name);
// if (!registrations.Any())
// throw new KeyNotFoundException(name);
// if (registrations.Count() > 1)
// throw new InvalidOperationException(string.Format(Resources.MultipleViewsRegisteredForNavigationKey, name, string.Join(", ", registrations.Select(x => x.View.FullName))));
// return registrations.First().View;
//}
}
......@@ -34,6 +34,14 @@ public sealed class PrismAppBuilder
_registrations = new List<Action<IContainerRegistry>>();
_initializations = new List<Action<IContainerProvider>>();
ViewModelCreationException.SetViewNameDelegate(view =>
{
if (view is BindableObject bindable)
return Mvvm.ViewModelLocator.GetNavigationName(bindable);
return $"View is not a BindableObject: '{view.GetType().FullName}";
});
MauiBuilder = builder;
MauiBuilder.ConfigureContainer(new PrismServiceProviderFactory(RegistrationCallback));
MauiBuilder.ConfigureLifecycleEvents(lifecycle =>
......
......@@ -2,6 +2,7 @@
using Prism.Common;
using Prism.Ioc;
using Prism.Ioc.Internals;
using Prism.Mvvm;
using Prism.Properties;
namespace Prism.Regions.Navigation;
......
using Prism.Common;
using Prism.Ioc;
using Prism.Ioc;
using Prism.Mvvm;
namespace Prism.Regions;
......
......@@ -3,6 +3,7 @@ using System.Reflection;
using Prism.Common;
using Prism.Events;
using Prism.Ioc;
using Prism.Mvvm;
using Prism.Properties;
namespace Prism.Regions;
......
using Prism.Ioc;
using Prism.Mvvm;
using Prism.Common;
namespace Prism.Services;
......
using System;
using System.Collections.Generic;
using Prism.Ioc;
namespace Prism.Mvvm;
/// <summary>
/// Provides an abstraction layer for ViewRegistration that can be mocked
/// </summary>
public interface IViewRegistry
{
/// <summary>
/// The existing ViewRegistrations
/// </summary>
IEnumerable<ViewRegistration> Registrations { get; }
/// <summary>
/// Creates a view given a specified instance of the Container and a navigation name
/// </summary>
/// <param name="container"></param>
/// <param name="name"></param>
/// <returns></returns>
object CreateView(IContainerProvider container, string name);
/// <summary>
/// Gets the ViewType for a specified navigation name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Type GetViewType(string name);
/// <summary>
/// Gets the navigation name for a specified ViewModelType
/// </summary>
/// <param name="viewModelType"></param>
/// <returns></returns>
string GetViewModelNavigationKey(Type viewModelType);
/// <summary>
/// Gets the Registrations where the View is of a given base type
/// </summary>
/// <param name="baseType"></param>
/// <returns></returns>
IEnumerable<ViewRegistration> ViewsOfType(Type baseType);
/// <summary>
/// Confirms whether the given navigation name has been registered
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
bool IsRegistered(string name);
}
namespace Prism.Mvvm;
using System;
using System.ComponentModel;
namespace Prism.Mvvm;
public class ViewModelCreationException : Exception
{
private static Func<object, string> _viewNameDelegate = null;
private static string GetViewName(object view) => _viewNameDelegate is null ? "Platform not initialized" : _viewNameDelegate(view);
[EditorBrowsable(EditorBrowsableState.Never)]
public static void SetViewNameDelegate(Func<object, string> viewNameDelegate) => _viewNameDelegate = viewNameDelegate;
public ViewModelCreationException(object view, Exception innerException)
: base($"Unable to Create ViewModel for '{view.GetType().FullName}'.", innerException)
{
if (view is VisualElement visualElement)
{
View = visualElement;
ViewName = (string)visualElement.GetValue(ViewModelLocator.NavigationNameProperty);
}
View = view;
ViewName = GetViewName(view);
}
public string ViewName { get; }
public VisualElement View { get; }
public object View { get; }
}
namespace Prism.Common;
using System;
namespace Prism.Mvvm;
public record ViewRegistration
{
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Prism.Ioc;
namespace Prism.Mvvm;
public abstract class ViewRegistryBase<TBaseView> : IViewRegistry
where TBaseView : class
{
private readonly IEnumerable<ViewRegistration> _registrations;
private readonly ViewType _registryType;
protected ViewRegistryBase(ViewType registryType, IEnumerable<ViewRegistration> registrations)
{
_registrations = registrations;
_registryType = registryType;
}
public IEnumerable<ViewRegistration> Registrations =>
_registrations.Where(x => x.Type == _registryType);
public Type GetViewType(string name) =>
GetRegistration(name)?.View;
public object CreateView(IContainerProvider container, string name)
{
try
{
var registration = GetRegistration(name);
if (registration is null)
throw new KeyNotFoundException($"No view with the name '{name}' has been registered");
var view = container.Resolve(registration.View) as TBaseView;
SetNavigationNameProperty(view, registration.Name);
//;
//;
SetContainerProvider(view, container);
ConfigureView(view, container);
if (registration.ViewModel is not null)
SetViewModelProperty(view, registration.ViewModel);
//
Autowire(view);
return view;
}
catch (KeyNotFoundException)
{
throw;
}
catch (ViewModelCreationException)
{
throw;
}
catch (Exception ex)
{
throw new ViewCreationException(name, _registryType, ex);
}
}
private IEnumerable<Type> GetCandidates(Type viewModelType)
{
var names = new List<string>
{
Regex.Replace(viewModelType.Name, @"ViewModel$", string.Empty),
Regex.Replace(viewModelType.Name, @"Model$", string.Empty),
};
if (_registryType == ViewType.Page)
names.Add(Regex.Replace(viewModelType.Name, @"ViewModel$", "Page"));
else if (_registryType == ViewType.Region)
names.Add(Regex.Replace(viewModelType.Name, @"ViewModel$", "Region"));
else if (_registryType == ViewType.Dialog)
names.Add(Regex.Replace(viewModelType.Name, @"ViewModel$", "Dialog"));
names = names.Where(x => !x.EndsWith("PagePage")).ToList();
var namespaces = _registryType switch
{
ViewType.Page => new[]
{
viewModelType.Namespace.Replace("ViewModels", "Views"),
viewModelType.Namespace.Replace("ViewModels", "Pages")
},
ViewType.Region => new[]
{
viewModelType.Namespace.Replace("ViewModels", "Views"),
viewModelType.Namespace.Replace("ViewModels", "Regions")
},
ViewType.Dialog => new[]
{
viewModelType.Namespace.Replace("ViewModels", "Views"),
viewModelType.Namespace.Replace("ViewModels", "Dialogs")
},
_ => new[]
{
viewModelType.Namespace.Replace("ViewModels", "Views"),
}
};
var candidates = namespaces.Select(@namespace => names.Select(name => $"{@namespace}.{name}"))
.SelectMany(x => x)
.Select(x => viewModelType.AssemblyQualifiedName.Replace(viewModelType.FullName, x));
return candidates
.Select(x => Type.GetType(x, false))
.Where(x => x is not null);
}
public string GetViewModelNavigationKey(Type viewModelType)
{
var registration = Registrations.LastOrDefault(x => x.ViewModel == viewModelType);
if (registration is not null)
return registration.Name;
var candidates = GetCandidates(viewModelType);
registration = Registrations.LastOrDefault(x => candidates.Any(c => c == x.View));
if (registration is not null)
{
return registration.Name;
}
throw new KeyNotFoundException($"No View with the ViewModel '{viewModelType.Name}' has been registered");
}
public IEnumerable<ViewRegistration> ViewsOfType(Type baseType) =>
Registrations.Where(x => x.View == baseType || baseType.IsAssignableFrom(x.View));
public bool IsRegistered(string name) =>
GetRegistration(name) is not null;
protected ViewRegistration GetRegistration(string name) =>
Registrations.LastOrDefault(x => x.Name == name);
protected abstract void ConfigureView(TBaseView view, IContainerProvider container);
/// <summary>
/// Calls the platform code to Autowire the View if it does not have a ViewModel already
/// </summary>
/// <param name="view"></param>
protected abstract void Autowire(TBaseView view);
/// <summary>
/// Sets the specified navigation name that was used to Navigate. This can be useful for back navigation
/// </summary>
/// <param name="view"></param>
/// <param name="name"></param>
protected abstract void SetNavigationNameProperty(TBaseView view, string name);
/// <summary>
/// Sets the ViewModel Type to resolve
/// </summary>
/// <param name="view"></param>
/// <param name="viewModelType"></param>
protected abstract void SetViewModelProperty(TBaseView view, Type viewModelType);
/// <summary>
/// Sets the IContainerProvider making it easier to access on the given View
/// </summary>
/// <param name="view"></param>
/// <param name="container"></param>
protected abstract void SetContainerProvider(TBaseView view, IContainerProvider container);
//public static Type GetPageType(string name)
//{
// var registrations = _registrations.Where(x => x.Name == name);
// if (!registrations.Any())
// throw new KeyNotFoundException(name);
// if (registrations.Count() > 1)
// throw new InvalidOperationException(string.Format(Resources.MultipleViewsRegisteredForNavigationKey, name, string.Join(", ", registrations.Select(x => x.View.FullName))));
// return registrations.First().View;
//}
}
namespace Prism.Common;
namespace Prism.Mvvm;
public enum ViewType
{
......
using System;
using System.Linq;
#if NETSTANDARD || NET47
namespace System.Runtime.CompilerServices;
internal static class IsExternalInit
{
}
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using Prism.Common;
namespace Prism.Maui.Tests.Mocks.Ioc;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册