自定义区域/区域管理器中用户控件的设计时渲染
本文关键字:区域 控件 管理器 用户 自定义 | 更新日期: 2023-09-27 18:04:01
我真的很喜欢MS Prism框架的导航实现,但我并不需要所有的功能,而且我一直在视图和视图模型的临时生命周期管理方面遇到问题。因此,我仔细研究了代码并重新实现了我自己的区域/区域管理器,添加了一些额外的功能。
到目前为止,一切都很好,但是有一些设计时的支持,我想,但不知道如何实现。我添加了一个DefaultViewType附加属性。我希望能够在设计器中渲染那个视图。
另一个奇怪的是,Visual Studio(或Resharper)在设计器中报告了以下问题,据我所知,这是不真实的:
'RegionName'属性已被'ContentControl'注册。
下面是代码(也可在https://github.com/agartee/StackOverFlowRegionManagerQuestion.git):
获得)My RegionManager是一个订阅MVVM轻消息的服务,类型为NavigationRequestMessage:
public class RegionManager : IRegionManager
{
private readonly IDictionary<string, ContentControl> _regions =
new Dictionary<string, ContentControl>();
public RegionManager(IMessenger messenger)
{
messenger.Register<NavigationRequestMessage>(this, Navigate);
}
public void RegisterRegion(string regionName, ContentControl region)
{
if (_regions.ContainsKey(regionName))
_regions.Remove(regionName);
_regions.Add(regionName, region);
}
public void Navigate(NavigationRequestMessage message)
{
if (!_regions.ContainsKey(message.RegionName)) return;
var targetRegion = _regions[message.RegionName];
if (message.ViewType != null)
{
var view = ServiceLocator.Current.GetInstance<UserControl>(message.ViewType.Name);
targetRegion.Content = view;
}
else
{
targetRegion.Content = null;
}
}
}
public class NavigationRequestMessage
{
public string RegionName { get; private set; }
public Type ViewType { get; private set; }
public NavigationRequestMessage(string regionName, Type viewType)
{
RegionName = regionName;
ViewType = viewType;
}
}
包含附加属性的区域:
public static class Region
{
public static readonly DependencyProperty RegionNameProperty = DependencyProperty.RegisterAttached(
"RegionName",
typeof(string),
typeof(ContentControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, RegionNameChanged));
public static string GetRegionName(UIElement element)
{
return (string)element.GetValue(RegionNameProperty);
}
public static void SetRegionName(UIElement element, string value)
{
element.SetValue(RegionNameProperty, value);
}
public static readonly DependencyProperty DefaultViewTypeProperty = DependencyProperty.RegisterAttached(
"DefaultViewType",
typeof(Type),
typeof(ContentControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public static Type GetDefaultViewType(UIElement element)
{
return (Type)element.GetValue(DefaultViewTypeProperty);
}
public static void SetDefaultViewType(UIElement element, Type value)
{
element.SetValue(DefaultViewTypeProperty, value);
}
private static void RegionNameChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var regionName = (string)e.NewValue;
var region = (ContentControl)dependencyObject;
RoutedEventHandler regionOnLoaded = null;
regionOnLoaded = (sender, args) =>
{
region.Loaded -= regionOnLoaded;
RegisterRegion(regionName, region);
NavigateToDefaultView(dependencyObject);
};
region.Loaded += regionOnLoaded;
}
private static void RegisterRegion(string regionName, ContentControl region)
{
var navigationService = ServiceLocator.Current.GetInstance<IRegionManager>();
navigationService.RegisterRegion(regionName, region);
}
public static ContentControl GetParentView(DependencyObject child)
{
var parent = VisualTreeHelper.GetParent(child);
if (parent == null)
return null;
var parentWindow = parent as Window;
if (parentWindow != null)
return parentWindow;
var parentUserControl = parent as UserControl;
return parentUserControl ?? GetParentView(parent);
}
private static void NavigateToDefaultView(DependencyObject dependencyObject)
{
var regionName = (string)dependencyObject.GetValue(RegionNameProperty);
var defaultViewType = (Type)dependencyObject.GetValue(DefaultViewTypeProperty);
if (String.IsNullOrWhiteSpace(regionName) || defaultViewType == null)
return;
var navigationService = ServiceLocator.Current.GetInstance<IRegionManager>();
navigationService.Navigate(new NavigationRequestMessage(regionName, defaultViewType));
}
}
通过以下更改,我能够完成我想要完成的任务:
public class Region : Behavior<ContentControl>
{
public static readonly DependencyProperty DefaultViewTypeProperty = DependencyProperty.Register(
"DefaultViewType",
typeof(Type),
typeof(Region),
new PropertyMetadata(default(UserControl)));
public Type DefaultViewType
{
get { return (Type)GetValue(DefaultViewTypeProperty); }
set { SetValue(DefaultViewTypeProperty, value); }
}
public static readonly DependencyProperty RegionNameProperty = DependencyProperty.RegisterAttached(
"RegionName",
typeof(string),
typeof(Region),
new PropertyMetadata(default(string)));
public string RegionName
{
get { return (string)GetValue(RegionNameProperty); }
set { SetValue(RegionNameProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
ShowDefaultView();
RegisterRegionWithManager();
}
private void ShowDefaultView()
{
if (DefaultViewType == null)
return;
if (DesignerProperties.GetIsInDesignMode(AssociatedObject))
{
var constructorInfo = DefaultViewType.GetConstructors().First();
var parameters = new List<object>();
constructorInfo.GetParameters().ToList().ForEach(p => parameters.Add(null));
AssociatedObject.Content = constructorInfo.Invoke(parameters.ToArray());
}
else
{
var serviceLocator = ServiceLocator.Current;
AssociatedObject.Content = serviceLocator.GetInstance(DefaultViewType);
}
}
private void RegisterRegionWithManager()
{
if (DesignerProperties.GetIsInDesignMode(AssociatedObject))
return;
var serviceLocator = ServiceLocator.Current;
var navigationService = serviceLocator.GetInstance<IRegionManager>();
if (navigationService != null)
navigationService.RegisterRegion(RegionName, AssociatedObject);
}
}