为什么WPF使用对角分割渲染我的控件?

时间:2011-01-14 20:22:15

标签: wpf xaml

WPF似乎是对角分割我的控件。我究竟做错了什么?蓝色按钮上的问题更清晰,但右侧的问题仍然很明显。

alt text alt text

奇怪的是,kaxaml正确呈现按钮。 WPF设计师似乎永远不会。当我在独立的'WpfApplication1'中运行示例代码时,它正确呈现。但是,在我的应用程序中,我得到了对角线“切割”。值得一提的是,在运行时,WPF似乎不能始终如一地应用切片效果,有时它们会正确呈现!

更新1: 线索!它只是 影响按钮!当我创建一个独立的Border并将标签作为其内容时,没有切片效果?!?!!所以它不是xaml本身,而是与按钮有关的神奇之处?!大声思考,与未完全覆盖默认Button模板有关的事情?

更新2: 嗯,这一点变得更奇怪了。这与阴影效果有关。似乎要绘制的第一种具有效果的控件(BitmapEffect或wpf 4.0 Effect)与所有其他控件实例一样被拆分。例如,这里有一个带有可爱红色阴影的DatePicker,它对角地分割DatePicker控件,之后按钮很好,尽管也有效果应用于它。

alt text

如果我不应用效果,则不会分割任何控件。如果我绘制一个具有效果的控件,则此控件将被拆分,并且不同类型的后续控件也可以。但是,如果您有两个或三个相同类型的控件,它们也会被拆分。即,如果按钮被拆分,页面上的所有按钮也将被拆分。

这必须与我的GPU或图形驱动程序有关。我今天早上更新了它们,但没有快乐。 (我使用的是Radeon Mobility HD 5650,v 8.683.2.0)。如果这个问题与我的电脑隔离,我认为这不是世界末日。可能,我可以通过在每个页面顶部的随机路径像素上绘制透明效果来打败它自己的游戏......

更新3

亲爱的,亲爱的。我现在在另一台PC上重现了这个,所以它不是我的显卡或驱动程序。

        <StackPanel.Resources>

            <!-- Background for button when IsDefault  true"-->
            <LinearGradientBrush x:Key="DefaultButtonFill" StartPoint="0.5, 0" EndPoint="0.5, 1">
                <GradientStop Color="#FFDFEDEC" Offset="0"/>
                <GradientStop Color="#FF92B1E3" Offset="0.4"/>
                <GradientStop Color="#FF749EE0" Offset="0.5"/>
                <GradientStop Color="#94DDF6" Offset="1"/>
            </LinearGradientBrush>

            <!-- default button background -->
            <LinearGradientBrush x:Key="NotDefaultButtonFill" StartPoint="0.5, 0" EndPoint="0.5, 1">
                <GradientStop Color="#FFEFEFF5" Offset="0"/>
                <GradientStop Color="#FFE1E1E6" Offset="0.4"/>
                <GradientStop Color="#FFC7CBD0" Offset="0.5"/>
                <GradientStop Color="#FFE8ECEE" Offset="1"/>
            </LinearGradientBrush>

            <Style TargetType="Button">
                <Setter Property="FontSize" Value="14"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Border x:Name="border"
                        CornerRadius="12"
                        Background="{StaticResource NotDefaultButtonFill}"
                        Padding="25 8"
                        Margin="10"
                        Cursor="Hand">


                                <Border.BitmapEffect>
                                    <DropShadowBitmapEffect x:Name="shadow" Direction="280" Color="Black"  ShadowDepth="2" Opacity=".6"/>
                                </Border.BitmapEffect>

                                <!--
                    <Border.Effect>
                        <DropShadowEffect x:Name="shadow" Direction="280" Color="Black"  ShadowDepth="2" Opacity=".6"/>
                    </Border.Effect>-->

                                <ContentPresenter 
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Content="{TemplateBinding Content}" />

                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsDefault" Value="True">
                                    <Setter TargetName="border" Property="Background" Value="{StaticResource DefaultButtonFill}"/>
                                </Trigger>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter TargetName="border" Property="Background" Value="LightGray"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>





        </StackPanel.Resources>

        <StackPanel Orientation="Horizontal">
            <Button>Normal</Button>
            <Button IsDefault="True">IsDefault</Button>
        </StackPanel>
    </StackPanel>

5 个答案:

答案 0 :(得分:3)

这是由于WPF和ATI图形驱动程序之间的某些微妙交互导致的错误。在对像素边界上没有完全定位的元素上使用DropShadowEffect时似乎会发生这种情况。

如果您在相关元素(或相应的样式)上设置UseLayoutRounding =“True”,这似乎可以在很多情况下解决问题,尽管不是全部。另一件事是确保你不是故意将元素定位在半像素处(例如,通过使用小数边距 - 如果在Expression Blend中移动元素,就会发生这种情况。)

这是一个简单的责备案例:

 <UserControl
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <UserControl.Resources>
     <Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
         <Setter Property="Width" Value="28"/>
         <Setter Property="Height" Value="28"/>
         <Setter Property="Template">
             <Setter.Value>
                 <ControlTemplate TargetType="{x:Type Button}">
                     <Border x:Name="Border" Background="Transparent">
                         <Grid Margin="{TemplateBinding Padding}">
                         <ContentPresenter x:Name="contentPresenter" VerticalAlignment="Center"
                                           HorizontalAlignment="Center">
                           <ContentPresenter.Effect>
                             <DropShadowEffect BlurRadius="13" Color="Black" Opacity="1" ShadowDepth="0" Direction="0"/>
                           </ContentPresenter.Effect>
                         </ContentPresenter>
                         </Grid>
                     </Border>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
     </Style>
   </UserControl.Resources>
   <Grid>  
     <Button x:Name="MinimizeButton"
                Style="{StaticResource ButtonStyle}">
            <Rectangle x:Name="MinimizeIcon"
                       Width="11"
                       Height="2"
                       HorizontalAlignment="Center"
                       Margin="0,7,0,0"
                       VerticalAlignment="Bottom"
                       Stroke="Gray"
                       StrokeThickness="2"/>
    </Button>
   </Grid>
 </UserControl>

如果您坚持使用Kaxaml并移动缩放滑块,则应该会在缩放设置为150时看到分割。

如果您将<Setter Property="UseLayoutRounding" Value="True"/>添加到按钮样式,缩放级别150会正确渲染,但拆分确实会以缩放级别300显示。

这就是为什么我说我的解决办法在所有情况下都不起作用 - 如果您只需要以固定大小显示内容,UseLayoutRounding可能会隐藏该大小的问题,但如果您有缩放功能,你的应用程序,你有时可能会遇到问题。

来自Microsoft Forums,这是ATI对此问题所说的话:

  

根本原因是WPF 4.0将使用DX10样式纹理坐标寻址在使用DX9 API时使用点过滤器(Round to nearest)呈现文本。我们遵循DX9规则(截断到最近),因为这是DX9驱动程序。如果我们强制我们的HW使用DX10样式纹理坐标使用,问题就会消失。但是,Ithink微软应该遵循自己的规则,在使用DX9 API时使用DX9风格的纹理坐标。“

如果您也可以重现此问题,请将您的投票添加到issue I've added on Connect,以便我们可以修复此问题 - 尽管我没有太多希望;微软通过说他们认为暂时可以接受的解决办法来回复similar issue

答案 1 :(得分:3)

当WPF向处于部分像素偏移的树渲染效果时,会出现核心问题。我们可以为硬件路径做两种基本方法:

1)创建与屏幕像素对应的中间纹理,并在该纹理内的部分像素偏移处渲染树。然后,当将中间值传回屏幕时,我们可以使用像素对齐的四边形并使用简单的最近邻点采样,因为内容是像素对齐的。

2)创建一个与树的边界对应的中间纹理,并在该纹理内以0偏移渲染树。然后,当将中间体传回屏幕时,我们需要使用覆盖部分像素的四边形。由于四边形可以覆盖部分像素,因此采样模式很重要。像NearestNeighbor这样的采样模式可能会导致可能与GPU相关的锯齿伪像。

WPF选择#2并强制进行最近邻采样 - 除非具有除偏移或缩放之外的任何变换的树(最常见的是旋转),在这种情况下它强制进行双线性采样。这与软件光栅化路径略有不同,软件光栅化路径将使用对树有效的插值模式,除非树具有偏移或缩放以外的变换,在这种情况下它也强制进行双线性采样。

我们通过使硬件路径与软件路径达成一致来“修复”此问题。这意味着我们将尊重对正在渲染的树有效的采样模式。这可以使用RenderOptions.BitmapScalingMode附加属性从托管代码进行设置。默认值为线性,当四边形覆盖部分像素时,这是一种更适应的采样模式。但是,它确实引入了明显的模糊性 - 但与软件光栅化产生的相同。

对于4.0,有几种解决方法:

1)防止元素树位于部分像素处。例如,您可以在使用效果定位元素的容器上将UseLayoutRounding属性设置为true。

2)应用小旋转以使渲染路径使用双线性插值。这可能非常小,例如具有<RotateTransform Angle="0.00000000001"/>

的RenderTransform

请注意,自定义ShaderEffects使用具有可配置采样模式的显式采样器。

客户将能够通过将RenderOptions.BitmapScalingMode附加属性设置为“NearestNeighbor”来获取当前行为。请注意,如果旋转树,即使这将被忽略,也将使用双线性采样。在某些GPU上,使用NearestNeighbor采样时,像DropShadow这样的效果仍会遇到锯齿现象。

另请注意,将效果应用于图像时,相同的RenderOptions.BitmapScalingMode附加属性值将用于图像内容和效果中间纹理。如果您想要使用NearestNeighbor采样的图像,但使用线性采样的效果,则需要将效果应用于图像的容器,例如包含图像的简单画布。

答案 2 :(得分:0)

尝试按如下方式排列嵌套:

<StackPanel Orientation="Horizontal">
   <StackPanel.Resources>
      ...
   </StackPanel.Resources>
   <Button>Normal</Button>
   <Button IsDefault="True">IsDefault</Button>
</StackPanel>

答案 3 :(得分:0)

如果是设计师,那么你可以做的事情就不多了。对角线看起来很像将矩形分成两个三角形,以便在GPU上渲染是错误的。这可能是由设计人员或显卡驱动程序引起的。这可能是一个简单的舍入错误。

它是否显示设计师的所有缩放级别?

答案 4 :(得分:0)

我找到了问题的解决方法。这种解决方法毫无意义,但似乎会使拼接效果消失。

简而言之,我的应用是一个标签式界面(MDI)。我有一个样式的tabcontrol,它托管我的应用程序中的所有其他控件。在标签栏中,我有一个样式按钮,可以创建一个新标签(与拼接按钮的风格不同)。

如果我从“添加标签”按钮中删除专家样式,将其保留为默认样式,拼接效果将远离我应用中的其他按钮。两个按钮样式都保存在同一个ResourceDictionary中,该ResourceDictionary合并到我的app.xaml ResourceDictionary中。也许这是一个线索,但我无法弄清楚它的意义。

此外,如果我在“添加标签”按钮之前将带有样式的标签插入标签条,则无论“添加标签按钮”的样式如何,拼接效果都会消失。如果我只添加没有命名样式的标签,则会返回拼接效果。

标签或添加标签按钮似乎都没有获得拼接效果。但是,更改其样式会控制拼接效果是否出现在其他按钮上。

我只能断定这是WPF中的一个错误,因为我无法想到为什么更改一个控件上的样式会对其他控件的渲染产生任何影响的正当理由。