【问题标题】:WPF Style Triggers in DataTemplateDataTemplate 中的 WPF 样式触发器
【发布时间】:2011-08-22 09:51:36
【问题描述】:

我希望你能帮助我。我在资源中得到了以下代码:

<UserControl.Resources>
  <BitmapImage x:Key="img_src_lock" UriSource="/EEBase;component/Images/Lock_24x32.png" />
  <BitmapImage x:Key="img_src_unlock" UriSource="/EEBase;component/Images/Unlock_24x32.png" />
  <Style TargetType="{x:Type ToggleButton}">
    <Style.Triggers>
      <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True">
        <Setter Property="Content">
          <Setter.Value>
            <Image Source="{StaticResource img_src_lock}" />
          </Setter.Value>
        </Setter>
      </DataTrigger>
      <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False">
        <Setter Property="Content">
          <Setter.Value>
            <Image Source="{StaticResource img_src_unlock}" />
          </Setter.Value>
        </Setter>
      </DataTrigger>
    </Style.Triggers>
  </Style>
  <!-- TypeComboTemplateCollapsed -->
  <DataTemplate x:Key="TypeComboTemplateCollapsed">
    <TextBlock 
      Text="{Binding Path=Text, Mode=OneWay}" 
      VerticalAlignment="Center"
      HorizontalAlignment="Left"
      Margin="5,0,0,5"
      />
  </DataTemplate>
  <!-- TypeComboTemplateExpanded -->
  <DataTemplate x:Key="TypeComboTemplateExpanded">
    <TextBlock 
      Text="{Binding Path=Text, Mode=OneWay}" 
      VerticalAlignment="Center"
      Margin="5,0,0,5"
      />
  </DataTemplate>
  <!-- EditCircleTemplate -->
  <DataTemplate x:Key="EditCircleTemplate">
    <!-- some content here, no ToggleButton -->
  </DataTemplate>
  <!-- EditRectangleTemplate -->
  <DataTemplate x:Key="EditRectangleTemplate">
    <!-- some other content here, including the ToggleButtons -->
    <ToggleButton
      IsChecked="{Binding Path=BaseLocked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
      Margin="5"
      />
    <ToggleButton
      IsChecked="{Binding Path=HeightLocked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
      Margin="5"
      />
  </DataTemplate>
</UserControl.Resources>

在我看来这一切都是正确的。

现在,问题是: 当我执行以下操作时,会发生异常:
指定元素已经是另一个元素的逻辑子元素。先断开连接。
1.加载控件,选择类型为CIRC
2. 更改下拉菜单以选择 RECT(模板触发器和切换按钮显示正确)
3. 将下拉菜单改回 CIRC
--> 现在发生异常。
4. 如果我忽略异常,则不会加载模板“EditCircleTemplate”,并显示模型对象的正常 ToString。

附加信息:
最初 WPF 中有 4 种不同的类型,其中两种带有 ToggleButtons(这就是我使用模板的原因)。我把它们删掉了,它们并没有真正的不同。但我发现使用所有 4 个模板时,在切换到新模板时不会发生错误,而是在 使用切换按钮卸载模板时发生错误。这有点奇怪。 此外,如果我删除 ToggleButtons 的 DataTriggers,一切都会像魅力一样工作。
异常来自 XAML 解释器,因此 Stacktrace 根本没有用。

谁能给我提示我做错了什么?

编辑: 哎呀,我想我忘记了内容代码:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="160"/>
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition />
    </Grid.RowDefinitions>
    <ComboBox 
        Margin="5"
        Grid.Row="0"
        Grid.Column="0"
        ItemsSource="{Binding Path=PossibleTypes, Mode=OneTime}"
        SelectedItem="{Binding Path=SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <ContentControl x:Name="content" Content="{Binding}" ContentTemplate="{StaticResource TypeComboTemplateExpanded}"/>
                </StackPanel>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBoxItem}}" Value="{x:Null}">
                        <Setter TargetName="content" Property="ContentTemplate" Value="{StaticResource TypeComboTemplateCollapsed}"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
    <ContentControl
        Grid.Column="0"
        Grid.Row="1"
        Grid.ColumnSpan="2"
        Content="{Binding Mode=OneWay}">
        <ContentControl.ContentTemplate>
            <DataTemplate >
                <ContentControl
                    Name="inputContent"
                    Content="{Binding Mode=OneWay}"
                    ContentTemplate="{x:Null}" 
                    />
                <DataTemplate.Triggers>
                    <DataTrigger 
                        Binding="{Binding Path=SelectedType.Type, Mode=OneWay}" 
                        Value="CIRC">
                        <Setter 
                            TargetName="inputContent" 
                            Property="ContentTemplate" 
                            Value="{StaticResource EditCircleTemplate}"
                            />
                    </DataTrigger>
                    <DataTrigger 
                        Binding="{Binding Path=SelectedType.Type, Mode=OneWay}" 
                        Value="RECT">
                        <Setter 
                            TargetName="inputContent" 
                            Property="ContentTemplate" 
                            Value="{StaticResource EditRectangleTemplate}"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ContentControl.ContentTemplate>
    </ContentControl>
</Grid>

编辑2/解决方案:
我刚刚找到了一种解决方法 - 它只是不满足我:
我不必将样式放在 UserControl.Resources 中,这对我来说是更简洁直观的解决方案,我必须使用 每个 ToggleButton 上的触发器分别设置样式
所以删除并添加以下代码到每个 ToggleButton 就可以了:

<ToggleButton.Style>
  <Style TargetType="{x:Type ToggleButton}">
    <Style.Triggers>
      <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True" >
        <Setter Property="Content">
          <Setter.Value>
            <Image Source="{StaticResource img_src_lock}" />
          </Setter.Value>
        </Setter>
      </DataTrigger>
      <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False">
        <Setter Property="Content">
          <Setter.Value>
            <Image Source="{StaticResource img_src_unlock}" />
          </Setter.Value>
        </Setter>
      </DataTrigger>
    </Style.Triggers>
  </Style>
</ToggleButton.Style>

大问题仍然存在:为什么?

【问题讨论】:

    标签: wpf xaml data-binding triggers binding


    【解决方案1】:

    问题在于,如果您以一种样式创建Image,则只有一个实例,因此一旦多个控件使用该样式,就会为这个只能由一个拥有的实例发生争执控制。

    对此最简单的解决方案是将样式放在资源中并将x:Shared 设置为 false,这样无论在何处引用都会使用样式的副本。

    【讨论】:

    • 嗯,听起来很合理。为什么当我从带按钮的模板切换到不带按钮时也会发生异常?
    • @gelse:也许这会导致触发器触发,从而再次导致冲突。这可能发生在模板实际更改之前。
    【解决方案2】:

    为什么需要创建 BitmapImages 并将它们设置为触发器中内容图像的源?为什么不直接使用 URI 源来访问图像?

        <Style TargetType="{x:Type ToggleButton}"> 
          <Style.Triggers> 
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True"> 
              <Setter Property="Content"> 
                <Setter.Value> 
                  <Image Source="/EEBase;component/Images/Lock_24x32.png" /> 
                </Setter.Value> 
              </Setter> 
            </DataTrigger> 
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False"> 
              <Setter Property="Content"> 
                <Setter.Value> 
                  <Image Source="/EEBase;component/Images/Unlock_24x32.png" /> 
                </Setter.Value> 
              </Setter> 
            </DataTrigger> 
          </Style.Triggers> 
        </Style> 
    

    如果这有帮助,请告诉我。

    【讨论】:

    • 这不会改变任何事情。同样的错误。老实说,我不知道为什么我确实使用了 BitmapImages。我认为这是因为最初我希望 ImageSource 是可绑定的。所以我必须在模型上的 ImageSource 对象或 URI 的字符串之间进行选择,并使用 BitmapImages 加载它。这也意味着图像只加载到内存中一次。如果您使用 URI,我不确定是否是这种情况。
    • 啊 - 现在我很确定问题是因为框架尝试设置 TRIGGER 两次。我不知道这是否可能以及为什么会引发错误,但这对我来说似乎是最好的解释。
    猜你喜欢
    • 2010-10-15
    • 1970-01-01
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 2012-06-02
    相关资源
    最近更新 更多