【WP8】扩展CM的WindowManager
14-09-09更新:修复AppBar冲突bug
关于WindowManager,一直都很想写一篇博客分享一下,一直在忙别的,今天抽空把这个分享一下
在弹窗在移动开发是一个很常见的交互,很多时候我们都需要进行弹窗,比如我们需要询问用户的一些操作,提供更丰富的交互,又比如我们需要弹出一个框提示用户给我们好评
WP系统提供关于对话框的操作只有一个MessageBox,MessageBox样式简单,并且无法扩展,比如我们需要自定义按钮的文字都不可以,极大限制了我们的发挥,如果我们需要弹窗,还有Popup控件可以用,比如Coding4fun就对Popup进行了扩展,提供了更丰富对话框样式
用CM(Caluburn.Micro)也有一段时间了,CM不仅提供了MVVM的支持,还提供了IoC依赖注入容器,还提供了WindowManager的支持(这是一个非常值得深入研究的开源框架)可以在页面的ViewModel中直接通过控件对应的ViewModel构造控件进行显示,下面我们对控件进行
一、页面框架层次
首先说明一下页面框架的层次关系:
Frame:最底层
Page:页面在Frame的上层
Popup:在页面的上层,弹出的控件会在覆盖在页面的上面
Keyboard:键盘可以遮住Popup弹窗
ApplicationBar:应用程序栏在键盘之上,不会被Popup覆盖,所以自定义的弹窗是不能把ApplicationBar给遮住的
MessageBox:系统自带的MessageBox可以把一切遮住,包括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 = ,
To =
};
Storyboard.SetTarget(doubleAnimation, host);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity", new object[]));
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 = ,
To =
};
Storyboard.SetTarget(doubleAnimation, host);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity", new object[]));
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 = ,
To =
}; Storyboard.SetTarget(doubleAnimation, viewContainer.Projection);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("RotationX", new object[]));
storyboard.Children.Add(doubleAnimation); doubleAnimation = new DoubleAnimation
{
Duration = new Duration(TimeSpan.FromSeconds(DURATION_SECONDS)),
From = ,
To =
};
Storyboard.SetTarget(doubleAnimation, host);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity", new object[]));
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 = ,
To =
}; Storyboard.SetTarget(doubleAnimation, viewContainer.Projection);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("RotationX", new object[]));
storyboard.Children.Add(doubleAnimation); doubleAnimation = new DoubleAnimation
{
Duration = new Duration(TimeSpan.FromSeconds(DURATION_SECONDS)),
From = ,
To =
};
Storyboard.SetTarget(doubleAnimation, host);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity", new object[]));
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实现
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Windows.Media;
using Caliburn.Micro;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell; namespace TestDemo
{
/// <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 * ); viewContainer = new ContentControl
{
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch,
};
maskingLayer = new Border
{
Child = viewContainer,
Background = new SolidColorBrush(Color.FromArgb(alpha, , , )),
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; private void DisableAppBar()
{
if (isHideApplicationBar)
{
if (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;
}
} private void RestoreAppBar()
{
if (isHideApplicationBar)
{
if (applicationBarVisible)
{
currentPage.ApplicationBar.IsVisible = applicationBarVisible;
}
}
else
{
if (currentPage.ApplicationBar.IsMenuEnabled != 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 = ;
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);
效果图
截图后面一张正在登陆的LoadingMask由于比较轻,没有用上面的WindowManager,我自己重新定义了一个PageMaskManager,下次再分享
注意:
1、为了防止多个弹窗导致HideApplicationBar冲突问题
2、由于这里是通过ApplicationBar.IsMenuEnable来判断是否被禁用的
3、所以请保证ApplicationBar.IsMenuEnable为true
WindowManager会改变ApplicationBar的绑定,如果使用了绑定,请注意 IsVisible,IsEnabled,IsMenuEnabled属性的绑定,因为这两个属性在WindowManager中被重新设置过
还有应该注意多个WindowManager同时使用时候的ApplicationBar冲突
个人能力有限,如果上文有误或者您有更好的实现,可以给我留言
转载请注明出处:http://www.cnblogs.com/bomo/p/3952419.html
【WP8】扩展CM的WindowManager的更多相关文章
- 【WP8】扩展CM的INavigationService方法
CM支持通过ViewModel进行导航,并通过支持参数传递,但是内部只是通过反射的方式构造Uri的参数进行导航,所以只支持简单类型的参数传递,下面对其进行扩展,在页面导航时支持复杂类型的参数传递,并扩 ...
- WP8中使用async/await扩展HttpWebRequest
前文讲到WP8中使用Async执行HTTP请求,用了微软提供的扩展.下面提供了一种方法,自己实现HttpWebRequest的扩展. 随后就可以使用 await HttpWebRequest.GetR ...
- [深入浅出WP8.1(Runtime)]Windows Phone 8.1和Silverlight 8.1的区别
1.2.2 Windows Phone 8.1应用程序模型 Windows Phone 8.1支持多种开发语言来开发应用程序,包括C#.VB.JavaScript和C++,那么本书的代码主要是采用C# ...
- 机器学习常用Python扩展包
在Ubuntu下安装Python模块通常有3种方法:1)使用apt-get:2)使用pip命令(推荐);3)easy_instal 可安装方法参考:[转]linux和windows下安装python集 ...
- 6.Swift协议|扩展|访问权限|异常调试|类型转换|运算函数|ARC|类类型初试化器|值类型初始化器
1. 协议(Protocol):与OC之间唯一不同的是Swift中的协议不管是属性还时方法全部是必须实现的 /** protocol*/ protocol FullNamed { /** 计算属性申明 ...
- Swift-----类型转换 、 嵌套类型 、 扩展 、 协议 、 访问控制
使用is和as操作符判断和转换数组中的对象类型 1.1 问题 类型转换可以判断实例的类型,也可以将实例看做是其父类或者子类的实例.在Swift中使用is和as操作符实现类型转换. 本案例定义一个媒体类 ...
- 将Win8.1/WP8.1应用迁移到Universal Windows Platform
在上一篇在VS2015 RC打开CTP中创建的工程,我们介绍了怎么在RC中打开CTP中创建的Universal 工程,这一篇我们来讲下怎么将Windows 8.1/WP8.1的应用迁移到Univers ...
- MVVM架构~knockoutjs系列之扩展ajax验证~验证数据是否存在
返回目录 在大部分网站里,用户名都是唯一的,即当用户注册时,如果用户输入的名字不合法,我们需要提示用户,让用户再起个新名字,而这种复杂的验证一般是通过JS来实现的,如果把它集成到ko里,那就完美了.有 ...
- EF架构~豁出去了,为了IOC,为了扩展,改变以前的IRepository接口
回到目录 使用了4年的IRepository数据仓储接口,今天要改变了,对于这个数据仓储操作接口,它提倡的是简洁,单纯,就是对数据上下文的操作,而直正的数据上下文本身我们却把它忽略了,在我的IRepo ...
随机推荐
- window下,加载redis拓展
下载地址: http://windows.php.net/downloads/pecl/snaps/redis/2.2.5/ 看下自己phpinfo的信息 就选择 ts-x86 ...
- 解决mint17-linux里chrome一搞就弹出“登陆密钥环”的框框
摘自:https://sites.google.com/site/easylinuxtipsproject/tips#TOC-Make-the-keyring-password-on-your-com ...
- centos 7 /etc/rc.local 开机不执行的问题
最近发现centos7 的/etc/rc.local不会开机执行,于是认真看了下/etc/rc.local文件内容的就发现了问题的原因了 1 2 3 4 5 6 7 8 9 10 11 #!/bin/ ...
- [iOS Xcode8]上传AppStore 无法构建版本 没有➕号
最近iOS10出来了 Xcode也跟着升级到了8 想着App做个更新 于是修改好了代码打算上传新包 ,无奈总是发现构建不了新版本 经过各种蛋疼的查找.我列一下我的经验 1.如果是收费的App,那么是要 ...
- 单源最短路径算法---Dijkstra
Dijkstra算法树解决有向图G=(V,E)上带权的单源最短路径问题,但是要求所有边的权值非负. 解题思路: V表示有向图的所有顶点集合,S表示那么一些顶点结合,从源点s到该集合中的顶点的最终最短路 ...
- struts2:遍历自定义字符串数组,遍历Action实例所引用对象中的数组
在struts2:OGNL表达式,遍历List.Map集合:投影的使用一文中已经讲述了OGNL遍历List.Map集合等功能. 本文简单写一个遍历数组的示范程序. 1. 遍历自定义字符串数组 < ...
- android 九宫加密记事本
自己写的超级安全记事本,用PBEWithMD5AndDES加密sqlite内容几乎无法破解, 九宫点密码登录, 支持备份到SDcard,email,network drivers etc. 附件Apk ...
- 【原】 COCOS2D—LUA 获取剪贴板内容
android下: local luaj = require ("framework.luaj") local ok,ret = luaj.callStaticMethod( ...
- 关于Android真机调测Profiler
U3D中的Profile也是可以直接在链接安卓设备运行游戏下查看的,导出真机链接U3D的Profile看数据,这样能更好的测试具体原因. 大概看了下官方的做法,看了几张帖子顺带把做法记录下来. ...
- C8051 SMBus 原理
一.SMBus总线 SMBus串行I/O接口完全符合系统管理总线规范 1.1 版.它是一个双线的双向串行总线,与I2C串行总线兼容.系统控制器对总线的读写操作都是以字节为单位的,由SMBus接口自 ...