调试XAML和vb中的资源值问题?

时间:2015-12-14 05:50:33

标签: wpf vb.net xaml animation

我的似乎就像一个直截了当的问题:制作一个可以处理任何分辨率显示的应用程序。

背景 这是一个仅显示应用程序(无用户界面)。这是为了显示信息,我已经制作了一个测试应用来解决问题。花了几天时间试图找到解决这个小问题的方法。要么需要一个聪明的编程解决方案(迄今为止我已经无法解决),要么采用不同的方式解决问题(再次,难以捉摸)。

这是一个后面有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

With the window small, the animation causes the bar to overshoot the location where it need to be (the grid height value is too large for this size window

使窗口大于默认值会对动画产生相反的影响;酒吧低于着陆位置。

我已经考虑将整个显示器包装在一个视图框控件中,因此整个混乱可以缩放以适应任何显示,并尝试了其中一些,结果不那么出色。它过于复杂,图形和文本都有扭曲。

从我可以做的故障排除中,似乎资源值确实发生了变化,而不是绑定点的值;我几乎坚持使用应用程序启动时使用的任何值。

为动态可配置的布局和广泛的动画功能提供框架似乎很奇怪,然后让这两个方面陷入困境并且不能很好地发挥作用#34;因为一个简单的double值无法从代码传递到布局标记。

请原谅帖子的长度;我试图彻底,并试图预测某人可能写的回应。还请原谅样品的可怕颜色 - 在测试布局期间对我有什么帮助,不适合最终应用。

理想情况下,最佳解决方案将允许任何大小的显示,而无需程序预先知道该分辨率。此外,能够在程序执行期间调整大小将是很好的,以防程序可以在一个小于全屏的安排中使用(毕竟)。在这种情况下,它需要能够优雅地处理大多数合理的窗口大小(我必须在窗口大小的某些下边界限制内编码)。

无论我使用什么解决方案,它都需要能够处理其他类似的动画需求(其他部分需要在屏幕上移动和移动,在给定的情况下移动正确的数量)。

1 个答案:

答案 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

感谢您指出我正确的方向,我希望我的后续行动(诚然很长)可以帮助其他人。