XAML 具有自定义属性的 DependencyProperty (class)

XAML DependencyProperty with custom properties (class)

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

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;

这是您的解决方案;

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

您是否考虑过为此使用自定义转换器?无论您如何定义,它都会转换依赖项 属性 的值。您的例子可能是:

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

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

编辑:我编写该解决方案时认为您想将其绑定到 TextBox,而不是 TextBlock。如果您只是显示值,则无需在 ConvertBack 方法中定义任何内容,只需抛出一个 NotImplementedException。