2012-11-29 16 views
8

Próbuję utworzyć szybką etykietę tekstową w tle TextBox przy użyciu dołączonych właściwości, ale nie mogę rozwiązać wiązanie podpisie tekstu w zasobie styl:W jaki sposób powiązać załączoną właściwość w pliku Style.Resource?

definicja Styl:

<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> 

Dołączone nieruchomości:

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)); 
} 

Zastosowanie:

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

Chodzi o to, że można określić wiersz tekstowy używany w zaroślach wizualnej podczas tworzenia tekstowe, ale dostaję błąd Oprawa:

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')

kod działa dobrze, jeśli po prostu zakodować Label .Content Właściwość w stylu.

Wszelkie pomysły?

Odpowiedz

1

Problemem tutaj ma do czynienia ze sposobem Style utworów: w zasadzie, jeden „kopia” z Style zostaną utworzone (na pierwszy odnośnik), iw tym momencie, może być wiele TextBox kontrole chcesz to Style zastosowane do - którego z nich użyje dla RelativeSource?

The (prawdopodobne) odpowiedź jest użycie Template zamiast Style - ze sterowaniem lub szablonu danych, będziesz w stanie uzyskać dostęp do wizualnego drzewa z TemplatedParent, i że powinien dostać cię tam, gdzie trzeba być .

EDYCJA: W przypadku dalszych rozważań, mogę być tu niepoprawny ... Wezmę krótką próbną uprząż, gdy znajdę się z powrotem przed komputerem i zobaczę, czy mogę to udowodnić/obalić.

DALSZA EDYCJA: Chociaż to, co pierwotnie powiedziałem, było prawdopodobnie "prawdziwe", to nie jest twój problem; Co powiedział Raul re: drzewo wizualny jest poprawne:

  • ustawiasz właściwość Background na TextBox do instancji VisualBrush.
  • Numer Visual tego pędzla jest , a nie odwzorowany na drzewo wizualne kontrolki.
  • W rezultacie nawigacja nie powiedzie się, ponieważ obiekt nadrzędny tego wizualnego będzie pusty.
  • Jest tak w przypadku, niezależnie od tego, czy jest zadeklarowany jako Style lub ControlTemplate.
  • Wszystko to powiedziawszy, poleganie na ElementName zdecydowanie nie jest idealne, ponieważ zmniejsza możliwość ponownego użycia definicji.

Co robić?

Ja niszczyłem mój mózg w ciągu nocy, próbując wymyślić sposób, w jaki można poprowadzić kontekst właściwego dziedziczenia do zawartego pędzla, z małym sukcesem ...I nie pochodzić z tego super Hacky sposób, jednakże:

pierwsze, obiekt pomocnika (Uwaga: Zwykle nie mój styl Kod ten sposób, ale starają się zaoszczędzić miejsce):

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); } 
     } 
    } 
} 

A teraz zaktualizowane 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

Dobra, spróbuję - daj mi znać, jeśli Ci się uda! Dzięki – glasswall

+0

Pozdrawiam za badania, dam mu wir .. – glasswall

2

problemem jest to, że Label wewnątrz VisualBrush nie jest wizualnym dzieckiem TextBox, że jest powód, że wiązanie nie działa. Moim rozwiązaniem tego problemu będzie użycie wiązania ElementName. Ale pędzel wizualny, który tworzysz, znajduje się wewnątrz zasobu słownika Style, a następnie powiązanie ElementName nie będzie działać, ponieważ nie znajdzie identyfikatora elementu. Rozwiązaniem tego będzie utworzenie VisualBrush w zasobach globalnego słownika. Zobacz ten kod XAML dla delcaring się 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> 

Ten kod powinien dzieła. Nie trzeba już zmieniać kodu, więc nie przepisuję całego kodu.

Mam nadzieję, że to rozwiązanie zadziała dla Ciebie ...

+1

Wygląda na to, że po prostu koduje nazwę elementu w stylu - w jaki sposób byłoby to możliwe do ponownego wykorzystania? Mogę mieć tylko jeden textbox za pomocą tego rozwiązania? – glasswall

Powiązane problemy