使用View Model中的属性绑定到包含逻辑的User Control属性

时间:2011-05-06 11:56:36

标签: wpf binding mvvm-light

我正在使用WPF和MVVM构建应用程序。我遇到过一种情况,我有一个包含usercontrol(代表一个Timer)的视图。此usercontrol后面有一个属性代码,在获取和设置数据之前执行一些计算。

TimerControl.xaml.cs:

public DateTime? DateTimeValue
    {
        get
        {
            string hours = this.txtHours.Text;
            string minutes = this.txtMinutes.Text;
            string amPm = this.txtAmPm.Text;
            if (!string.IsNullOrWhiteSpace(hours) && !string.IsNullOrWhiteSpace(minutes) && !string.IsNullOrWhiteSpace(amPm))
            {
                string value = string.Format("{0}:{1} {2}", this.txtHours.Text, this.txtMinutes.Text, this.txtAmPm.Text);
                DateTime time = DateTime.Parse(value);                    
                return time;
            }
            else
            {
                return null;
            }
        }
        set
        {                
            DateTime? time = value;
            if (time.HasValue)
            {
                string timeString = time.Value.ToShortTimeString();
                //9:54 AM 
                string[] values = timeString.Split(':', ' ');
                if (values.Length == 3)
                {
                    this.txtHours.Text = values[0];
                    this.txtMinutes.Text = values[1];
                    this.txtAmPm.Text = values[2];
                }
            }                
        }
    }

现在我想将此属性绑定到视图的视图模型中的属性。以下是VM中的属性:

public DateTime? StartTime
{  
        get  
        {                
            return _StartTime;
        }

        set
        {
            _StartTime = value;
            RaisePropertyChanged("StartTime");
        }
} 

这就是我在View的xaml中执行绑定的方式。

MyView.xaml:

<my:TimeControl  Background="White" Grid.Column="1" Grid.Row="2" Margin="3" x:Name="StartTimeControl" DateTimeValue="{Binding StartTime}"  Width="150" Height="26" HorizontalAlignment="Left">

但它给了我一个错误: 无法在“TimeControl”类型的“DateTimeValue”属性上设置“绑定”。 '绑定'只能在DependencyObject的DependencyProperty上设置。

我一直在努力想要找到一种方法来实现这种绑定工作。我甚至试图在TimeControl的代码中为DateTimeValue属性创建一个依赖属性,该属性已经解决了上述异常,但绑定仍然不起作用。每当我在后面的VM代码中访问StartTime属性时,它都显示为null。虽然它应该通过获取DateTimeValue属性来显示有效值。

请建议我一种方法来完成这项工作。谢谢。

2 个答案:

答案 0 :(得分:2)

你想要做什么?

您无法绑定到标准属性。如果要绑定,则应使用依赖项属性。

public DateTime? DateTimeValue
{
    get { return (DateTime?)GetValue(DateTimeValueProperty); }
    set { SetValue(DateTimeValueProperty, value); }
}

// Using a DependencyProperty as the backing store for DateTimeValue.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty DateTimeValueProperty =
    DependencyProperty.Register("DateTimeValue", typeof(DateTime?), typeof(TimeControl), new UIPropertyMetadata(null));

在UserControl中:

<TextBox Text="{Binding DateTimeValue,RelativeSource={RelativeSource AncestorLevel=1, Mode=FindAncestor,AncestorType=UserControl}, Converter=...}" />

无法直接绑定到DateTimeValue,因为没有可用于string-&gt; DateTime的转换器,因此您必须编写IValueConverter并在绑定中指定它。

当然,你应该能够直接绑定值。

答案 1 :(得分:2)

此问题中显示的DateTimeValue属性的实现肯定是错误的并导致异常,因为DateTimeValue应该是依赖属性。

但是你提到你试图使用依赖属性但没有成功。我想原因是DataContexts碰撞,你的XAML看起来像这样:

<UserControl x:Class="Test.SomeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:self="clr-namespace:Test"
             Name="Root">
    <WrapPanel>
        <self:TimerControl Time="{Binding StartTime}"/>
    </WrapPanel>
</UserControl>

此代码不起作用。为什么? TimerControl的DataContext是继承的(或者你可以替换它),同时当你解决StartTime时,你会想到ViewModel作为DataContext。所以你应该清楚地指出正确的DataContext:

 <self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/> 

===更新===

我的Timer控件的整个代码(正如你可以看到我的Timer有文本框,当你输入一些文本时,textbox会引发相应的事件,我们处理并设置Time属性):

public partial class Timer : UserControl
{
    public Timer()
    {
        InitializeComponent();
    }

    public DateTime? Time
    {
        get
        {
            return (DateTime?)this.GetValue(Timer.TimeProperty);
        }
        set
        {
            this.SetValue(Timer.TimeProperty, value);
        }
    }

    public static readonly DependencyProperty TimeProperty =
        DependencyProperty.Register("Time",
            typeof(DateTime?),
            typeof(Timer),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (d, e) => { }));

    private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (DateTime.Now.Ticks % 2 == 0)
        {
            this.Time = DateTime.Now;
        }
        else
        {
            this.Time = null;
        }
    }

}

和XAML:

<UserControl x:Class="Test.Timer">
    <Grid>
        <TextBox TextChanged="TextBox_TextChanged"/>
    </Grid>
</UserControl>

在XAML中使用时间控制:

<UserControl x:Class="Test.StartupView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:self="clr-namespace:Test"
             Name="Root">
    <WrapPanel>
        <self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/>
    </WrapPanel>
</UserControl> 

StartupView背后的代码:

    public StartupView()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
    }

ViewModel中的属性保持不变。在调试期间,每当我在Timer中更改文本时,都会触发StartTime属性的setter。