1

对。我有一个小程序(复制了我的问题)。基本上,它会尝试绑定到它的样式对象的某些属性。它有点工作:它给了我默认值(来自依赖属性)。我一直在想这可能是因为Style'sRelativeSource SelfTextBox它的样式不同。但我不知道。我试过调试这个,一次又一次地检查 XAML 中设置的值是否实际设置。问题是,它可以使用较小的测试程序。这只是一个扩大规模。我不知道出了什么问题。谢谢!

重现此问题的代码:

主窗口.xaml

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

主窗口.xaml.cs

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

测试字典.xaml

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

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

编辑:好吧,看来我很愚蠢。原始版本(此处)使用 a Trigger,这意味着它获取父文本框的值。现在的问题是:我怎样才能让它工作?

谢谢你的时间!

4

1 回答 1

2

这里真正的亮点是,当您使用Labelin aVisualBrush时,标签不是“视觉树”的一部分TextBox例如,请参见 Sheldon Xiao 在 MSDN 上对类似问题的回答: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;
}
于 2013-07-04T23:04:18.210 回答