簡體   English   中英

ContentPresenter會竊取資源嗎?

[英]ContentPresenter steals resources?

在一個簡單的視圖中,我想顯示兩個按鈕,它們具有內容圖像,這些圖像是通過資源以App.xaml中加載的額外style.xaml提供的。

<BitmapImage x:Key="AddIcon"  UriSource="pack://application:,,,/WpfTestBench;component/Images/plus.png"></BitmapImage>

<BitmapImage x:Key="RemoveIcon"  UriSource="pack://application:,,,/WpfTestBench;component/Images/minus.png"></BitmapImage>

<Style x:Key="AddButtonWithIconStyle" TargetType="{x:Type Button}">
  <Setter Property="Content">
    <Setter.Value>
      <Image Source="{DynamicResource AddIcon}" Stretch="None" 
           VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </Setter.Value>
  </Setter>
</Style>

<Style x:Key="RemoveButtonWithIconStyle" TargetType="{x:Type Button}">
  <Setter Property="Content">
    <Setter.Value>
      <Image Source="{DynamicResource RemoveIcon}" Stretch="None" 
           VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </Setter.Value>
  </Setter>
</Style>

在我看來,我使用如下樣式:

<DockPanel Margin="0,0,5,0" DockPanel.Dock="Bottom" LastChildFill="False" >
   <Button Command="{Binding DeleteCommand}" DockPanel.Dock="Right"  
              Style="{StaticResource RemoveButtonWithIconStyle}"/>
   <Button Command="{Binding AddCommand}" DockPanel.Dock="Right" Margin="5,0" 
              Style="{StaticResource AddButtonWithIconStyle}"/>
</DockPanel>

到目前為止,這看起來不錯。 但是,當我通過ContentPresenter向該視圖中添加另一個具有基本相同內容(與上述按鈕樣式相同)的視圖時,(父視圖的)前兩個按鈕的顯示不再起作用。 我僅用按鈕功能就得到了兩個小圓圈。

<ContentPresenter Content="{Binding SomeEmbeddedViewModel, UpdateSourceTrigger=PropertyChanged}"/>

上面的列表加按鈕是父視圖的一部分,第二個列表和按鈕與內容演示者一起添加。

為什么會這樣? ContentPresenter是否會以某種方式阻止共享資源?

如另一個答案中所述,您應該在Button的ContentTemplate中具有Image控件。

用其Source屬性綁定到實際Content的Image聲明一個簡單的Image Button樣式:

<Style x:Key="ImageButtonStyle" TargetType="Button">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Image Source="{Binding}"/>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>
...
<Button Style="{StaticResource ImageButtonStyle}"
        Content="{DynamicResource RemoveIcon}" .../>

您也可以將樣式聲明為默認的按鈕樣式,可能在DockPanel內部:

<DockPanel>
    <DockPanel.Resources>
        <Style TargetType="Button">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Image Source="{Binding}" Stretch="None"/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </DockPanel.Resources>

    <Button Command="{Binding DeleteCommand}" DockPanel.Dock="Right"  
            Content="{DynamicResource RemoveIcon}"/>

    <Button Command="{Binding AddCommand}" DockPanel.Dock="Right" Margin="5,0" 
            Content="{DynamicResource AddIcon}"/>
</DockPanel>

診斷

此行為的核心原因是一個元素只能在視覺樹中出現一次。

首先,默認情況下, ResourceDictionary中的ResourceDictionary是共享的,即,每次使用特定鍵獲取資源時,您總是會得到相同的實例。 在您的情況下,您總是會得到相同的Style實例,這也意味着(每個樣式)總是只有一個Image實例。

因此,如果將相同的樣式應用於兩個按鈕,則框架會嘗試將相同的Image實例放置在兩個不同的位置,這是不允許的。 為避免這種情況,在嘗試將圖像加載到視覺樹中時,如果圖像已經在視覺樹中,則首先從先前的位置將其卸載。

這就是為什么僅在最后一個按鈕(按加載順序)中可見圖像的原因。

對於這個問題,基本上有兩種解決方案。

1.禁用資源共享

您可以禁用資源共享,以便每次從ResourceDictionary獲取ResourceDictionary都會獲得該資源的新實例。 為此,請在資源上設置x:Shared="False"

<Style x:Key="AddButtonWithIconStyle" x:Shared="False" TargetType="{x:Type Button}">
    (...)
</Style>

2.以您的樣式使用ContentTemplate

可以將ContentTemplate定義為ContentTemplate ,而不是將圖像作為按鈕的內容,而是將其分別應用於每個按鈕(因此每個按鈕都有自己的圖像實例):

<Style x:Key="RemoveButtonWithIconStyle" TargetType="{x:Type Button}">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Image Source="{DynamicResource RemoveIcon}" Stretch="None" 
                       VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

我個人建議您使用第二種解決方案,因為這正是WPF中模板的目的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM