样式和绑定行为奇怪

时间:2013-07-03 21:42:19

标签: c# wpf binding styles relativesource

右。我有一个小程序(复制我的问题)。基本上,它尝试绑定到它的样式对象的某些属性。 种类有效:它为我提供了默认值(来自依赖属性)。我一直在想这可能是因为Style的{​​{1}}与RelativeSource Self的造型不一样。但我不知道。我已经尝试过调试它,一次又一次地检查XAML中设置的值是否实际设置了。问题是,它有一个较小的测试程序。这只是一个扩大规模。我不知道出了什么问题。 谢谢!

重现此问题的代码:

MainWindow.xaml

TextBox

MainWindow.xaml.cs

<Window x:Class="MyNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:lcl="clr-namespace:MyNamespace"
        Title="My title." Height="350" Width="425" MaxHeight="350" MaxWidth="425" MinHeight="350" MinWidth="425">
    <Window.Resources>
        <ResourceDictionary Source="TestDictionary.xaml"/>
    </Window.Resources>
    <Grid>
        <TextBox Style="{StaticResource TextBoxWithDefault}" FontSize="36" lcl:MyOptions.Default="Not default." VerticalAlignment="Center"/>
    </Grid>
</Window>

TestDictionary.xaml

using System.Windows;

namespace MyNamespace
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
    public static class MyOptions
    {
        public static string GetDefault(DependencyObject obj)
        {
            return (string)obj.GetValue(DefaultProperty);
        }
        public static void SetDefault(DependencyObject obj, string value)
        {
            obj.SetValue(DefaultProperty, value);
        }
        public static readonly DependencyProperty DefaultProperty =
            DependencyProperty.RegisterAttached(
                "Default",
                typeof(string),
                typeof(MyOptions),
                new PropertyMetadata("Default"));
    }
}

我不知道这里出了什么问题,因为这个版本的缩小版完美无缺。我可能会忽略一些东西,当我找到它时会显得非常明显。但我现在找不到它。

编辑:好吧,我似乎是愚蠢的。原始版本(here)使用<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:lcl="clr-namespace:MyNamespace" xmlns:sys="clr-namespace:System;assembly=mscorlib"> <Style TargetType="TextBox" x:Key="TextBoxWithDefault"> <Style.Resources> <Label Content="{Binding Path=(lcl:MyOptions.Default), Mode=TwoWay, RelativeSource={RelativeSource Self}}" Foreground="LightGray" FontSize="{Binding Path=(FontSize), Mode=TwoWay, RelativeSource={RelativeSource Self}}" x:Key="TheLabel"/> </Style.Resources> <Style.Triggers> <Trigger Property="Text" Value="{x:Static sys:String.Empty}"> <Setter Property="Background"> <Setter.Value> <VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="None" Visual="{DynamicResource TheLabel}"/> </Setter.Value> </Setter> </Trigger> <Trigger Property="Text" Value="{x:Null}"> <Setter Property="Background"> <Setter.Value> <VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="None" Visual="{DynamicResource TheLabel}"/> </Setter.Value> </Setter> </Trigger> <Trigger Property="IsKeyboardFocused" Value="True"> <Setter Property="Background" Value="White"/> </Trigger> </Style.Triggers> </Style> </ResourceDictionary> ,这意味着它获取父文本框的值。现在的问题是:我怎样才能让它发挥作用?

谢谢你的时间!

1 个答案:

答案 0 :(得分:2)

这里的真正展示是,当您在Label中使用VisualBrush时,该标签不属于TextBox'"Visual Tree"的一部分(请参阅参考资料)示例Sheldon Xiao对MSDN上这个类似问题的回答:Binding Problem inside VisualBrush)。

这意味着标签不会继承文本框“DataContext,也无法从RelativeSource绑定到达文本框。相比之下,您在其他帖子中接受的答案设置了按钮的实际内容, 使内容成为按钮可视树的一部分。

所以我认为没有针对此问题的纯XAML解决方案 - 将正确的MyOptions.Default从文本框推送到标签。一种可能的基于代码的解决方案是取消TextBoxWithDefault样式,并在Default更改时从附加属性执行所有操作:

...
public static readonly DependencyProperty DefaultProperty =
    DependencyProperty.RegisterAttached(
        "Default",
        typeof(string),
        typeof(MyOptions),

        //Listen for changes in "Default":
        new PropertyMetadata(null, OnMyDefaultChanged));

private static void OnMyDefaultChanged(DependencyObject sender, 
                                       DependencyPropertyChangedEventArgs e)
{
    var text = (TextBox)sender;
    var myDefault = e.NewValue;

    var defaultLabel = new Label();
    defaultLabel.Foreground = Brushes.LightGray;

    //Explicitly bind the needed value from the TextBox:
    defaultLabel.SetBinding(Label.ContentProperty,
                            new Binding()
                            {
                                Source = text,
                                Path = new PropertyPath(MyOptions.DefaultProperty)
                            });

    text.Background = new VisualBrush()
    {
        Visual = defaultLabel,
        AlignmentX = AlignmentX.Left,
        AlignmentY = AlignmentY.Center,
        Stretch = Stretch.None
    };
    text.TextChanged += new TextChangedEventHandler(OnTextWithDefaultChanged);
}

private static void OnTextWithDefaultChanged(object sender, 
                                             TextChangedEventArgs e)
{
    var text = (TextBox)sender;
    var defaultLabel = (text.Background as VisualBrush).Visual as Label;

    defaultLabel.Visibility = string.IsNullOrEmpty(text.Text) ? 
                                                Visibility.Visible : 
                                                Visibility.Collapsed;
}