【WP8】扩展CM的WindowManager

关于WindowManager,一直都很想写一篇博客分享一下,一直在忙别的,今天抽空把这个分享一下

在弹窗在移动开发是一个很常见的交互,很多时候我们都需要进行弹窗,比如我们需要询问用户的一些操作,提供更丰富的交互,又比如我们需要弹出一个框提示用户给我们好评

WP系统提供关于对话框的操作只有一个MessageBox,MessageBox样式简单,并且无法扩展,比如我们需要自定义按钮的文字都不可以,极大限制了我们的发挥,如果我们需要弹窗,还有Popup控件可以用,比如Coding4fun就对Popup进行了扩展,提供了更丰富对话框样式

用CM(Caluburn.Micro)也有一段时间了,CM不仅提供了MVVM的支持,还提供了IoC依赖注入容器,还提供了WindowManager的支持(这是一个非常值得深入研究的开源框架)可以在页面的ViewModel中直接通过控件对应的ViewModel构造控件进行显示,下面我们对控件进行

一、页面框架层次

首先说明一下页面框架的层次关系:

  Frame:最底层

  Page:页面在Frame的上层

  Popup:在页面的上层,弹出的控件会在覆盖在页面的上面

  ApplicationBar:应用程序栏不会被Popup覆盖,所以自定义的弹窗是不能把ApplicationBar给遮住的

  键盘和MessageBox:键盘在系统弹窗会把上面所有的元素都覆盖,如果ApplicationBar存在的时候,键盘会在ApplicationBar上面

二、WindowManager职责

  WindowManager通过封装Popup,给为外部提供很方便的弹窗操作,下面列举一下WindowManager关于弹窗所做的操作(基于CM)

    1、新建一个Host:当我们显示弹窗之前,我们需要一个容器(ContentControl)用于内部管理

    2、通过ViewModel找到对应的View(控件),并且将ViewModel绑定到View上(CM)

    3、Host.Content = view

    4、ApplySetting:通过反射设置信息,CM默认的WindowManager提供了对Host的设置

    5、判断ViewModel是否实现IActivate和IDeactivate事件,如果有,则绑定事件(在弹窗打开和关闭的时候触发)

    6、接管BackKey:比如用户按返回键是否需要关闭弹窗

    7、接管ApplicationBar:由于Popup在ApplicationBar下层,无法遮住ApplicationBar,一般的做法是隐藏掉ApplicationBar,或者是禁用掉ApplicationBar上的按钮和MenuItem,在弹窗关闭后,恢复ApplicationBar原有的状态

    8、接管Page.OrientationChanged事件:一般我们不处理该事件

    9、Popup.IsOpen = true;  打开弹窗(打开之前一般设置其Opacity为0,防止闪一下)

    10、开启弹窗动画(CM默认没有提供动画的支持,后面我们自定义的时候加入该支持)

定义WindowManager主要就是上面一些操作,这里不分析CM中的WindowManager的源码了,有兴趣的可以去看,但是CM提供的WindowManager还是不够用,我们需要可以更多自定义的一些配置,比如:

  1、弹窗的时候是否隐藏ApplicationBar

  2、弹窗的时候点击其他区域是否关闭弹窗

  3、弹窗是否需要用一个遮罩遮住原来页面

  4、弹窗是否支持动画注入(支持扩展)

  5、弹窗是否可以被返回键关闭

三、定义与实现

  原本是想直接继承WindowManager来做的,但是发现WindowManager提供的属性和方法太少了,很难扩展,所以下面通过自定义的方式扩展

  定义接口

    public interface ICustomWindowManager : IWindowManager
    {
        void ShowDialog(object rootModel, bool isTapClose = true, double maskOpacity = 0.8, IWindowAnimator windowAnimator = null, bool isHideApplicationBar = true, bool canClose = true);
    }

  实现:

    动画接口

    /// <summary>
    /// WindowManager动画的接口(用于扩展动画)
    /// </summary>
    public interface IWindowAnimator
    {
        void Enter(FrameworkElement viewContainer, FrameworkElement host);
        void Exit(FrameworkElement viewContainer, FrameworkElement host, Action complete);
    }

    WindowAnimator动画默认实现

    /// <summary>
    /// 默认弹窗动画的实现(渐入和渐出)
    /// </summary>
    public class DefaultWindowAnimator : IWindowAnimator
    {
        public void Enter(FrameworkElement viewContainer, FrameworkElement host)
        {
            var storyboard = new Storyboard();
            var doubleAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromSeconds(0.1)),
                From = 0,
                To = 1
            };
            Storyboard.SetTarget(doubleAnimation, host);
            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity", new object[0]));
            storyboard.Children.Add(doubleAnimation);
            storyboard.Begin();
        }

        public void Exit(FrameworkElement viewContainer, FrameworkElement host, Action complete)
        {
            var storyboard = new Storyboard();
            var doubleAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromSeconds(0.1)),
                From = 1,
                To = 0
            };
            Storyboard.SetTarget(doubleAnimation, host);
            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity", new object[0]));
            storyboard.Children.Add(doubleAnimation);
            storyboard.Completed += (sender, e) => complete.Invoke();
            storyboard.Begin();
        }
    }

    下面提供其他动画的实现

    /// <summary>
    /// 翻转动画
    /// </summary>
    public class FlipWindowAnimator : IWindowAnimator
    {
        private const double DURATION_SECONDS = 0.15;

        public void Enter(FrameworkElement viewContainer, FrameworkElement host)
        {
            viewContainer.Projection = new PlaneProjection();

            var storyboard = new Storyboard();
            var doubleAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromSeconds(DURATION_SECONDS)),
                From = 90,
                To = 0
            };

            Storyboard.SetTarget(doubleAnimation, viewContainer.Projection);
            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("RotationX", new object[0]));
            storyboard.Children.Add(doubleAnimation);

            doubleAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromSeconds(DURATION_SECONDS)),
                From = 0,
                To = 1
            };
            Storyboard.SetTarget(doubleAnimation, host);
            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity", new object[0]));
            storyboard.Children.Add(doubleAnimation);
            storyboard.Begin();
        }

        public void Exit(FrameworkElement viewContainer, FrameworkElement host, Action complete)
        {
            var storyboard = new Storyboard();
            var doubleAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromSeconds(DURATION_SECONDS)),
                From = 0,
                To = 90
            };

            Storyboard.SetTarget(doubleAnimation, viewContainer.Projection);
            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("RotationX", new object[0]));
            storyboard.Children.Add(doubleAnimation);

            doubleAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromSeconds(DURATION_SECONDS)),
                From = 1,
                To = 0
            };
            Storyboard.SetTarget(doubleAnimation, host);
            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity", new object[0]));
            storyboard.Children.Add(doubleAnimation);

            storyboard.Completed += (sender, e) => complete.Invoke();
            storyboard.Begin();
        }
    }

翻转动画:FlipWindowAnimator

    滑动动画参考自WPToolkit

    /// <summary>
    /// 上下滑动动画
    /// </summary>
    public class SlideUpWindowAnimator : IWindowAnimator
    {
        /// <summary>
        /// 向上显示动画
        /// </summary>
        private const string SLIDE_UP_STORYBOARD = @"
        <Storyboard  xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""150""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.35"" Value=""0"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseOut"" Exponent=""6""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimation Storyboard.TargetProperty=""(UIElement.Opacity)"" From=""0"" To=""1"" Duration=""0:0:0.350"">
                <DoubleAnimation.EasingFunction>
                    <ExponentialEase EasingMode=""EaseOut"" Exponent=""6""/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>";

        /// <summary>
        /// 向下消失动画
        /// </summary>
        private const string SLIDE_DOWN_STORYBOARD = @"
        <Storyboard  xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.TranslateY)"">
                <EasingDoubleKeyFrame KeyTime=""0"" Value=""0""/>
                <EasingDoubleKeyFrame KeyTime=""0:0:0.25"" Value=""150"">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ExponentialEase EasingMode=""EaseIn"" Exponent=""6""/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimation Storyboard.TargetProperty=""(UIElement.Opacity)"" From=""1"" To=""0"" Duration=""0:0:0.25"">
                <DoubleAnimation.EasingFunction>
                    <ExponentialEase EasingMode=""EaseIn"" Exponent=""6""/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>";

        public void Enter(FrameworkElement viewContainer, FrameworkElement host)
        {
            var storyboard = XamlReader.Load(SLIDE_UP_STORYBOARD) as Storyboard;

            if (storyboard != null)
            {
                foreach (var t in storyboard.Children)
                {
                    Storyboard.SetTarget(t, host);
                }

                storyboard.Begin();
            }
        }

        public void Exit(FrameworkElement viewContainer, FrameworkElement host, Action complete)
        {
            var storyboard = XamlReader.Load(SLIDE_DOWN_STORYBOARD) as Storyboard;

            if (storyboard != null)
            {
                foreach (var t in storyboard.Children)
                {
                    Storyboard.SetTarget(t, host);
                }

                storyboard.Completed += (sender, e) => complete.Invoke();
                storyboard.Begin();
            }
        }
    }

上下滑动动画:SlideUpWindowAnimator

  WindowManager实现:大部分参考自原来的WindowManager实现

    /// <summary>
    /// 自定义窗口管理器(基于Caliburn.Micro)
    /// </summary>
    public class CustomWindowManager : ICustomWindowManager
    {
        public static Func<Uri, bool> IsSystemDialogNavigation = uri => uri != null && uri.ToString().StartsWith("/Microsoft.Phone.Controls.Toolkit");

        public virtual void ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null)
        {
            var navigationSvc = IoC.Get<INavigationService>();

            var host = new DialogHost(navigationSvc);
            var view = ViewLocator.LocateForModel(rootModel, host, context);
            host.Content = view as FrameworkElement;
            host.SetValue(View.IsGeneratedProperty, true);

            ViewModelBinder.Bind(rootModel, host, null);
            host.SetActionTarget(rootModel);

            ApplySettings(host, settings);

            var activatable = rootModel as IActivate;
            if (activatable != null)
            {
                activatable.Activate();
            }

            var deactivator = rootModel as IDeactivate;
            if (deactivator != null)
            {
                host.Closed += delegate { deactivator.Deactivate(true); };
            }

            host.Open();
        }

        public virtual void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null)
        {
            var popup = CreatePopup(rootModel, settings);
            var view = ViewLocator.LocateForModel(rootModel, popup, context);

            popup.Child = view;
            popup.SetValue(View.IsGeneratedProperty, true);

            ViewModelBinder.Bind(rootModel, popup, null);

            var activatable = rootModel as IActivate;
            if (activatable != null)
            {
                activatable.Activate();
            }

            var deactivator = rootModel as IDeactivate;
            if (deactivator != null)
            {
                popup.Closed += delegate { deactivator.Deactivate(true); };
            }

            popup.IsOpen = true;
        }

        public void ShowDialog(object rootModel, bool isTapClose = true, double maskOpacity = 0.5,
            IWindowAnimator windowAnimator = null, bool isHideApplicationBar = true, bool canClose = true)
        {
            var navigationSvc = IoC.Get<INavigationService>();

            var host = new DialogHost(navigationSvc, isTapClose, maskOpacity, isHideApplicationBar, windowAnimator, canClose);
            var view = ViewLocator.LocateForModel(rootModel, host, null);
            host.Content = view as FrameworkElement;
            host.SetValue(View.IsGeneratedProperty, true);

            ViewModelBinder.Bind(rootModel, host, null);
            host.SetActionTarget(rootModel);

            var activatable = rootModel as IActivate;
            if (activatable != null)
            {
                activatable.Activate();
            }

            var deactivator = rootModel as IDeactivate;
            if (deactivator != null)
            {
                host.Closed += delegate { deactivator.Deactivate(true); };
            }

            host.Open();
        }

        protected virtual Popup CreatePopup(object rootModel, IDictionary<string, object> settings)
        {
            var popup = new Popup();
            ApplySettings(popup, settings);
            return popup;
        }

        private static void ApplySettings(object target, IEnumerable<KeyValuePair<string, object>> settings)
        {
            if (settings != null)
            {
                var type = target.GetType();

                foreach (var pair in settings)
                {
                    var propertyInfo = type.GetProperty(pair.Key);

                    if (propertyInfo != null)
                        propertyInfo.SetValue(target, pair.Value, null);
                }
            }
        }

        [ContentProperty("Content")]
        public class DialogHost : FrameworkElement
        {
            readonly INavigationService navigationSvc;
            PhoneApplicationPage currentPage;

            Popup hostPopup;
            bool isOpen;
            ContentControl viewContainer;
            Border pageFreezingLayer;
            Border maskingLayer;
            private FrameworkElement host;
            private readonly IWindowAnimator animator;

            private readonly double maskOpacity;
            private readonly bool isTapClose;
            private readonly bool canClose;

            private readonly bool isHideApplicationBar;

            private readonly Dictionary<IApplicationBarIconButton, bool> appBarButtonsStatus =
                new Dictionary<IApplicationBarIconButton, bool>();
            bool appBarMenuEnabled;

            public DialogHost(INavigationService navigationSvc, bool isTapClose = true, double maskOpacity = 0.5, bool isHideApplicationBar = true, IWindowAnimator animator = null, bool canClose = true)
            {
                this.navigationSvc = navigationSvc;
                this.canClose = canClose;
                currentPage = navigationSvc.CurrentContent as PhoneApplicationPage;
                if (currentPage == null)
                {
                    throw new InvalidOperationException(
                        string.Format("In order to use ShowDialog the view currently loaded in the application frame ({0})"
                                      + " should inherit from PhoneApplicationPage or one of its descendents.", navigationSvc.CurrentContent.GetType()));
                }

                navigationSvc.Navigating += OnNavigating;
                navigationSvc.Navigated += OnNavigated;

                this.maskOpacity = maskOpacity;
                this.isTapClose = isTapClose;
                this.isHideApplicationBar = isHideApplicationBar;
                CreateUiElements();

                this.animator = animator ?? new DefaultWindowAnimator();
            }

            public EventHandler Closed = delegate { };

            public void SetActionTarget(object target)
            {
                Caliburn.Micro.Action.SetTarget(viewContainer, target);
            }

            public virtual FrameworkElement Content
            {
                get { return (FrameworkElement)viewContainer.Content; }
                set { viewContainer.Content = value; }
            }

            public void Open()
            {
                if (isOpen)
                {
                    return;
                }

                isOpen = true;

                if (currentPage.ApplicationBar != null)
                {
                    DisableAppBar();

                }

                ArrangePlacement();

                currentPage.BackKeyPress += CurrentPageBackKeyPress;
                currentPage.OrientationChanged += CurrentPageOrientationChanged;

                hostPopup.IsOpen = true;
            }

            public void Close()
            {
                Close(reopenOnBackNavigation: false);
            }

            void Close(bool reopenOnBackNavigation)
            {
                if (!isOpen)
                {
                    return;
                }

                isOpen = false;
                animator.Exit(Content, host, () => { hostPopup.IsOpen = false; });

                if (currentPage.ApplicationBar != null)
                {
                    RestoreAppBar();
                }

                currentPage.BackKeyPress -= CurrentPageBackKeyPress;
                currentPage.OrientationChanged -= CurrentPageOrientationChanged;

                if (!reopenOnBackNavigation)
                {
                    navigationSvc.Navigating -= OnNavigating;
                    navigationSvc.Navigated -= OnNavigated;

                    Closed(this, EventArgs.Empty);
                }
            }

            protected IWindowAnimator CreateElementsAnimator()
            {
                return new DefaultWindowAnimator();
            }

            protected void CreateUiElements()
            {
                var alpha = Convert.ToByte(maskOpacity * 255);

                viewContainer = new ContentControl
                {
                    HorizontalContentAlignment = HorizontalAlignment.Stretch,
                    VerticalContentAlignment = VerticalAlignment.Stretch,
                };
                maskingLayer = new Border
                {
                    Child = viewContainer,
                    Background = new SolidColorBrush(Color.FromArgb(alpha, 0, 0, 0)),
                    VerticalAlignment = VerticalAlignment.Stretch,
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    Width = Application.Current.Host.Content.ActualWidth,
                    Height = Application.Current.Host.Content.ActualHeight
                };

                if (isTapClose)
                {
                    maskingLayer.Tap += (s, e) =>
                    {
                        if (e.OriginalSource == maskingLayer)
                        {
                            Close();
                        }
                    };
                }

                pageFreezingLayer = new Border
                {
                    Background = new SolidColorBrush(Colors.Transparent),
                    Width = Application.Current.Host.Content.ActualWidth,
                    Height = Application.Current.Host.Content.ActualHeight
                };

                var panel = new Grid { RenderTransform = new CompositeTransform() };
                panel.Children.Add(pageFreezingLayer);
                panel.Children.Add(maskingLayer);

                host = panel;
                hostPopup = new Popup { Child = panel };
            }

            private bool applicationBarVisible;

            void DisableAppBar()
            {

                if (isHideApplicationBar && currentPage.ApplicationBar.IsVisible)
                {
                    applicationBarVisible = currentPage.ApplicationBar.IsVisible;
                    currentPage.ApplicationBar.IsVisible = false;
                }
                else
                {

                    appBarMenuEnabled = currentPage.ApplicationBar.IsMenuEnabled;
                    appBarButtonsStatus.Clear();
                    currentPage.ApplicationBar.Buttons.Cast<IApplicationBarIconButton>()
                        .Apply(b =>
                        {
                            appBarButtonsStatus.Add(b, b.IsEnabled);
                            b.IsEnabled = false;
                        });

                    currentPage.ApplicationBar.IsMenuEnabled = false;

                }
            }

            void RestoreAppBar()
            {
                if (isHideApplicationBar)
                {
                    if (applicationBarVisible)
                    {
                        currentPage.ApplicationBar.IsVisible = applicationBarVisible;
                    }
                }
                else
                {
                    if (!appBarMenuEnabled)
                    {
                        currentPage.ApplicationBar.IsMenuEnabled = appBarMenuEnabled;
                        currentPage.ApplicationBar.Buttons.Cast<IApplicationBarIconButton>()
                            .Apply(b =>
                            {
                                bool status;
                                if (appBarButtonsStatus.TryGetValue(b, out status))
                                    b.IsEnabled = status;
                            });
                    }
                }
            }

            void ArrangePlacement()
            {
                //设置Opacity为0防止闪屏
                host.Opacity = 0;
                maskingLayer.Dispatcher.BeginInvoke(() => animator.Enter(Content, host));
            }

            Uri currentPageUri;
            void OnNavigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
            {
                if (IsSystemDialogNavigation(e.Uri))
                {
                    currentPageUri = navigationSvc.CurrentSource;
                }
            }

            void OnNavigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
            {
                if (IsSystemDialogNavigation(e.Uri))
                {
                    Close(currentPageUri != null);
                }
                else if (e.Uri.Equals(currentPageUri))
                {
                    currentPageUri = null;
                    //refreshes the page instance
                    currentPage = (PhoneApplicationPage)navigationSvc.CurrentContent;

                    Open();
                }
                else
                {
                    Close(reopenOnBackNavigation: false);
                }
            }

            void CurrentPageBackKeyPress(object sender, CancelEventArgs e)
            {
                e.Cancel = true;
                if (canClose)
                {
                    Close();
                }
            }

            void CurrentPageOrientationChanged(object sender, OrientationChangedEventArgs e)
            {
                ArrangePlacement();
            }
        }

        //TODO:待改
        public void ShowDialog1(object rootModel, IWindowAnimator windowAnimator = null,
            bool isTapClose = true, double maskOpacity = 0.8,
            bool isHideApplicationBar = true, bool canClose = true)
        {
        }
    }

CustomWindowManager

四、使用

  使用很简单,首先我们定义一个自定义窗口

<UserControl x:Class="XTuOne.Friday.Controls.Views.CommonDialogView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             VerticalAlignment="Top"
             FontFamily="{StaticResource PhoneFontFamilyNormal}"
             FontSize="{StaticResource PhoneFontSizeNormal}"
             Foreground="{StaticResource PhoneForegroundBrush}"
             d:DesignHeight="480"
             d:DesignWidth="480"
             mc:Ignorable="d">

    <Grid Background="#1F1F1F">
        <StackPanel Margin="12 48 12 12">
            <TextBlock x:Name="Title"
                       Margin="{StaticResource PhoneHorizontalMargin}"
                       FontFamily="{StaticResource PhoneFontFamilySemiBold}"
                       FontSize="{StaticResource PhoneFontSizeLarge}"
                       Foreground="White"
                       TextWrapping="Wrap" />
            <TextBlock x:Name="Text"
                       Margin="12 24"
                       Foreground="White"
                       Style="{StaticResource PhoneTextTitle3Style}"
                       TextWrapping="Wrap" />
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <Button x:Name="Ok"
                        Grid.Column="0"
                        BorderBrush="White"
                        Foreground="White">
                    <TextBlock x:Name="OkText" Text="Ok" />
                </Button>
                <Button x:Name="Cancel"
                        Grid.Column="1"
                        BorderBrush="White"
                        Foreground="White">
                    <TextBlock x:Name="CancelText" Text="Cancel" />
                </Button>
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>

自定义弹窗控件:CommonDialogView

  后台没有内容,就不贴,然后定义控件对应的ViewModel

    public enum DialogResult
    {
        Cancel,
        Ok,
        Close,
    }

    public class CommonDialogViewModel : Screen
    {
        //返回值
        public DialogResult Result { get; private set; }

        //对话框的标题
        public string Title { get; set; }

        //对话框的文字
        public string Text { get; set; }

        //左边按钮的文字
        public string OkText { get; set; }

        //右边按钮的文字
        public string CancelText { get; set; }

        public CommonDialogViewModel()
        {
            Result = DialogResult.Close;
            OkText = "Ok";
            CancelText = "Cancel";
        }

        public void Ok()
        {
            Result = DialogResult.Ok;
            TryClose();
        }

        public void Cancel()
        {
            Result = DialogResult.Cancel;
            TryClose();
        }
    }

CommonDialogViewModel

  接下来是使用

    var windowAnimator = new FlipWindowAnimator();
    var customWindowManager = new CustomWindowManager();

    var commonDialog = new CommonDialogViewModel
    {
        Title = "提示",
        Text = "该手机号已经注册过,您可以:",
        CancelText = "换个手机",
        OkText = "直接登录"
    };

    commonDialog.Deactivated += (s, e) =>
    {
        if (commonDialog.Result == DialogResult.Ok)
        {
            //用户点击左边按钮

        }
        else if (commonDialog.Result == DialogResult.Cancel)
        {
            //用户点击右边按钮
        }
        else
        {
            //非用户点击按钮关闭(用户点击返回键或离开App)
        }
    };
    customWindowManager.ShowDialog(commonDialog, false, 0.8, flipWindowAnimator, false);

  效果图

 

注意:  

  为了防止多个弹窗导致HideApplicationBar冲突问题
  由于这里是通过ApplicationBar.IsMenuEnable来判断是否被禁用的
  所以请保证ApplicationBar.IsMenuEnable为true

个人能力有限,如果上文有误或者您有更好的实现,可以给我留言

转载请注明出处:http://www.cnblogs.com/bomo/p/3952419.html

时间: 2024-10-24 22:47:16

【WP8】扩展CM的WindowManager的相关文章

【WP8】扩展CM的INavigationService方法

CM支持通过ViewModel进行导航,并通过支持参数传递,但是内部只是通过反射的方式构造Uri的参数进行导航,所以只支持简单类型的参数传递,下面对其进行扩展,在页面导航时支持复杂类型的参数传递,并扩展了部分方法,比如,导航后删除上一个页面,清空导航,清空跳转等,详细的看代码 // ************************************************* // // 作者:bomo // 小组:WP开发组 // 创建日期:2014/5/22 23:03:26 // 版本

【WP8】自定义EventAggregator

MVVM模式实现了ViewModel和View的分离,但是有很多时候我们需要进行页面间通信 比如,我们在设置界面修改了某个项的值,需要同步到主页面,让主页面做相关的逻辑,由于每个页面对应一个ViewModel,ViewModel之间又是独立的,很多MVVM实现都提供了EventAggregator来实现ViewModel之间的通信,其原理就是订阅与广播 EventAggregator原理 1.消息订阅者向EventAggregator订阅消息 2.消息发布者向EventAggregator发布消

【WP8】ScrollViewer滑动到底触发器

很多时候会有到底加载更多的需求,而ScrollViewer不支持继承,无法继承它进行扩展,只能通过触发器来控制到底的事件(当然,可以通过UserControl去扩展) 思路:定义一个Trigger,自定义依赖属性,绑定到该属性到ScrollViewer的VerticalOffset属性上,然后监听属性的变化,就能监控到滚动事件了,然后判断滚动的位置从而判断出是否到底 原理很简单,下面看实现 // *************************************************

正则表达式与扩展正则表达式

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑.通常被用来检索.替换那些符合某个模式(规则)的文本.其主要应用对象是文本,因此它在各种文本编辑器场合都有应用. 贪婪模式与懒惰模式 正则表达式默认的情况下,会在满足匹配条件下尽可能的匹配更多内容.如 a.*b,用他来匹配 aabab ,它会匹配整个 aabab ,而不会只匹配到 aab 为止,这就是贪

进击的雨燕-------------扩展

详情转自:http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html 扩展就是向一个已有的类.结构体.枚举类型或者协议类型添加新功能(functionality).这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模).扩展和 Objective-C 中的分类(categories)类似.(不过与 Objective-C 不同的是,Swift 的扩展没有名字.) Swift 中的扩展可以: 添加计算型属性和计

五 Swift开发之扩展(Extension)

五 Swift开发之扩展(Extensions) //扩展就是向一个已有的类.结构体或枚举类型添加新功能(functionality).这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模).扩展和 Objective-C 中的分类(categories)类似.(不过与Objective-C不同的是,Swift 的扩展没有名字.) Swift 中的扩展可以: 1.添加计算型属性和计算静态属性 2.定义实例方法和类型方法 3.提供新的构造器 4.定义下标 5.定义和使用新的嵌套类型 6

WP8.1学习系列(第一章)——添加应用栏

做过android开发的同学们应该都知道有个ActionBar的头部操作栏,而wp也有类似的一个固定在app页面里通常拥有的内部属性,就是应用栏.以前叫做ApplicationBar,现在wp和win统一称AppBar,以后Win10一统手机和桌面相信Api将会高度统一. 废话不多说了,从wp8.1开始,系统提供了AppBar和CommandBar两种控件,CommandBar集成了很多功能,但是是系统指定的模板,如果要高度自定义(如显示进度条,搜索框等等)应用栏就需要使用AppBar了.其中A

System Center 2012 R2 CM系列之安装Configuration Manager预装软件

安装Configuration Manager 2012 R2 预装软件 本章节主要描述创建Configuration Manager 2012 R2所必须的system container.设计Configuration Manager 2012 R2服务器权限.扩展Active Directory架构以及安装安装Configuration Manager 2012 R2所必须的服务器角色和功能 1. 创建系统管理对象 1) 使用管理员账户登陆"BJ-DC-01" 2) 点击服务器管

6.Swift协议|扩展|访问权限|异常调试|类型转换|运算函数|ARC|类类型初试化器|值类型初始化器

1. 协议(Protocol):与OC之间唯一不同的是Swift中的协议不管是属性还时方法全部是必须实现的 /** protocol*/ protocol FullNamed { /** 计算属性申明,只读的计算属性*/ var fullName:String { get } } /** 实现协议*/ struct Person:FullNamed { /** 实现协议  可以把计算属性实现为存储属性,更改其本身的性质*/ var fullName: String = "abc" }