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

时间:2016-07-08 14:34:54

标签: c# wpf xaml dependency-properties

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

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;

2 个答案:

答案 0 :(得分:1)

您是否考虑过使用自定义转换器?它将转换依赖项属性的值,但是您定义了它。您的案例中的一个例子可能是:

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会将bool值显示为有意义的内容。

编辑:我写过这个解决方案,想要将它绑定到TextBox,而不是TextBlock。如果您只是显示值,则不需要在ConvertBack方法中定义任何内容,只需抛出NotImplementedException。

答案 1 :(得分:0)

这是你的解决方案;

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