在自定义Region / RegionManager中设计UserControls的时间

时间:2014-05-17 12:16:50

标签: c# wpf xaml

我非常喜欢MS Prism框架所做的导航实现,但我不需要所有功能,而且我一直遇到视图和视图模型的瞬态生命周期管理问题。所以,我浏览了代码并重新实现了我自己的Region / Region Manager,增加了一些额外的功能。

到目前为止,一切都很顺利,但是我希望有一些设计时间的支持,但是没有关于如何实现的线索。我添加了一个DefaultViewType附加属性。我很乐意能够在设计师中呈现这种观点。

另一个奇怪的是Visual Studio(或Resharper)在设计器中报告了以下问题,据我所知,这是不真实的:

'RegionName'属性已由'ContentControl'注册。

以下是代码(也可在https://github.com/agartee/StackOverFlowRegionManagerQuestion.git获得):

My RegionManager是一种订阅类型为NavigationRequestMessage的MVVM Light消息的服务:

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));
    }
}

1 个答案:

答案 0 :(得分:1)

我能够通过以下更改完成我想要完成的任务:

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);
    }
}