我的似乎就像一个直截了当的问题:制作一个可以处理任何分辨率显示的应用程序。
背景 这是一个仅显示应用程序(无用户界面)。这是为了显示信息,我已经制作了一个测试应用来解决问题。花了几天时间试图找到解决这个小问题的方法。要么需要一个聪明的编程解决方案(迄今为止我已经无法解决),要么采用不同的方式解决问题(再次,难以捉摸)。
这是一个后面有VB.net代码的WPF应用程序。唯一窗口的组成需要保持与显示组件相同的相对布局,并且应用程序旨在全屏运行(此演示在窗口中运行,因此我可以轻松调整大小并测试不同的&#34 ;屏幕布局")。
布局的一部分以外的所有工作正常。
有问题的部分是" bar"根据具体情况,它可用于向上或向下设置动画。条形图的大小已经适当更改,具体取决于应用程序窗口的大小。但是......酒吧行进的距离取决于酒吧的大小;它越短,它必须行进的像素越少,相反,更高分辨率的屏幕需要它进一步行进。通过更改包含矩形(条形)和视口的网格对象的高度来完成动画,视口又包含文本块对象。
我在XAML中创建了一个资源值,其值表示包含该栏的网格对象的默认高度。如果该资源作为静态资源绑定到关键帧的值,则该值将传递给storyboard并发生动画。但是,我无法更改此配置中的值。当窗口大小发生变化时,VB代码会尝试更改资源键值,但没有快乐。还尝试将此绑定作为动态资源完成。
大量的阅读表明某些绑定到XAML工作,而其他(如关键帧)并不那么幸运;关于"可冻结的事情,"这看起来很傻,因为这不应该那么难。我读过的其他内容表明"修复"是在后面的代码中实现解决方案。大多数人都没有提供关于如何做到这一点的任何其他信息,以及其他一些看起来很荒谬的其他项目建议方法。
以下是XAML,其中包含一些内联评论,指出正在进行的操作:
<Window x:Name="SmartClock" x:Class="MainWindow"
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"
xmlns:local="clr-namespace:WpfApplication3"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Window.Resources>
<!-- Set a variable (resource) to be used as the height value in animation -->
<System:Double x:Key="OnAirBarUp">84</System:Double>
<!-- Simple keyframe animation, which slides a bar with text into view -->
<Storyboard x:Key="story_OnAirUp" x:Name="OnAirUp">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<!-- ****** HERE'S WHERE THE "FUN" BEGINS :/
This line needs to have a target value which changes, depending on the
height of objects in the window, and those are affected by the size
of the window. The intention is to make this a full-screen app, but
can be used on many different monitors, so the resolution is not known
at design time.
The resource I set earlier in the XAML ("84") will be used for this
keyframe value, and the animation works... but is only "correct" for
the default window size.
(yes, I know that the bar starts in the "up" position currently, but that
is just to help me see it as I debug this... when the button is pushed,
the bar should instantly disappear, and then slide back on to the screen
to the same position).
*************************************************************************** -->
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="{StaticResource ResourceKey=OnAirBarUp}"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<!-- Animation is triggered by clicking the button -->
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
<BeginStoryboard Storyboard="{StaticResource story_OnAirUp}"/>
</EventTrigger>
</Window.Triggers>
<!-- Here we setup the page -->
<Grid x:Name="grid_Page" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="4*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<!-- This is "status" grid at the top of the page -->
<Grid x:Name="grid_Top" Grid.Row="0" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Name="row_ProgramName" Height="1*"/>
<RowDefinition Name="row_Status" Height="1*"/>
</Grid.RowDefinitions>
<Viewbox x:Name="viewbox_ProgramName" Grid.Row=" 0" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_ProgramName" Text="Some text on top" Background="#FFFFFF12"/>
</Viewbox>
<!-- This creates a grid which will hold the "ON AIR" bar -->
<Grid x:Name="grid_OnAir" Grid.Row="1" VerticalAlignment="Bottom" ShowGridLines="True" Height="84" >
<Rectangle x:Name="rectangle_OnAir" Fill="#FF90A436" RadiusX="20" RadiusY="20" Margin="10,0" Height="83"/>
<Viewbox x:Name="viewbox_OnAir" Grid.Row="1" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_OnAir" Text="ON AIR" FontSize="64" VerticalAlignment="Top" FontWeight="Bold" Background="#FFDC0000" Margin="0" HorizontalAlignment="Center" />
</Viewbox>
</Grid>
</Grid>
<!-- This is a big ugly button for triggering the storyboard -->
<Button x:Name="button1" Content="Button" Margin="0" Grid.Row="1" FontSize="26.667" HorizontalAlignment="Left" Width="257"/>
</Grid>
</Window>
这是VB.net代码,也有很多评论:
Class MainWindow
Private Sub MainWindow_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
' Set the size of items on the screen based on the size of the window.
' "rectangle_OnAir" is the rectangle (the "On-Air" bar for the display)
' "viewbox_OnAir" allows the textblock to properly scale.
' "grid_OnAir" is the container which will be made taller and shorter
' through animating the "height" property. This allows the contents
' to slide up and down in their region of the screen.
' Since the window can be any size, we adjust the height of the
' controls to accomodate.
rectangle_OnAir.Height = grid_Top.ActualHeight / 2
viewbox_OnAir.Height = grid_Top.ActualHeight / 2
grid_OnAir.Height = grid_Top.ActualHeight / 2
' Set the height of the dynamic resource "OnAirBarUp" to a value
' which equals the height of "rectangle_OnAir".
' This will be used in the animation to allow the whole rectangle to
' be displayed.
Resources("OnAirBarUp") = grid_Top.ActualHeight / 2
End Sub
End Class
这是许多排列的一个版本。
当代码运行时没有重新调整窗口大小,这就是它的样子: This is the default layout of this test page... the "On Air" bar is visible, and pressing the "button" causes it to disappear for a moment, and then immediately animate upwards into the current position
使窗口大于默认值会对动画产生相反的影响;酒吧低于着陆位置。
我已经考虑将整个显示器包装在一个视图框控件中,因此整个混乱可以缩放以适应任何显示,并尝试了其中一些,结果不那么出色。它过于复杂,图形和文本都有扭曲。
从我可以做的故障排除中,似乎资源值确实发生了变化,而不是绑定点的值;我几乎坚持使用应用程序启动时使用的任何值。
为动态可配置的布局和广泛的动画功能提供框架似乎很奇怪,然后让这两个方面陷入困境并且不能很好地发挥作用#34;因为一个简单的double值无法从代码传递到布局标记。
请原谅帖子的长度;我试图彻底,并试图预测某人可能写的回应。还请原谅样品的可怕颜色 - 在测试布局期间对我有什么帮助,不适合最终应用。
理想情况下,最佳解决方案将允许任何大小的显示,而无需程序预先知道该分辨率。此外,能够在程序执行期间调整大小将是很好的,以防程序可以在一个小于全屏的安排中使用(毕竟)。在这种情况下,它需要能够优雅地处理大多数合理的窗口大小(我必须在窗口大小的某些下边界限制内编码)。
无论我使用什么解决方案,它都需要能够处理其他类似的动画需求(其他部分需要在屏幕上移动和移动,在给定的情况下移动正确的数量)。
答案 0 :(得分:0)
我昨晚在Microsoft's MSDN forums上的Stack Overflow 和上发布了此消息,并在MSDN上获得了良好的回复。这指向了正确的方向,我发布了一条后续消息,详细描述了对我有用的内容。以下是后续报告:
安迪,
感谢您快速而详细的回复!
我将从这一部分开始:它现在有效。
我确实需要花费几个小时的时间(使用你的线索)才能到达那里。我当时(大约四个小时前)开始这个回复,但坚持得到了回报。
-------------
你说:&#34;我知道你为这个酒吧制作动画,但不知道它相对于它的来源或去往的地方。&#34;
我猜测你的字面意思,就像屏幕上的位置一样,而不是概念上(就像目的一样)。
字面意思&#34;其中&#34;是使条形从屏幕的中间第三(大约)区域上升(水平分割)。整个应用程序是一个具有一些增值功能的时钟,可用于新闻编辑室。我已经有一个基于这种设计的工作时钟,但是&#34; On Air&#34;酒吧尚未实施。这将通过网络套接字连接上的事件触发,完全是当工作室播出时。&#34;播出。当发生这种情况时,屏幕上方三分之一处的文字会优雅地缩小为“空中&#34;酒吧上升,基本上进入前三分之一区域。
如果我没有调整窗口大小,动画就能正常工作。因为我不知道将在不同的实现中使用什么尺寸的显示器,并且因为这将最终在我们的其他一些设备做同样的事情,我需要在这个阶段构建它所以它足够灵活,可以处理1024x768屏幕或1920x1080,或介于两者之间,或4K,或其他什么。
我可以使条形图固定大小,但这会违背布局的平衡(美学问题)和正确尺寸文本的可读性(可用性问题)。
根据情况(实时发生的其他事情,由计算机网络上的事件触发),还有其他状态字段可以进入视图,这需要具有类似的定位灵活性动画。
查看您向我指出的选取框代码,似乎对对象进行了绑定,但我不确定该方法如何改变关键帧可以移动对象的距离,只需调整属性被移动的物体。
-------------
我查看了"marquee" code,虽然需要一些耐心来剖析它。 C#不是太糟糕,但我不流利,所以我转向在线代码转换器将其转换为VB:
Public Partial Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
End Sub
Private SBMarquee As Storyboard
Private XAnimation As DoubleAnimation
Private Sub Window_ContentRendered(sender As Object, e As EventArgs)
SBMarquee = TryCast(Me.Resources("SBmarquee"), Storyboard)
XAnimation = TryCast(SBMarquee.Children(0), DoubleAnimation)
XAnimation.[To] = MarqueeContainer.ActualWidth * -1
AddHandler Me.SizeChanged, AddressOf Window_SizeChanged
End Sub
Private Sub Window_SizeChanged(sender As Object, e As SizeChangedEventArgs)
XAnimation.[To] = MarqueeContainer.ActualWidth * -1
MarqueeContainer.Visibility = Visibility.Hidden
SBMarquee.Begin()
MarqueeContainer.Visibility = Visibility.Visible
End Sub
End Class
冷却。
我不熟悉部分课程,但在网上快速检查似乎告诉我,我可以将相关部分包含在主窗口的VB类中。
我不知道&#34; initializeComponent&#34;部分是必需的,但它没有抛出错误。但是,创建Storyboard对象的尝试遇到了一些阻力。我想我需要一个&#34; Imports&#34;命令来添加正确的命名空间(完全披露:&#34;命名空间&#34;的概念对我来说不明显,但我得到了需要导入的东西...&#34;命名空间&# 34;绰号看起来不合适,而且我太字面了。这是我添加的内容,就在最顶层(在&#34;类&#34;行之上):
Imports System.Windows.Media.Animation
现在这些行有效:
私人故事_OnAirUp作为故事板 Private timeline_OnAirUp As DoubleAnimationUsingKeyFrames
第一行创建一个Storyboard对象,第二行创建必要的时间轴对象。我一开始以为我会做一个&#34; EasingDoubleKeyFrame&#34;我可以做的对象,但Storyboard对象的子对象不是关键帧(嗯,不是使用DoubleAnimationUsingKeyFrames元素构造的XAML);我最初使用关键帧的尝试遇到阻力。以下是我尝试过哪些没有工作:
Private story_OnAirUp As Storyboard
Private keyframe_OnAirUp As EasingDoubleKeyFrame
此时没有错误,所以我想到我是金色的。没那么快,年轻的padawan。
这是我添加的下一个代码,从选取框示例中改变:
Private Sub Window_ContentRendered(sender As Object, e As EventArgs)
story_OnAirUp = TryCast(Me.Resources("story_OnAirUp"), Storyboard)
keyframe_OnAirUp = TryCast(story_OnAirUp.Children(1), EasingDoubleKeyFrame)
keyframe_OnAirUp.Value = grid_Top.ActualHeight / 2
AddHandler Me.SizeChanged, AddressOf MainWindow_SizeChanged
End Sub
没有骰子。 &#34; TryCast(story_OnAirUp.Children(1)&#34;部分生成错误:
&#34;类型的价值&#39;时间线&#39;无法转换为&#39; EasingDoubleKeyFrame&#39;。&#34;
嗯,那很臭。
在对代码进行一些比较之后,我发现我的XAML代码与选取框示例不同,因为包含了这一行:
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
因此,Storyboard对象的子节点不是EasingDoubleKeyFrame(嗯,不是直接),而是&#34; DoubleAnimationUsingKeyFrames&#34;元件。但是,我需要绑定的值不在DoubleAnimationUsingKeyFrames元素中;它位于EasingDoubleKeyFrame中。
(注意:示例代码有&#34; DoubleAnimation&#34;更改的属性为&#34; To:&#34;但&#34; EasingDoubleKeyFrame&#34;使用&#34;值&# 34;财产;我注意到并相应调整。)
我试图制作DoubleAnimationUsingKeyFrames元素的子对象,但事实证明&#34;孩子&#34;是它的财产。然后我突然明白了:
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
&#34;(1)&#34;指向第二个关键帧,这是&#34; On Air&#34;网格位于&#34;全高,&#34;其默认值为84像素,但需要更改。
我得到&#34; Window_ContentRendered&#34;完成后,我尝试运行代码。
&#34; MainWindow_SizeChanged&#34;中的错误子程序:
&#34;未处理的类型&#39; System.NullReferenceException&#39;发生在WpfApplication3.exe中 附加信息:对象引用未设置为对象的实例。&#34; 嗯。好的,&#34; MainWindow_SizeChanged&#34;例程在&#34; Window_ContentRendered&#34;之前运行例程,不允许在使用对象之前对其进行实例化。
我从&#34; Window_ContentRendered&#34;移动了对象创建代码。 to&#34; MainWindow_SizeChanged,&#34;评论我们的&#34; ContentRendered&#34;例程,然后再试一次。
有效。这是有效的代码:
<强> XAML 强>
<Window x:Name="SmartClock" x:Class="MainWindow"
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"
xmlns:local="clr-namespace:WpfApplication3"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Window.Resources>
<!-- Simple keyframe animation, which slides a bar with text into view -->
<Storyboard x:Key="story_OnAirUp" x:Name="OnAirUp">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<!-- **** This is the keyframe which has a changed value from the VB code -->
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="84"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<!-- Animation is triggered by clicking the button -->
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
<BeginStoryboard Storyboard="{StaticResource story_OnAirUp}"/>
</EventTrigger>
</Window.Triggers>
<!-- Here we setup the page -->
<Grid x:Name="grid_Page" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="4*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<!-- This is "status" grid at the top of the page -->
<Grid x:Name="grid_Top" Grid.Row="0" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Name="row_ProgramName"/>
<RowDefinition Name="row_Status" Height="Auto"/>
</Grid.RowDefinitions>
<Viewbox x:Name="viewbox_ProgramName" Grid.Row=" 0" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_ProgramName" Text="Some text on top" Background="#FFFFFF12"/>
</Viewbox>
<!-- This creates a grid which will hold the "ON AIR" bar -->
<Grid x:Name="grid_OnAir" Grid.Row="1" VerticalAlignment="Bottom" ShowGridLines="True" Height="84" >
<Rectangle x:Name="rectangle_OnAir" Fill="#FF90A436" RadiusX="20" RadiusY="20" Margin="10,0" Height="83"/>
<Viewbox x:Name="viewbox_OnAir" Grid.Row="1" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_OnAir" Text="ON AIR" FontSize="64" VerticalAlignment="Top" FontWeight="Bold" Background="#FFDC0000" Margin="0" HorizontalAlignment="Center" />
</Viewbox>
</Grid>
</Grid>
<!-- This is a big ugly button for triggering the storyboard -->
<Button x:Name="button1" Content="Button" Margin="0" Grid.Row="1" FontSize="26.667" HorizontalAlignment="Left" Width="257"/>
</Grid>
</Window>
<强> VB 强>
Imports System.Windows.Media.Animation
Class MainWindow
Public Sub New()
InitializeComponent()
End Sub
Private story_OnAirUp As Storyboard
Private timeline_OnAirUp As DoubleAnimationUsingKeyFrames
Private Sub MainWindow_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
' These next three lines were moved from the "ContentRendered" section
story_OnAirUp = TryCast(Me.Resources("story_OnAirUp"), Storyboard)
timeline_OnAirUp = TryCast(story_OnAirUp.Children(0), DoubleAnimationUsingKeyFrames)
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
rectangle_OnAir.Height = grid_Top.ActualHeight / 2
viewbox_OnAir.Height = grid_Top.ActualHeight / 2
grid_OnAir.Height = grid_Top.ActualHeight / 2
' This is the line which actually sets the value to the animation keyframe.
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
End Sub
End Class
感谢您指出我正确的方向,我希望我的后续行动(诚然很长)可以帮助其他人。