2012-11-29 116 views
8

我試圖創建使用附加屬性文本框的背景提示文本標籤,但我不能化解的樣式資源的結合,文本標題:如何綁定到Style.Resource中的附加屬性?

樣式定義:

<Style x:Key="CueBannerTextBoxStyle" 
     TargetType="TextBox"> 
    <Style.Resources> 
    <VisualBrush x:Key="CueBannerBrush" 
       AlignmentX="Left" 
       AlignmentY="Center" 
       Stretch="None"> 
     <VisualBrush.Visual> 
     <Label Content="{Binding Path=(EnhancedControls:CueBannerTextBox.Caption), RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" 
       Foreground="LightGray" 
       Background="White" 
       Width="200" /> 
     </VisualBrush.Visual> 
    </VisualBrush> 
    </Style.Resources> 
    <Style.Triggers> 
    <Trigger Property="Text" 
      Value="{x:Static sys:String.Empty}"> 
     <Setter Property="Background" 
       Value="{DynamicResource CueBannerBrush}" /> 
    </Trigger> 
    <Trigger Property="Text" 
      Value="{x:Null}"> 
     <Setter Property="Background" 
       Value="{DynamicResource CueBannerBrush}" /> 
    </Trigger> 
    <Trigger Property="IsKeyboardFocused" 
      Value="True"> 
     <Setter Property="Background" 
       Value="White" /> 
    </Trigger> 
    </Style.Triggers> 
</Style> 

附加屬性:

public class CueBannerTextBox 
{ 
    public static String GetCaption(DependencyObject obj) 
    { 
     return (String)obj.GetValue(CaptionProperty); 
    } 

    public static void SetCaption(DependencyObject obj, String value) 
    { 
     obj.SetValue(CaptionProperty, value); 
    } 

    public static readonly DependencyProperty CaptionProperty = 
     DependencyProperty.RegisterAttached("Caption", typeof(String), typeof(CueBannerTextBox), new UIPropertyMetadata(null)); 
} 

用法:

<TextBox x:Name="txtProductInterfaceStorageId" 
       EnhancedControls:CueBannerTextBox.Caption="myCustomCaption" 
       Width="200" 
       Margin="5" 
       Style="{StaticResource CueBannerTextBoxStyle}" /> 

的想法是,你可以定義在視覺刷使用,當您創建文本框的文字提示,但我發現了一個綁定錯誤:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TextBox', AncestorLevel='1''. BindingExpression:Path=(0); DataItem=null; target element is 'Label' (Name=''); target property is 'Content' (type 'Object')

的代碼工作正常,如果我只是硬編碼的標籤。樣式中的內容屬性。

任何想法?

回答

1

這裏的問題有做的方式 Style作品:基本上,一個 Style的「複製」將(在第一參考)創建,並在這一點上,可能有多個 TextBox控制你想要這個 Style應用於 - 哪一個將用於RelativeSource?

的(可能的)答案是使用一個 Template代替 Style - 與控制或數據模板,你就可以訪問 TemplatedParent的可視化樹,並在你需要的是應該讓你。

編輯:在進一步的思考,我可能是不正確的......在這裏,當我回到電腦前我就一頓簡便的測試工具,看看我能證明/反駁這一點。

進一步編輯:雖然我最初說的可以說是「真實的」,那不是你的問題;什麼勞爾說,再次:可視樹是正確的:

  • 您在TextBoxBackground屬性設置爲VisualBrush實例。
  • 該筆刷的Visual而不是映射到控件的可視化樹中。
  • 因此,任何{RelativeSource FindAncestor}導航將失敗,因爲該視覺的父項將爲空。
  • 無論是聲明爲Style還是ControlTemplate,都是如此。
  • 所有這些,依靠ElementName肯定是不理想的,因爲它降低了定義的可重用性。

那麼,該怎麼辦?

我一直在試圖想方設法考慮一種適當的繼承上下文到包含的刷子的方式,一點點成功...我沒有想出這個超級哈克方式,但是:

首先,輔助屬性(注:我通常不會風格我這樣的代碼,而是試圖以節省空間):

public class HackyMess 
{ 
    public static String GetCaption(DependencyObject obj) 
    { 
     return (String)obj.GetValue(CaptionProperty); 
    } 

    public static void SetCaption(DependencyObject obj, String value) 
    { 
     Debug.WriteLine("obj '{0}' setting caption to '{1}'", obj, value); 
     obj.SetValue(CaptionProperty, value); 
    } 

    public static readonly DependencyProperty CaptionProperty = 
     DependencyProperty.RegisterAttached("Caption", typeof(String), typeof(HackyMess), 
      new FrameworkPropertyMetadata(null)); 

    public static object GetContext(DependencyObject obj) { return obj.GetValue(ContextProperty); } 
    public static void SetContext(DependencyObject obj, object value) { obj.SetValue(ContextProperty, value); } 

    public static void SetBackground(DependencyObject obj, Brush value) { obj.SetValue(BackgroundProperty, value); } 
    public static Brush GetBackground(DependencyObject obj) { return (Brush) obj.GetValue(BackgroundProperty); } 

    public static readonly DependencyProperty ContextProperty = DependencyProperty.RegisterAttached(
     "Context", typeof(object), typeof(HackyMess), 
     new FrameworkPropertyMetadata(default(HackyMess), FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior | FrameworkPropertyMetadataOptions.Inherits)); 
    public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached(
     "Background", typeof(Brush), typeof(HackyMess), 
     new UIPropertyMetadata(default(Brush), OnBackgroundChanged)); 

    private static void OnBackgroundChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
    { 
     var rawValue = args.NewValue; 
     if (rawValue is Brush) 
     { 
      var brush = rawValue as Brush; 
      var previousContext = obj.GetValue(ContextProperty); 
      if (previousContext != null && previousContext != DependencyProperty.UnsetValue) 
      { 
       if (brush is VisualBrush) 
       { 
        // If our hosted visual is a framework element, set it's data context to our inherited one 
        var currentVisual = (brush as VisualBrush).GetValue(VisualBrush.VisualProperty); 
        if(currentVisual is FrameworkElement) 
        { 
         (currentVisual as FrameworkElement).SetValue(FrameworkElement.DataContextProperty, previousContext); 
        } 
       } 
      } 
      // Why can't there be just *one* background property? *sigh* 
      if (obj is TextBlock) { obj.SetValue(TextBlock.BackgroundProperty, brush); } 
      else if (obj is Control) { obj.SetValue(Control.BackgroundProperty, brush); } 
      else if (obj is Panel) { obj.SetValue(Panel.BackgroundProperty, brush); } 
      else if (obj is Border) { obj.SetValue(Border.BackgroundProperty, brush); } 
     } 
    } 
} 

現在更新的XAML:

<Style x:Key="CueBannerTextBoxStyle" 
     TargetType="{x:Type TextBox}"> 
    <Style.Triggers> 
    <Trigger Property="TextBox.Text" 
      Value="{x:Static sys:String.Empty}"> 
     <Setter Property="local:HackyMess.Background"> 
     <Setter.Value> 
      <VisualBrush AlignmentX="Left" 
         AlignmentY="Center" 
         Stretch="None"> 
      <VisualBrush.Visual> 
       <Label Content="{Binding Path=(local:HackyMess.Caption)}" 
        Foreground="LightGray" 
        Background="White" 
        Width="200" /> 
      </VisualBrush.Visual> 
      </VisualBrush> 
     </Setter.Value> 
     </Setter> 
    </Trigger> 
    <Trigger Property="IsKeyboardFocused" 
      Value="True"> 
     <Setter Property="local:HackyMess.Background" 
       Value="White" /> 
    </Trigger> 
    </Style.Triggers> 
</Style> 
<TextBox x:Name="txtProductInterfaceStorageId" 
     local:HackyMess.Caption="myCustomCaption" 
     local:HackyMess.Context="{Binding RelativeSource={RelativeSource Self}}" 
     Width="200" 
     Margin="5" 
     Style="{StaticResource CueBannerTextBoxStyle}" /> 
<TextBox x:Name="txtProductInterfaceStorageId2" 
     local:HackyMess.Caption="myCustomCaption2" 
     local:HackyMess.Context="{Binding RelativeSource={RelativeSource Self}}" 
     Width="200" 
     Margin="5" 
     Style="{StaticResource CueBannerTextBoxStyle}" /> 
+0

好吧,我會試試 - 讓我知道你是否成功!謝謝 – glasswall

+0

歡呼的研究,我會給它一個旋轉.. – glasswall

2

的問題是,VisualBrushLabel不是TextBox的視覺孩子,這就是爲什麼有約束力不工作的原因。我對這個問題的解決方案將使用ElementName綁定。但是,您正在創建的視覺筆刷位於Style的字典資源中,因此ElementName綁定不起作用,因爲找不到元素ID。解決方案是在全球字典資源中創建VisualBrush。看到這個XAML代碼delcaring的VisualBrush

<Window.Resources> 
    <VisualBrush x:Key="CueBannerBrush" 
       AlignmentX="Left" 
       AlignmentY="Center" 
       Stretch="None"> 
    <VisualBrush.Visual> 
     <Label Content="{Binding Path=(EnhancedControls:CueBannerTextBox.Caption), ElementName=txtProductInterfaceStorageId}" 
      Foreground="#4F48DD" 
      Background="#B72121" 
      Width="200" 
      Height="200" /> 
    </VisualBrush.Visual> 
    </VisualBrush> 
    <Style x:Key="CueBannerTextBoxStyle" 
     TargetType="{x:Type TextBox}"> 
    <Style.Triggers> 
     <Trigger Property="Text" 
       Value="{x:Static System:String.Empty}"> 
     <Setter Property="Background" 
       Value="{DynamicResource CueBannerBrush}" /> 
     </Trigger> 
     <Trigger Property="Text" 
       Value="{x:Null}"> 
     <Setter Property="Background" 
       Value="{DynamicResource CueBannerBrush}" /> 
     </Trigger> 
     <Trigger Property="IsKeyboardFocused" 
       Value="True"> 
     <Setter Property="Background" 
       Value="White" /> 
     </Trigger> 
    </Style.Triggers> 
    </Style> 
</Window.Resources> 

此代碼應工作。沒有更多的代碼需要改變,所以我不重寫所有的代碼。

希望這個解決方案適合你...

+1

看起來它只是硬編碼的元素名稱的風格,但 - 這將如何重新使用?我只能有一個文本框使用此解決方案? – glasswall