2012-12-13 50 views
1

我有兩種基本相同的數據模板在解決隱式樣式資源方式方面表現完全不同的情況。這種不一致使得我正在處理的大型應用程序難以處理應用程序範圍的資源。通過數據模板解決WPF合併資源不一致

情景。

我在名爲AppStyles.xaml的獨立xaml文件中有一個ResourceDictionary。它爲Button和TextBlock類定義了一個隱式樣式。

<!-- AppStyles.xaml --> 
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 

    <Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}"> 
     <Setter Property="Padding" Value="10"/> 
     <Setter Property="Background" Value="Red"/> 
    </Style> 

    <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> 
     <Setter Property="Width" Value="300"/> 
    </Style> 

</ResourceDictionary> 

我的MainWindow.xaml文件將AppStyles.xaml合併到它自己的資源中。 MainWindow.xaml包含兩個ContentPresenter,每個ContentPresenter都綁定到一個簡單的viewmodel類。第一個ContentPresenter使用的數據模板是在MainWindow.xaml中內聯聲明的UserControl,第二個ContentPresenter使用的數據模板也是內聯聲明的,但引用在單獨文件中定義的UserControl。兩個數據模板使用的UserControl的實際聲明在其他方面是相同的。

<!-- MainWindow.xaml --> 
<Window x:Class="Demo2.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:Demo2" 
     Title="MainWindow" Height="350" Width="525" 
     x:Name="_this"> 

    <Window.Resources> 
     <ResourceDictionary> 

      <ResourceDictionary.MergedDictionaries> 
       <ResourceDictionary Source="/AppStyles.xaml"/> 
      </ResourceDictionary.MergedDictionaries> 

      <DataTemplate DataType="{x:Type local:TemplateVm1}"> 
       <UserControl> 
        <StackPanel> 
         <TextBlock Text="{Binding TextBlockValue}"/> 
         <Button Content="{Binding ButtonValue}"/> 
        </StackPanel> 
       </UserControl> 
      </DataTemplate> 

      <DataTemplate DataType="{x:Type local:TemplateVm2}"> 
       <local:UserControl1/> 
      </DataTemplate> 

     </ResourceDictionary> 
    </Window.Resources> 


    <StackPanel> 

     <ContentPresenter Content="{Binding ElementName=_this, Path=TemplateVm1}"/> 

     <ContentPresenter Content="{Binding ElementName=_this, Path=TemplateVm2}"/> 

    </StackPanel> 
</Window> 

問題。

問題是兩個ContentPresenter呈現完全不同! ContentPresenters都使用AppStyles.xaml中的樣式呈現Button,但第一個ContentPresenter不應用隱式TextBlock樣式,而第二個則會應用。

我的期望是TextBlock樣式不會應用於由ContentPresenter顯示的模板,這是由於WPF行爲,只有從Control派生的組件纔會在當前模板之外查看資源,並且TextBlock不會從控制)。

那麼這裏發生了什麼,我該如何讓它表現得一貫?

爲了完整的例子,這裏是viewmodels和MainWindow代碼的實現,以及第二個模板中使用的UserControl。

// TemplateVm.cs 
namespace Demo2 
{ 
    public class TemplateVm1 
    { 
     public TemplateVm1() 
     { 
      TextBlockValue = "TextBlock in ContentPresenter1."; 
      ButtonValue = "Button in ContentPresenter1"; 
     } 

     public string TextBlockValue { get; private set; } 
     public string ButtonValue { get; private set; } 
    } 

    public class TemplateVm2 
    { 
     public TemplateVm2() 
     { 
      TextBlockValue = "TextBlock in ContentPresenter2."; 
      ButtonValue = "Button in ContentPresenter2"; 
     } 

     public string TextBlockValue { get; private set; } 
     public string ButtonValue { get; private set; } 
    } 
} 
// MainWindow.xaml.cs 
namespace Demo2 
{ 
    using System.Windows; 

    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      TemplateVm1 = new TemplateVm1(); 
      TemplateVm2 = new TemplateVm2(); 
      InitializeComponent(); 
     } 

     public TemplateVm1 TemplateVm1 { get; private set; } 
     public TemplateVm2 TemplateVm2 { get; private set; } 
    } 
} 
<!-- UserControl1.xaml --> 
<UserControl x:Class="Demo2.UserControl1" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> 

    <StackPanel> 
     <TextBlock Text="{Binding TextBlockValue}"/> 
     <Button Content="{Binding ButtonValue}"/> 
    </StackPanel> 

</UserControl> 
+0

嗯,我也有點困惑,爲什麼這種情況正在發生,但是當你合併您AppStyles進入App.xaml中你應該得到一個一致的行爲。如果這不起作用,一種方法是在UserControl中合併xaml,但我強烈建議不要這種解決方案。你真的確定使用了一種風格,而另一種風格是不是可以通過設置背景色等更多有意義的屬性來確認? – dowhilefor

+0

合併到App.xaml中的資源適用於其他地方合併的資源,因爲合併到App.xaml中的任何資源都適用於絕對一切。所以是的,我可以將AppStyles.xaml放入App.xaml,然後將TextBlock樣式應用於兩個模板。但由於應用App.xaml資源的方式不同,因此TextBlock樣式也將應用於Button中(因爲它們在默認ControlTemplates中具有TextBlocks),這是最不可取的,因爲我只希望應用樣式應用應用程序聲明的控件。 – Neutrino

+0

我非常確定,在第一個模板的情況下,應用了Button的樣式,而TextBlock的樣式未應用。我已經用寬度和背景屬性對它進行了測試。在第二個模板的情況下,Button和TextBlock都被應用。 – Neutrino

回答

1

如果我沒有記錯,WPF認爲ControlTemplates是一個boundry,而不會套用模板裏面隱含的風格。

但是這個規則有一個例外:從Control繼承的任何東西都會應用隱式樣式。

由於Button繼承自Control,它應用了隱式樣式。但是TextBlock繼承自FrameworkElement而不是Control,因此它不會自動應用隱式樣式,而必須手動添加它。

你應該發現,如果您打開TextBlockLabel,隱式風格會得到Control

應用,因爲Label繼承作爲另一種選擇,我想你可以通過創建另一個隱含的手動應用隱含TextBlock風格里面TextBlock風格的UserControl.Resources

<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type FrameworkElement}}" /> 
+0

這正是我的想法,但是這並不回答爲什麼隱式TextBlock樣式應用於一個模板而不是另一個模板的問題? 在不應用TextBlock樣式的第一個模板中,我可以手動重新定義確實會導致其應用的樣式。但是,重新定義需要應用的樣式是容易出錯的,並且在某些可重複使用的文件中可能會破壞應用程序定義其所有樣式的要點。 – Neutrino

+0

但是,我可以使用標籤的建議是一個好主意,可能會很好地解決問題。 也許這就是打算使用這兩個控件的方式,標籤作爲客戶端應用程序的頂級UI控件,TextBlocks主要作爲不應用應用程序級別樣式的自定義控件的子組件。嘿,另一天,另一個無證WPF細微差別學到了:) – Neutrino