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的更多相关文章

  1. 【WP8】扩展CM的INavigationService方法

    CM支持通过ViewModel进行导航,并通过支持参数传递,但是内部只是通过反射的方式构造Uri的参数进行导航,所以只支持简单类型的参数传递,下面对其进行扩展,在页面导航时支持复杂类型的参数传递,并扩 ...

  2. WP8中使用async/await扩展HttpWebRequest

    前文讲到WP8中使用Async执行HTTP请求,用了微软提供的扩展.下面提供了一种方法,自己实现HttpWebRequest的扩展. 随后就可以使用 await HttpWebRequest.GetR ...

  3. [深入浅出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# ...

  4. 机器学习常用Python扩展包

    在Ubuntu下安装Python模块通常有3种方法:1)使用apt-get:2)使用pip命令(推荐);3)easy_instal 可安装方法参考:[转]linux和windows下安装python集 ...

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

    1. 协议(Protocol):与OC之间唯一不同的是Swift中的协议不管是属性还时方法全部是必须实现的 /** protocol*/ protocol FullNamed { /** 计算属性申明 ...

  6. Swift-----类型转换 、 嵌套类型 、 扩展 、 协议 、 访问控制

    使用is和as操作符判断和转换数组中的对象类型 1.1 问题 类型转换可以判断实例的类型,也可以将实例看做是其父类或者子类的实例.在Swift中使用is和as操作符实现类型转换. 本案例定义一个媒体类 ...

  7. 将Win8.1/WP8.1应用迁移到Universal Windows Platform

    在上一篇在VS2015 RC打开CTP中创建的工程,我们介绍了怎么在RC中打开CTP中创建的Universal 工程,这一篇我们来讲下怎么将Windows 8.1/WP8.1的应用迁移到Univers ...

  8. MVVM架构~knockoutjs系列之扩展ajax验证~验证数据是否存在

    返回目录 在大部分网站里,用户名都是唯一的,即当用户注册时,如果用户输入的名字不合法,我们需要提示用户,让用户再起个新名字,而这种复杂的验证一般是通过JS来实现的,如果把它集成到ko里,那就完美了.有 ...

  9. EF架构~豁出去了,为了IOC,为了扩展,改变以前的IRepository接口

    回到目录 使用了4年的IRepository数据仓储接口,今天要改变了,对于这个数据仓储操作接口,它提倡的是简洁,单纯,就是对数据上下文的操作,而直正的数据上下文本身我们却把它忽略了,在我的IRepo ...

随机推荐

  1. vim之插入

    进入vim插入模式,我们熟知的有: 字符位置插入: i      在光标之前插入 a     在光标之后追加 行位置插入: A  在一行的结尾处追加 I   在一行的开头处插入 o  在光标所在位置的 ...

  2. WPF 获取程序路径的一些方法,根据程序路径获取程序集信息

    一.WPF 获取程序路径的一些方法方式一 应用程序域 //获取基目录即当前工作目录 string str_1 = System.AppDomain.CurrentDomain.BaseDirector ...

  3. LCLFramework框架之Repository模式

    Respository模式在示例中的实际目的小结一下 Repository模式是架构模式,在设计架构时,才有参考价值: Repository模式主要是封装数据查询和存储逻辑: Repository模式 ...

  4. 将十六进制的颜色字符串转为UIColor

    扩展UIColor,将十六进制的颜色字符串转成UIColor对象. extension UIColor { static func colorWithHexString(hex:String) -&g ...

  5. Java程序员转Android开发必读经验分享

    小编最近几日偷偷的发现部分Java程序员想转安卓开发,故此加紧补充知识,为大家搜集资料,积极整理前人的经验,希望可以给正处于困惑中的你,带来些许的帮助. 啰哩啰嗦的说说Java和Android程序的区 ...

  6. ssh框架搭建错误集合

    1,把jsp放入到WEB-INF/view目录下,struts2.xml配置<result name="success">/WEB-INF/view/home.jsp& ...

  7. System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x800AC472

    更新至服务器后运行出错: System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x800AC472 解决方法 注册 ...

  8. CentOS 6.6 配置PuTTY远程登录

    1. 下载与安装 可以下载putty-0.63-installer.exe(http://www.chiark.greenend.org.uk/~sgtatham/putty/download.htm ...

  9. 在linux下安装Xwindows

    检查Linux系统是否能够联网. 执行命令 yum -y groupinstall Desktop 等上面的命令执行完后,再执行这条命令 yum -y groupinstall "X Win ...

  10. Ranorex入门指南

    Ranorex入门指南 http://automationqa.com/forum.php?mod=viewthread&tid=2766&fromuid=29