65.9K
CodeProject 正在变化。 阅读更多。
Home

Windows Phone 8 中的动态布局更改

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (2投票s)

2014年2月23日

CPOL

4分钟阅读

viewsIcon

18090

downloadIcon

153

展示了如何使用页面绑定来自动适应方向变化时的页面布局。

引言

我最近被分配了一个 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 - 可用于设置 WidthHeightMaxHeight 等。
  • OrientationToDoubleNullableConverter - 我专门使用它来设置 MaxHeight MaxWidth。在这种情况下,x:Null 值表示没有 MaxHeight ,而数字值将限制它。

让我们再看一个类:

public abstract class BoolToValueConverter<T> : IValueConverter { } 

该类再次定义了两种类型 T 的值,名为 TrueValue FalseValue。它执行相同类型的转换,但它会检查传入的 boolstring 值,最后只是一个 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 ,则“保存”按钮可以可见)。

您不必局限于这两个基类;这只是我为本文选择的两个。

关注点

XAML 是一个功能极其强大的工具,我很高兴看到 Windows Phone 8 中它的子集如此之大。对于熟悉 WPF 的人来说,确实缺少一些功能,但几乎所有这些都可以很容易地绕过。这些绑定以及 C# 和 XAML 等高级语言的性能在 Windows Phone 8 设备上表现非常好。我对这一切感到惊喜。总而言之,Windows Phone 8 非常有趣,唯一的缺点是您必须在自己的机器上安装 Windows 8 才能进行操作 =(。

历史

  • 初始版本
© . All rights reserved.