WPF自定义导航

1.导航接口

/// <summary>
    /// 自定义导航服务接口
    /// </summary>
    public interface ICustomNavigationService
    {
        /// <summary>
        /// 记录不同页面容器的当前页面键集合
        /// </summary>
        List<(string pageHostName, ApplicationPage page)> CurrentPageKeyList
        {
            get;
        }

        /// <summary>
        /// 回退到上一页
        /// </summary>
        void GoBack(string pageHostName);

        /// <summary>
        /// 导航到指定键页面
        /// </summary>
        /// <param name="pageHostName">容器名称</param>
        /// <param name="pageKey">页面ApplicationPage类型键</param>
        void NavigateTo(string pageHostName, ApplicationPage pageKey);

        /// <summary>
        /// 导航到指定键页面并且传递参数
        /// </summary>
        /// <param name="pageHostName">容器名称</param>
        /// <param name="pageKey">页面ApplicationPage类型键</param>
        /// <param name="parameter">页面间传递的参数:一般情况下,请直接在IOC容器中获取ViewModel并且传递值</param>
        void NavigateTo(string pageHostName, ApplicationPage pageKey, object parameter);
    }

2.实现

public class NavigationService : ICustomNavigationService
    {
        /// <summary>
        /// 锁对象
        /// </summary>
        private object _navLock = new object();

        /// <summary>
        /// 缓存页面键值对
        /// </summary>
        private Dictionary<ApplicationPage, BasePage> _navigationDic;

        /// <summary>
        /// 记录历史页面字典栈
        /// </summary>
        private Dictionary<string, Stack<ApplicationPage>> _historyStack;

        /// <summary>
        /// 记录不同页面容器的当前页面键集合
        /// </summary>
        public List<(string pageHostName, ApplicationPage page)> CurrentPageKeyList { get; set; }

        /// <summary>
        /// 构造器
        /// </summary>
        public NavigationService()
        {
            _navigationDic = new Dictionary<ApplicationPage, BasePage>();
            _historyStack=new Dictionary<string, Stack<ApplicationPage>>();
            CurrentPageKeyList = new List<(string, ApplicationPage)>();
        }

        /// <summary>
        /// 回退到上一页
        /// </summary>
        public void GoBack(string pageHostName)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 导航到指定键页面
        /// </summary>
        /// <param name="pageHostName">容器名称</param>
        /// <param name="pageKey">页面ApplicationPage类型键</param>
        public void NavigateTo(string pageHostName, ApplicationPage pageKey)
        {
            NavigateTo(pageHostName, pageKey, null);
        }

        /// <summary>
        /// 导航到指定键页面并且传递参数
        /// </summary>
        /// <param name="pageHostName">容器名称</param>
        /// <param name="pageKey">页面ApplicationPage类型键</param>
        /// <param name="parameter">页面间传递的参数:一般情况下,请直接在IOC容器中获取ViewModel并且传递值</param>
        public void NavigateTo(string pageHostName, ApplicationPage pageKey, object parameter)
        {
            lock (_navLock)
            {
                //导航页末调用Configure方法配置
                if (!_navigationDic.ContainsKey(pageKey))
                {
                    throw new ArgumentException("This key is not configured!");
                }

                //找到页面容器
                List<PageHost> pageHosts = new List<PageHost>();
                Application.Current.MainWindow.FindAllChilds<PageHost>(ref pageHosts);

                var pageHost = pageHosts.FirstOrDefault(t => t.Name == pageHostName);

                if (pageHost == null)
                {
                    throw new Exception("PageHost is not Configured!");
                }

                //当前页列表未记录当前键,初始化当前键与历史列表
                if (!CurrentPageKeyList.Any(t => t.pageHostName == pageHostName))
                {
                    CurrentPageKeyList.Add((pageHostName, pageKey));

                    _historyStack.Add(pageHostName, new Stack<ApplicationPage>());
                }
                else
                {
                    //将当前键加入历史列表栈
                    _historyStack[pageHostName].Push(CurrentPageKeyList.First(t => t.pageHostName == pageHostName).page);
                    CurrentPageKeyList.Remove(CurrentPageKeyList.First(t => t.pageHostName == pageHostName));
                    CurrentPageKeyList.Add((pageHostName, pageKey));
                }

                //获取页面框架
                var newPageFrame = pageHost.newPage;
                var oldPageFrame = pageHost.oldPage;

                //获取xaml传入的当前viewmodel
                var currentPageViewModel = pageHost.CurrentPageViewModel;

                //在页未改变情况下只更新ViewModel
                if (newPageFrame.Content is BasePage page && page == _navigationDic[pageKey])
                     page.ViewModelObject= currentPageViewModel;

                var oldPageContent = newPageFrame.Content;
                newPageFrame.Content = null;
                oldPageFrame.Content = oldPageContent;

                //动画效果
                if (oldPageContent is BasePage oldPage)
                {
                    oldPage.ShouldAnimateOut = true;

                    Task.Delay((int)(oldPage.SlideSeconds * 1000)).ContinueWith((t) =>
                    {
                        Application.Current.Dispatcher.Invoke(() => oldPageFrame.Content = null);
                    });
                }

                //切换页面
                BasePage showPage = _navigationDic[pageKey];
                showPage.ViewModelObject = currentPageViewModel;
                showPage.ExtraData = parameter;
                newPageFrame.Content = showPage;
            }
        }

        /// <summary>
        /// 配置页面键值对
        /// </summary>
        /// <param name="pageKey">页面ApplicationPage类型键</param>
        /// <param name="basePage">页面</param>
        /// <exception cref="ArgumentException">键或者值被引用过抛出异常</exception>
        public void Configure(ApplicationPage pageKey,BasePage basePage)
        {
            lock (_navLock)
            {
                if (_navigationDic.ContainsKey(pageKey))
                {
                    throw new ArgumentException("This key is already used:"+pageKey);
                }

                if (_navigationDic.Values.Any(t => t == basePage))
                {
                    throw new ArgumentException("This basepage is already configured with key "+ _navigationDic.First(t => t.Value == basePage).Key);
                }

                _navigationDic.Add(pageKey,basePage);
            }
        }
    }

3.使用mvvmlight自定义viewmodel

public class BaseViewModel:ViewModelBase
    {
        protected object mPropertyValueCheckLock=new object();

        protected async Task RunCommandAsync(Expression<Func<bool>> flagUpdating,Func<Task> action)
        {
            lock (mPropertyValueCheckLock)
            {
                if (flagUpdating.GetPropertyValue())
                {
                    return;
                }

                flagUpdating.SetPropertyValue(true);
            }

            try
            {
                await action();
            }
            finally
            {
                flagUpdating.SetPropertyValue(false);
            }
        }

        protected async Task<T> RunCommandAsync<T>(Expression<Func<bool>> flagUpdating, Func<Task<T>> action,T defaultValue)
        {
            lock (mPropertyValueCheckLock)
            {
                if (flagUpdating.GetPropertyValue())
                {
                    return defaultValue;
                }

                flagUpdating.SetPropertyValue(true);
            }

            try
            {
                return await action();
            }
            finally
            {
                flagUpdating.SetPropertyValue(false);
            }
        }
    }
public static class DependencyObjectHelpers
    {
        public static void FindAllChilds(this DependencyObject d, ref List<DependencyObject> dependencyObjects)
        {
            var count = VisualTreeHelper.GetChildrenCount(d);

            for (int i = 0; i < count; i++)
            {
                DependencyObject dependencyObject = VisualTreeHelper.GetChild(d, i);
                dependencyObjects.Add(dependencyObject);

                FindAllChilds(dependencyObject, ref dependencyObjects);
            }
        }

        public static void FindAllChilds<T>(this DependencyObject d, ref List<T> dependencyObjects) where T:class
        {
            var count = VisualTreeHelper.GetChildrenCount(d);

            for (int i = 0; i < count; i++)
            {
                DependencyObject dependencyObject = VisualTreeHelper.GetChild(d, i);

                if (dependencyObject.GetType() == typeof(T))
                {
                    dependencyObjects.Add(dependencyObject as T);
                }

                FindAllChilds(dependencyObject, ref dependencyObjects);
            }
        }
    }
public enum ApplicationPage
    {
        None=0
    }
`public partial class PageHost : UserControl
    {
        public ApplicationPage CurrentPage
        {
            get { return (ApplicationPage)GetValue(CurrentPageProperty); }
            set { SetValue(CurrentPageProperty, value); }
        }

        public static readonly DependencyProperty CurrentPageProperty =
            DependencyProperty.Register("CurrentPage", typeof(ApplicationPage), typeof(PageHost), new PropertyMetadata(default(ApplicationPage),null, CurrentPagePropertyChanged));

        public BaseViewModel CurrentPageViewModel
        {
            get { return (BaseViewModel)GetValue(CurrentPageViewModelProperty); }
            set { SetValue(CurrentPageViewModelProperty, value); }
        }

        public static readonly DependencyProperty CurrentPageViewModelProperty =
            DependencyProperty.Register("CurrentPageViewModel", typeof(BaseViewModel), typeof(PageHost), new PropertyMetadata(0));

        public PageHost()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                newPage.Content = null;
        }

        private static object CurrentPagePropertyChanged(DependencyObject d, object value)
        {
            //ioc navigation

            return value;
        }
    }
```csharp
public class BasePage : UserControl
    {
        #region Private Member

        /// <summary>
        /// The View Model associated with this page
        /// </summary>
        private object mViewModel;

        #endregion

        #region Public Properties

        /// <summary>
        /// The animation the play when the page is first loaded
        /// </summary>
        public PageAnimation PageLoadAnimation { get; set; } = PageAnimation.SlideAndFadeInFromRight;

        /// <summary>
        /// The animation the play when the page is unloaded
        /// </summary>
        public PageAnimation PageUnloadAnimation { get; set; } = PageAnimation.SlideAndFadeOutToLeft;

        /// <summary>
        /// The time any slide animation takes to complete
        /// </summary>
        public float SlideSeconds { get; set; } = 0.4f;

        /// <summary>
        /// A flag to indicate if this page should animate out on load.
        /// Useful for when we are moving the page to another frame
        /// </summary>
        public bool ShouldAnimateOut { get; set; }

        /// <summary>
        /// The View Model associated with this page
        /// </summary>
        public object ViewModelObject
        {
            get => mViewModel;
            set
            {
                // If nothing has changed, return
                if (mViewModel == value)
                    return;

                // Update the value
                mViewModel = value;

                // Fire the view model changed method
                OnViewModelChanged();

                // Set the data context for this page
                DataContext = mViewModel;
            }
        }

        public object ExtraData { get; set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Default constructor
        /// </summary>
        public BasePage()
        {
            // Don't bother animating in design time
            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            // If we are animating in, hide to begin with
            if (PageLoadAnimation != PageAnimation.None)
                Visibility = Visibility.Collapsed;

            // Listen out for the page loading
            Loaded += BasePage_LoadedAsync;
        }

        #endregion

        #region Animation Load / Unload

        /// <summary>
        /// Once the page is loaded, perform any required animation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void BasePage_LoadedAsync(object sender, System.Windows.RoutedEventArgs e)
        {
            // If we are setup to animate out on load
            if (ShouldAnimateOut)
                // Animate out the page
                await AnimateOutAsync();
            // Otherwise...
            else
                // Animate the page in
                await AnimateInAsync();
        }

        /// <summary>
        /// Animates the page in
        /// </summary>
        /// <returns></returns>
        public async Task AnimateInAsync()
        {
            // Make sure we have something to do
            if (PageLoadAnimation == PageAnimation.None)
                return;

            switch (PageLoadAnimation)
            {
                case PageAnimation.SlideAndFadeInFromRight:

                    // Start the animation
                    await this.SlideAndFadeInAsync(AnimationSlideInDirection.Right, false, SlideSeconds, size: (int)Application.Current.MainWindow.Width);

                    break;
            }
        }

        /// <summary>
        /// Animates the page out
        /// </summary>
        /// <returns></returns>
        public async Task AnimateOutAsync()
        {
            // Make sure we have something to do
            if (PageUnloadAnimation == PageAnimation.None)
                return;

            switch (PageUnloadAnimation)
            {
                case PageAnimation.SlideAndFadeOutToLeft:

                    // Start the animation
                    await this.SlideAndFadeOutAsync(AnimationSlideInDirection.Left, SlideSeconds);

                    break;
            }
        }

        #endregion

        /// <summary>
        /// Fired when the view model changes
        /// </summary>
        protected virtual void OnViewModelChanged()
        {

        }
    }

    /// <summary>
    /// A base page with added ViewModel support
    /// </summary>
    public class BasePage<VM> : BasePage
        where VM : BaseViewModel, new()
    {
        #region Public Properties

        /// <summary>
        /// The view model associated with this page
        /// </summary>
        public VM ViewModel
        {
            get => (VM)ViewModelObject;
            set => ViewModelObject = value;
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Default constructor
        /// </summary>
        public BasePage() : base()
        {
            // If in design time mode...
            if (DesignerProperties.GetIsInDesignMode(this))
                // Just use a new instance of the VM
                ViewModel = new VM();
            else
                // Create a default view model
                ViewModel = SimpleIoc.Default.GetInstance<VM>() ?? new VM();
        }

        /// <summary>
        /// Constructor with specific view model
        /// </summary>
        /// <param name="specificViewModel">The specific view model to use, if any</param>
        public BasePage(VM specificViewModel = null) : base()
        {
            // Set specific view model
            if (specificViewModel != null)
                ViewModel = specificViewModel;
            else
            {
                // If in design time mode...
                if (DesignerProperties.GetIsInDesignMode(this))
                    // Just use a new instance of the VM
                    ViewModel = new VM();
                else
                {
                    // Create a default view model
                    ViewModel = SimpleIoc.Default.GetInstance<VM>() ?? new VM();
                }
            }
        }

        #endregion
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: WPF是一种用于创建Windows桌面应用程序的框架,它提供了许多控件和功能来创建交互式界面。如果我们需要在WPF应用程序中自定义侧边导航,可以按照以下步骤进行操作: 1. 创建侧边导航的布局:可以使用WPF中的Grid或StackPanel控件来创建一个垂直的侧边导航栏,其中包含导航项的按钮或列表。 2. 设置导航项的样式:可以自定义按钮的外观,例如背景颜色、字体颜色和大小,以及按钮的左侧图标。使用WPF的Style和Trigger来设置按钮的不同状态下的外观效果,例如鼠标悬停或点击时的效果。 3. 绑定导航项到页面:在创建导航项的按钮或列表时,可以使用WPF的Command属性将导航命令与按钮或列表项的点击事件关联起来。这样,在用户点击导航项时,将触发相应的命令并导航到对应的页面。 4. 创建页面内容:根据导航项的点击事件,我们可以在主窗口的内容区域加载不同的页面内容。可以使用WPF中的Frame控件来承载页面内容,并使用不同的页面来填充Frame控件,以实现导航功能。 5. 设计页面导航逻辑:可以在应用程序中设计导航逻辑,例如在导航到不同页面时进行权限验证、显示页面切换动画等。使用WPF的Page和Frame控件配合使用,可以实现页面之间的无缝切换和导航。 通过以上步骤,我们可以在WPF应用程序中自定义一个侧边导航,使用户能够方便地切换不同的页面内容。这样的自定义侧边导航可以提升用户体验,并增加应用程序的交互性。 ### 回答2: WPF是一种用于创建Windows桌面应用程序的技术,它允许开发者使用XAML语言和C#或VB.NET代码来构建用户界面。在WPF中,可以自定义侧边导航以提供更好的用户体验。 要创建自定义的侧边导航,首先需要在WPF窗口中添加一个导航栏容器,可以使用StackPanel或WrapPanel等控件。然后,可以在导航栏容器中添加按钮或菜单项,用于作为导航的选项。 接下来,为每个导航选项创建一个页面。页面可以通过使用WPF的Page或UserControl来创建。可以为页面添加标识符或属性来表示其对应的导航选项。 然后,在侧边导航的按钮或菜单项的点击事件中,使用导航框架(如Frame或NavigationWindow)加载对应的页面。导航框架将负责显示和管理页面的导航。 为了提供更好的用户体验,可以为选中的导航选项添加高亮效果,以便用户知道当前所在的导航选项。可以使用样式或触发器来实现这一效果。 此外,还可以根据需要添加额外的功能,如在导航选项上添加图标或说明文本,或者使用动画效果来改善导航的过渡效果。 总结起来,自定义WPF侧边导航需要在WPF窗口中添加导航栏容器,创建导航选项和相应的页面,使用导航框架进行页面的导航,为选中的导航选项添加高亮效果,并根据需要添加其他功能来提升用户体验。通过这些步骤,可以实现一个自定义的侧边导航,为应用程序提供直观和简洁的导航界面。 ### 回答3: WPF是Windows Presentation Foundation的缩写,是一种用于创建Windows桌面应用程序的.NET框架。WPF提供了丰富的可视化元素和灵活的布局系统,使开发者能够构建功能强大、交互性强的应用程序。 自定义侧边导航WPF中常见的需求之一,可以通过以下步骤实现: 首先,创建一个主窗口容器,可以使用Grid或其他适当的布局容器。在主窗口的侧边位置放置一个StackPanel作为导航栏容器。 然后,通过创建自定义的UserControl来表示每个导航条目。在这个UserControl中,可以添加一个Button来表示条目的按钮,并将其样式设置为适当的样式。还可以添加一个TextBlock或其他适当的元素来显示条目的标题。 在主窗口容器的侧边导航栏StackPanel中,使用多个自定义导航条目UserControl来表示不同的导航选项。可以通过设置每个导航条目UserControl的属性来设置其显示的标题和按钮样式。 在导航条目的按钮的Click事件中,可以在主窗口中的内容区域切换不同的页面。可以使用Frame或其他适当的控件作为内容区域容器,并将其导航源设置为所需的页面。 最后,根据需要可以添加一些动画效果或交互功能来增强侧边导航的用户体验。例如,可以在导航条目被选中时,修改按钮样式或显示选中状态的指示。 总之,通过创建自定义的UserControl来表示每个导航条目,并在主窗口中的内容区域动态切换页面,可以实现WPF中的自定义侧边导航。这样用户就可以通过点击导航栏中的按钮来方便地浏览和切换不同的页面内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值