自定义区域/区域管理器中用户控件的设计时渲染

本文关键字:区域 控件 管理器 用户 自定义 | 更新日期: 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);
    }
}