如何绑定到Style.Resource中的附加属性



我试图使用附加的属性在TextBox的背景中创建一个提示文本标签,但我无法解决与样式资源中的文本标题的绑定:

样式定义:

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

如果我只是对样式中的Label.Content属性进行硬编码,代码就可以正常工作。

有什么想法吗?

这里的问题与Style的工作方式有关:基本上,Style的一个"副本"将被创建(在第一次引用时),此时,可能有多个TextBox控件需要应用此Style-它将使用哪个控件作为RelativeSource?

(可能的)答案是使用Template而不是Style-通过控制或数据模板,您将能够访问TemplatedParent的可视化树,这将使您到达需要的位置。

编辑:仔细想想,我在这里可能不正确。。。当我回到电脑前时,我会整理一个快速测试工具,看看我是否能证明/反驳这一点。

进一步编辑:虽然我最初说的可以说是"真的",但这不是你的问题;Raul说:视觉树是正确的:

  • 您正在将TextBox上的Background属性设置为VisualBrush实例
  • 该笔刷的Visual而不是映射到控件的可视化树中
  • 因此,任何{RelativeSource FindAncestor}导航都将失败,因为该视觉的父级将为null
  • 无论它被声明为Style还是ControlTemplate,都是这种情况
  • 尽管如此,依赖ElementName肯定是不理想的,因为它降低了定义的可重用性

那么,该怎么办?

一夜之间,我一直在绞尽脑汁,试图想出一种方法来将适当的继承上下文整理到包含的笔刷中,但收效甚微。。。然而,我确实想出了这个超级黑客的方法:

首先,helper属性(注意:我通常不会用这种方式设计代码,但为了节省空间):

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}" />

问题是VisualBrush中的Label不是TextBox的可视子级,这就是绑定不起作用的原因。我的解决方案是使用ElementName绑定。但是您正在创建的可视化画笔位于Style的字典资源中,然后ElementName绑定将不起作用,因为找不到元素id。解决方案是在全局字典资源中创建VisualBrush。有关VisualBrush:的建模,请参阅此XAML代码

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

这个代码应该可以工作。没有更多的代码需要更改,所以我不会重写所有的代码。

希望这个解决方案对你有效。。。

最新更新