Windows Phone 8 中的动态布局更改
展示了如何使用页面绑定来自动适应方向变化时的页面布局。
引言
我最近被分配了一个 Windows Phone 8 项目,我想找到一种方法,让我的应用程序能够根据方向、屏幕尺寸,甚至依赖于对象属性来动态调整任何我想要的内容。在特定窗口中,可以通过处理事件来用 C# 代码处理这一切,但我认为 XAML 解决方案会更简洁。另外两种可能性是行为或库。
背景
用户应该熟悉 Silverlight 或 WPF 中的 XAML。
在 Android 设备上(我的背景),您需要创建两个独立的布局文件(相当于 WP8 中的 XAML)。您将相同的元素放置在具有唯一 ID 的布局中,Android 操作系统会自动在这两个布局之间切换。Windows Phone 8 的工作方式并非如此。相反,您只有一个布局页面。在该页面内,您可以创建多个 DataTemplate
对象,并在方向更改等事件中进行切换。我对这种解决方案并不满意,因为它不够优雅,并且需要大量的 C# 代码和事件处理程序才能使其正常工作。
Using the Code
首先,我创建了几个 IValueConverter
类。让我们来看一个根据方向处理布局更改的类。首先是这个类:
public abstract class OrientationToValueConverter<T> : IValueConverter {
public T PortraitValue { get; set; }
public T LandscapeValue { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value is PageOrientation) {
var orientation = (PageOrientation)value;
if ((orientation == PageOrientation.Landscape) ||
(orientation == PageOrientation.LandscapeLeft) ||
(orientation == PageOrientation.LandscapeRight)) {
return LandscapeValue;
}
else if ((orientation == PageOrientation.Portrait) ||
(orientation == PageOrientation.PortraitDown) ||
(orientation == PageOrientation.PortraitUp)) {
return PortraitValue;
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
该类包含两种任何类型的 T 的值,PortraitValue
和 LandscapeValue
。它只是获取传入的 PageOrientaion
值,并根据需要返回相应的纵向和横向值。这样做的目的是什么?好吧,您可以创建基于此的单行类,如下所示:
public class OrientationToVisibilityConverter : OrientationToValueConverter<Visibility> { }
public class OrientationToStackPanelOrientationConverter : OrientationToValueConverter<Orientation> { }
public class OrientationToGridLengthConverter : OrientationToValueConverter<GridLength> { }
public class OrientationToScrollBarVisibilityConverter : OrientationToValueConverter<ScrollBarVisibility> { }
public class OrientationToDoubleConverter : OrientationToValueConverter<double> { }
public class OrientationToDoubleNullableConverter : OrientationToValueConverter<double?> { }
让我们来检查一下这些新派生类中的一个,看看它们如何让我们的生活变得轻松。我们将查看 OrientationToVisibilityConverter
类。
我们可以在 PhoneApplicationPage
或底层 Grid
的 resources 属性中定义它,如下所示:
<Grid.Resources>
<converters:OrientationToVisibilityConverter x:Key="PortraitVisible" LandscapeValue="Collapsed" PortraitValue="Visible" />
<converters:OrientationToVisibilityConverter x:Key="LandscapeVisible" LandscapeValue="Visible" PortraitValue="Collapsed" />
</Grid.Resources>
请记住也要添加命名空间。请注意,这里我们有两个相同的转换器的独立实例。一个仅在纵向模式下使项目可见,另一个仅在横向模式下使项目可见。让我们来实现它。
<TextBlock Text="Portrait Only"
Visibility="{Binding Orientation, ElementName=MyPage, Converter={StaticResource PortraitVisible}}" />
<TextBlock Text="Landscape Only"
Visibility="{Binding Orientation, ElementName=MyPage, Converter={StaticResource LandscapeVisible}}" />
在这种情况下,您需要为页面命名(例如 MyPage
)。您还可以使用 RelativeSource
绑定,但这更简单。结果?您的内容已根据 PageOrientation
自动更改。
其他类有什么用?这里有一些我使用它们的例子。
OrientationToStackPanelOrientationConverter
- 允许StackPanel
在横向模式下水平排列,在纵向模式下垂直排列。OrientationToGridLengthConverter
- 允许对网格行或列进行大小调整。例如,您可能希望在一种情况下自动调整大小,在另一种情况下按比例调整大小。OrientationToScrollBarVisibilityConverter
- 假设您希望所有内容在横向模式下水平适应。您可以禁用横向滚动条,并在纵向模式下将其设置为自动。OrientationToDoubleConverter
- 可用于设置Width
、Height
、MaxHeight
等。OrientationToDoubleNullableConverter
- 我专门使用它来设置MaxHeight
或MaxWidth
。在这种情况下,x:Null
值表示没有MaxHeight
,而数字值将限制它。
让我们再看一个类:
public abstract class BoolToValueConverter<T> : IValueConverter { }
该类再次定义了两种类型 T 的值,名为 TrueValue
和 FalseValue
。它执行相同类型的转换,但它会检查传入的 bool
、string
值,最后只是一个 null
/非 null
检查。
if (value is bool) {
return (bool)value;
}
else if (value is string) {
return !string.IsNullOrWhiteSpace(value.ToString());
}
else {
return = (value != null);
}
同样,和以前一样,我创建了一些扩展此功能的类。这里有两个:
public class BoolToVisibilityConverter : BoolToValueConverter<Visibility> { }
public class BoolToDoubleConverter : BoolToValueConverter<double> { }
BoolToVisibilityConverter
应该很明显。但在 WP8 中,Visibility.Hidden
类型被移除了。所以您只能让项目可见或折叠。如果您想根据条件隐藏某个项目,但又想保留它在屏幕上的空间怎么办?很简单。只需创建一个 BoolToDoubleConverter
并将其绑定到不透明度!现在可以根据绑定隐藏该项目。此类的绑定通常不是针对方向,而是通常绑定到 DataContext
或对象属性(例如,如果我的屏幕加载了 DataContext
,则“保存”按钮可以可见)。
您不必局限于这两个基类;这只是我为本文选择的两个。
关注点
历史
- 初始版本