具有自定义属性(类)的 XAML 依赖项属性

本文关键字:XAML 依赖 属性 自定义属性 | 更新日期: 2023-09-27 17:55:57

>目前我有一个布尔依赖属性,它按以下方式绑定:

public bool Status
{
    get { return (bool)GetValue(StatusProperty); }
    set { SetValue(StatusProperty, value); }
}
public static readonly DependencyProperty StatusProperty =
        DependencyProperty.Register("Status", typeof(bool),
        typeof(UIButton), new PropertyMetadata(false));

我在这样的触发器中使用。

....
<Condition Binding="{Binding Path=Status}" Value="True" />
....

一切正常,但现在我想将这个布尔依赖属性扩展到类似的东西:

public class State : DependencyObject
{
    public bool? status
    {
        get { return (bool?)GetValue(statusProperty); }
        set { SetValue(statusProperty, value); }
    }
    public static readonly DependencyProperty statusProperty =
        DependencyProperty.Register("status", typeof(bool?),
        typeof(UIButton), new PropertyMetadata(false));
    public State()
    { 
    }
    public State(bool status)
    {
        this.status = status;
    }
    public override string ToString()
    {
        if (status)
            return "activated"; // this strings change on runtime, depending on user language.. simplified for demonstration purpose
        else
            return "deactivated"; // this strings change on runtime, depending on user language.. simplified for demonstration purpose
    }
}

我的目标是,重写布尔对象的 ToString 方法,或者换句话说,如果我将此依赖项属性绑定到这样的文本框,请添加自定义文本

<TextBlock Text="{Binding Path=Status.status}"/>

但仍然可以在我的触发器中使用它,如上所示。

使用当前代码,我正在收到 XAML 解析异常...不确定此构造是否有效。我认为不是。 :P

关于如何实现此类功能的任何想法?

谢谢。

编辑

目前我正在使用这个解决方案:

public class State
{
    public bool? status { get; set; }
    public string statusString {
        get {
            if (status == true)
                return "activated";
            else if (status == false)
                return "deactivated";
            else
                return "";
            }
        set {}
    }
    public State()
    { 
    }
    public State(bool? status)
    {
        this.status = status;
    }
}

具有以下绑定:

<TextBlock Text="{Binding Path=Status.statusString}"/>

<Condition Binding="{Binding Path=Status.status}" Value="False" />

这工作得很好,唯一的缺点是,在更改状态时,我需要完全交换整个对象。

myState = new State(false);

而不是像...

myState.status = false;

具有自定义属性(类)的 XAML 依赖项属性

您是否考虑为此使用自定义转换器?它将以您定义的方式转换依赖项属性的值。您案例中的一个例子可能是:

public class StatusToTextConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            bool? b = (bool?)value;
            if (b == true) return "activated";
            else if (b == false) return "deactivated";
            else return "not set";
        }
        catch (Exception)
        {
            return "invalid";
        }
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            bool? returnValue = null;
            string s = (string)value;
            if (s == "activated") returnValue = true;
            else if (s == "deactivated") returnValue = false;
            return returnValue;
        }
        catch (Exception)
        {
            return null;
        }
    }
}

然后,在 XAML 中将 StatusToTextConverter 定义为资源,并在 TextBlock 上的绑定表达式中使用该转换器:

<UserControl.Resources>
    <local:StatusToTextConverter x:Key="statusToTextConverter"/>
</UserControl.Resources>
<TextBlock Text="{Binding Path=Status, Converter="{StaticResource statusToTextConverter"}"/>

这样,触发条件仍将绑定到 bool 属性,但 TextBlock 会将布尔值显示为有意义的值。

编辑:我写了那个解决方案,认为你想把它绑定到文本框,而不是文本块。如果只是显示值,则无需在 ConvertBack 方法中定义任何内容,只需抛出 NotImplementException。

这是您的解决方案;

public class State : UIElement
{
    public State()
    {
    }
    private bool _status;
    public bool Status
    {
        get { return (bool)GetValue(StatusProperty); }
        set
        {
            SetValue(StatusProperty, value);
            if (value != _status)
            {
                _status = value;
                if (value)
                {
                    this.StatusText = "Activated";
                }
                else
                {
                    this.StatusText = "Deactivated";
                }

                RaiseStatusChangedEvent();
            }

        }
    }
    // Using a DependencyProperty as the backing store for Status.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty StatusProperty =
        DependencyProperty.Register("Status", typeof(bool), typeof(State), new PropertyMetadata(InternStatusChanged));

    static void InternStatusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Nullable<bool> value = e.NewValue as Nullable<bool>;
        if (value.HasValue)
        {
            ((State)d).Status = value.Value;
        }

    }

    public string StatusText
    {
        get { return (string)GetValue(StatusTextProperty); }
        set { SetValue(StatusTextProperty, value); }
    }
    // Using a DependencyProperty as the backing store for StatusText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty StatusTextProperty =
        DependencyProperty.Register("StatusText", typeof(string), typeof(State), new PropertyMetadata(""));

    // Create a custom routed event by first registering a RoutedEventID 
    // This event uses the bubbling routing strategy 
    public static readonly RoutedEvent StatusChangedEvent = EventManager.RegisterRoutedEvent(
        "StatusChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(State));
    // Provide CLR accessors for the event 
    public event RoutedEventHandler StatusChanged
    {
        add { AddHandler(StatusChangedEvent, value); }
        remove { RemoveHandler(StatusChangedEvent, value); }
    }
    // This method raises the SelectedPathChanged event 
    void RaiseStatusChangedEvent()
    {
        RoutedEventArgs newEventArgs = new RoutedEventArgs(State.StatusChangedEvent);
        RaiseEvent(newEventArgs);
    }



}

测试:

public partial class MainWindow : Window
    {
        State state;
        public MainWindow()
        {
            InitializeComponent();
            state = new State();
            state.StatusChanged += new RoutedEventHandler(state_StatusChanged);
            state.Status = true;

        }
        void state_StatusChanged(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(this.state.StatusText); 
        }
    }