2

我目前正在 Silverlight 中构建一些自定义控件。我希望这些控件能够响应验证错误。我想做的是让我的控件周围有红色边框,就像默认的 Silverlight 控件一样。

我的理解是我需要将它添加到我的模板中:

<VisualStateManager.VisualStateGroups>
  <VisualStateGroup x:Name="ValidationStates">
    <VisualState x:Name="Valid"/>
    <VisualState x:Name="InvalidUnfocused">
      <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
          <DiscreteObjectKeyFrame KeyTime="0">
            <DiscreteObjectKeyFrame.Value>
              <Visibility>Visible</Visibility>
            </DiscreteObjectKeyFrame.Value>
          </DiscreteObjectKeyFrame>
        </ObjectAnimationUsingKeyFrames>
      </Storyboard>
    </VisualState>
    <VisualState x:Name="InvalidFocused">
      <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
          <DiscreteObjectKeyFrame KeyTime="0">
            <DiscreteObjectKeyFrame.Value>
              <Visibility>Visible</Visibility>
            </DiscreteObjectKeyFrame.Value>
          </DiscreteObjectKeyFrame>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsOpen">
          <DiscreteObjectKeyFrame KeyTime="0">
            <DiscreteObjectKeyFrame.Value>
              <sys:Boolean>True</sys:Boolean>
            </DiscreteObjectKeyFrame.Value>
          </DiscreteObjectKeyFrame>
        </ObjectAnimationUsingKeyFrames>
      </Storyboard>
    </VisualState>
  </VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="ValidationErrorElement" BorderThickness="1" CornerRadius="1" BorderBrush="#FFDB000C" Visibility="Collapsed">
  <ToolTipService.ToolTip>
    <ToolTip x:Name="validationTooltip" Template="{StaticResource ValidationToolTipTemplate}" Placement="Right"
              PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
              DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
      <ToolTip.Triggers>
        <EventTrigger RoutedEvent="Canvas.Loaded">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsHitTestVisible">
                  <DiscreteObjectKeyFrame KeyTime="0">
                    <DiscreteObjectKeyFrame.Value>
                      <sys:Boolean>true</sys:Boolean>
                    </DiscreteObjectKeyFrame.Value>
                  </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
      </ToolTip.Triggers>
    </ToolTip>
  </ToolTipService.ToolTip>
  <Grid Width="12" Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Background="Transparent">
    <Path Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C"/>
    <Path Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff"/>
  </Grid>
</Border>

<ControlTemplate x:Key="ValidationToolTipTemplate">
  <Grid x:Name="Root" Margin="5,0" RenderTransformOrigin="0,0" Opacity="0">
    <Grid.RenderTransform>
      <TranslateTransform x:Name="xform" X="-25"/>
    </Grid.RenderTransform>
    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup Name="OpenStates">
        <VisualStateGroup.Transitions>
          <VisualTransition GeneratedDuration="0"/>
          <VisualTransition To="Open" GeneratedDuration="0:0:0.2">
            <Storyboard>
              <DoubleAnimation Storyboard.TargetName="xform" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2">
                <DoubleAnimation.EasingFunction>
                  <BackEase Amplitude=".3" EasingMode="EaseOut"/>
                </DoubleAnimation.EasingFunction>
              </DoubleAnimation>
              <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2"/>
            </Storyboard>
          </VisualTransition>
        </VisualStateGroup.Transitions>
        <VisualState x:Name="Closed">
          <Storyboard>
            <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/>
          </Storyboard>
        </VisualState>
        <VisualState x:Name="Open">
          <Storyboard>
            <DoubleAnimation Storyboard.TargetName="xform" Storyboard.TargetProperty="X" To="0" Duration="0"/>
            <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
          </Storyboard>
        </VisualState>
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <Border Margin="4,4,-4,-4" Background="#052A2E31" CornerRadius="5"/>
    <Border Margin="3,3,-3,-3" Background="#152A2E31" CornerRadius="4"/>
    <Border Margin="2,2,-2,-2" Background="#252A2E31" CornerRadius="3"/>
    <Border Margin="1,1,-1,-1" Background="#352A2E31" CornerRadius="2"/>

    <Border Background="#FFDC000C" CornerRadius="2"/>
    <Border CornerRadius="2">
      <TextBlock
          UseLayoutRounding="false"
          Foreground="White" Margin="8,4,8,4" MaxWidth="250" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}"/>
    </Border>
  </Grid>
</ControlTemplate>

我需要为我创建的每个控件添加它(ValidationToolTipTemplate-ControlTemplate 除外)。

  1. 如何将验证样式添加到我的所有控件并且仍然是 DRY?
  2. 如果这不可能,这是否意味着当验证模板的样式发生变化时,我必须将已知 Universe 中所有控件的模板复制并粘贴到一个名为“generic.xaml”的文件中并相应地进行更改?

我使用 Microsoft API 的时间已经足够长,知道 (2) 可能是要走的路,但我想先确定一下。

4

1 回答 1

3

Silveright/WPF 的工作方式在这些情况下存在一些问题。您最终会重复 XAML,因为 ControlTemplates 是全部或全部您要么最终替换样式的模板,要么不替换。您不能像对待班级那样挑选和选择要覆盖的内容。

我将尝试回答您的具体问题,然后尝试提供一些有助于消除 XAML 文件中的冗余的广泛方法。

如何将验证样式添加到所有控件?

首先,获取该边框并通过提供 Key 属性使其成为 StaticResoruce。然后将其放入共享资源字典中。当您再次需要该样式时,您可以在模板中使用它,如下所示:

<ContentControl x:Name="ValidationErrorElement" Content={StaticResource MyBorderResource}" />

除了封装模板之外,您无能为力。就 Validation VisualStates 而言,它们已经尽可能紧凑。但是,您可以将多个模板组合成一个更大的模板。我喜欢用看起来像按钮的 ToggleButtons、Buttons 和 RadioButtons 来做到这一点。请参阅下面的第 2 点。

一般经验法则

  1. 将常用的 XAML 片段封装到模板化控件和控件模板中。例如,假设您在一个边界内有一个边界,您在几个不同的地方使用它。这可以描述为 ControlTemplate 资源并加载到 ContentControl 中。

  2. 如果您有几个基本相同的样式或控件模板(想到 Button、ToggleButton、RadioButton),那么在这种情况下,您可以将样式应用于共同的祖先ButtonBase 。您的新按钮 ControlTemplate 将包含 Button、ToggleButton 和 RadioButton 所需的所有 VisualStates 和图形元素,并应用于 ButtonBase 类型。如上所述,这种方法适用于外观非常相似的控件,但对于不太相似的控件来说是一个糟糕的选择。

于 2012-07-25T19:49:39.220 回答