0

I want to learn how to use Dependency Objects and Properties. I have created this class,

    public class TestDependency : DependencyObject
    {
        public static readonly DependencyProperty TestDateTimeProperty =
            DependencyProperty.Register("TestDateTime", 
            typeof(DateTime), 
            typeof(TestDependency), 
            new PropertyMetadata(DateTime.Now));

        public DateTime TestDateTime
        {
            get { return (DateTime) GetValue(TestDateTimeProperty); }
            set { SetValue(TestDateTimeProperty, value); }
        }
    }

The window class is like this

public partial class MainWindow : Window
{
    private TestDependency td;
    public MainWindow()
    {
        InitializeComponent();
        td = new TestDependency();
        td.TestDateTime = DateTime.Now;
    }
}

Now I want to use it to show a the current DateTime in the TextBlock which updates itself every second, by adding this to a grid

<Grid>
    <TextBlock Text="{Binding TestDateTime,ElementName=td}" Width="200" Height="200"/>
</Grid>

I can see the TextBlock, but there is no Date Time value in it at all. What am I doing wrong?

Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100
black eyed pea
  • 427
  • 1
  • 7
  • 16

2 Answers2

2

First of all if you want to update the display time once a second your going to need a timer to trigger an update. A DispatchTimer works works well for that.

public class TestDependency : DependencyObject
{
    public static readonly DependencyProperty TestDateTimeProperty =
        DependencyProperty.Register("TestDateTime", typeof(DateTime), typeof(TestDependency),
        new PropertyMetadata(DateTime.Now));

    DispatcherTimer timer;

    public TestDependency()
    {
        timer = new DispatcherTimer(new TimeSpan(0,0,1), DispatcherPriority.DataBind, new EventHandler(Callback), Application.Current.Dispatcher);
        timer.Start();

    }

    public DateTime TestDateTime
    {
        get { return (DateTime)GetValue(TestDateTimeProperty); }
        set { SetValue(TestDateTimeProperty, value); }
    }

    private void Callback(object ignore, EventArgs ex)
    {
        TestDateTime = DateTime.Now;
    }

}

Next we need to modify the XAML so it binds properly to the updated dependency object.

<Window.DataContext>
    <local:TestDependency/>
</Window.DataContext>
<Grid>
    <TextBlock Text="{Binding TestDateTime}" />
</Grid>

Since we set the DataContext in XAML you can actually delete all of the code behind code in the MainWindow constructor.

blaise
  • 307
  • 1
  • 5
  • Thank you very much, it works very well. As an aside can I ask u another question, how is it that we do not need to pass in the variable name "td"? I mean, the dependency object is declared in the Window object as private TestDependency td; , without specifically putting "td" in the binding, and just adding the type "TestDependency", does it bind to the private variable or does it construct its own instance? – black eyed pea Feb 02 '13 at 16:02
  • @blackeyedpea WPF will construct an instance of TestDependency and set it as the value of Window.DataContext. So the TextBlock will refer it and use its TestDateTime to set the Text of TextBlock. – Colin Feb 02 '13 at 16:09
  • Thats because all the databinding is handled in the XAML. It actually ignored everything you did in the window objects contructor. The magic databinding code in the XAML is the part. If you wanted to do it in the code behind you could do something like this.DataContext = td; the {Binding TestDateTime} part tries to grab a property named TestDateTime of whatever object is currently set as the DataContext. – blaise Feb 02 '13 at 16:11
  • @blaise Thanks, DataContext's kinda new to me, very interesting. – black eyed pea Feb 02 '13 at 16:28
0

If you just want to show some values in your TextBlock, you don't need a Dependency Object here. Try something like this:

public partial class MainWindow : Window
{
    public DateTime Test
    { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        this.Test = DateTime.Now;
    }
}

<Grid>
    <TextBlock Text="{Binding Path=Test,RelativeSource={RelativeSource AncestorType=Window,Mode=FindAncestor}}"></TextBlock>
</Grid>

Here I am not showing the code which can update the value every second. I just want to clarify that this is not the right situation to use Dependency Property. Of course you can use Dependency Property to do this. But Dependency Object and Dependency Property can offer you some extension functionality such as Data Binding. But it doesn't mean that you need to use a Dependency Object or Dependency Property as the source of the Data Binding.

Colin
  • 551
  • 2
  • 10
  • Ok, actually I'm doing this specifically to try out DependencyObject and DepdencyProperty. Your answer is very correct and nice – black eyed pea Feb 02 '13 at 16:06
  • I'm trying to convert blaise's code to bind to the private variable td using by changing the Binding Path, how do I set it to the private variable td? – black eyed pea Feb 02 '13 at 16:26
  • @blackeyedpea To my knowledge, I don't think you can set a private variable as the Binding Path. Or maybe you can refer to [this](http://www.codeproject.com/Articles/100175/Model-View-ViewModel-MVVM-Explained) for more information. – Colin Feb 02 '13 at 16:33
  • what if it's a public variable, can this work? – black eyed pea Feb 02 '13 at 16:36
  • ElementName is for binding to other elements declared in XAML by their x:Name, not for things declared in code. When you assign an x:Name in XAML you can also access that object by name in your code-behind. – John Bowen Feb 02 '13 at 16:47
  • @JohnBowen Oh cool. What kind of binding do I use to bind to a Source object declared in procedural code? I managed to perform the binding in code using Binding binding = new Binding(); binding.Source = td; binding.Path = new PropertyPath("TestDateTime"); tb.SetBinding(TextBlock.TextProperty, binding); but I wonder if this can be done in Xaml too? – black eyed pea Feb 02 '13 at 16:51
  • Normally you want to rely on the DataContext like in the answer from @blaise but you can also set that DataContext property to anything you want from code. – John Bowen Feb 02 '13 at 17:03
  • @JohnBowen I made a new question for [this] (http://stackoverflow.com/questions/14664269/wpf-what-kind-of-binding-should-i-use-to-associate-a-textbox-to-a-dependency-ob), can you please post there? Thank you – black eyed pea Feb 02 '13 at 17:10