Domanda

I'm learning about Dependency Properties.

I have created a Dependency Property in a UserControl and within my MainWindow, I create an instance of the control and set the value. This works as expected.

My issues is when I try to use Binding.

So, my MainWindow XAML looks like (the StartTime type is string)

<Window x:Class="TimeLineCanvas.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:timeline="clr-namespace:TimeLineCanvas.UserControls"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding StartTime}" Height="100" /><!--Works binding to local property-->
            <timeline:TimeLine StartTime="28/01/2015" Height="100" /><!--Works when hard coded value-->
            <timeline:TimeLine StartTime="{Binding StartTime, UpdateSourceTrigger=PropertyChanged}" Height="100" /><!-- Does not display anything -->            
             <timeline:TimeLine x:Name="TimeLineInXaml" Height="100" /><!-- Works (value set in code behind) -->            
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Windows;
using System.ComponentModel;

namespace TimeLineCanvas
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        #region Constructors

        public MainWindow()
        {
            InitializeComponent();
            SetStartTime(DateTime.Now);
            TimeLineInXaml.StartTime = _startTime; 
            this.DataContext = this;
        }

        #endregion

        #region Properties

        private string _startTime;
        public string StartTime
        {
            get
            {
                return _startTime;
            }
            set
            {
                _startTime = value;
                OnPropertyChanged("StartTime");
            }
        }

        #endregion

        #region Methods

        internal void SetStartTime(DateTime dt)
        {
            this.StartTime = dt.ToShortDateString();
        }

        #endregion

        #region INotifyPropertyChanged Implementation

        public void OnPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion INotify
    }
}

My UserControl

<UserControl x:Class="TimeLineCanvas.UserControls.TimeLine"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Canvas>
            <TextBlock Text="{Binding StartTime, UpdateSourceTrigger=PropertyChanged}" />
        </Canvas>           
    </Grid>
</UserControl>

and the code behind in my UserControl

using System;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;

namespace TimeLineCanvas.UserControls
{
    /// <summary>
    /// Interaction logic for TimeLine.xaml
    /// </summary>
    public partial class TimeLine : UserControl, INotifyPropertyChanged
    {
        #region Constructor

        public TimeLine()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        #endregion

        #region Dependancy Properties

        public static readonly DependencyProperty StartTimeProperty = DependencyProperty.Register(
            "StartTime",
            typeof(string),
            typeof(TimeLine));

        #endregion

        #region Properties

        public string StartTime
        {
            get { return (string)GetValue(TimeLine.StartTimeProperty); }
            set
            {
                DateTime result;
                if (!DateTime.TryParse(value, out result))
                    System.Diagnostics.Debug.Assert(false, "Expected a value which can be converted to a DateTime");

                SetValue(TimeLine.StartTimeProperty, value);
            }
        }

        #endregion


        #region INotifyPropertyChanged Implementation

        public void OnPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion INotify
    }
}

So, the issue is clear, it's the binding but I don't know how to solve this! Even my Voodoo programming (trying anything and everything in random order) based upon other suggestions (such as adding DataContext="{Binding RelativeSource={RelativeSource Self}} to the UserControl, adding the INotifyPropertyChanged to both MainWindow and the UserContorl) does not affect the result.

What am I doing wrong or am I trying to do something it isn't intended for?

È stato utile?

Soluzione 2

Your code is a little bit confusing because you have duplicate property names and have obviously tested a lot during "Voodoo programming". :)

However, I think I found your problem anyway. Try to remove the following line in your TimeLine class:

this.DataContext = this;

This should not be in the control, but only in the MainWindow.

Explanation:

In the MainWindow constructor you set the MainWindow's DataContext to itself. Child elements inherit their DataContext from their parent element if it is not explicitly set. Therefore, the DataContext of the TextBlock is also set to the MainWindow which is why the Binding works properly here. However, in all TimeLine instances, you set the DataContext explicitly to themselves (in the constructor), i.e. the TimeLine object. This way, the Binding on the TimeLine instance does not refer to the MainWindow's StartTime property, but to the property with the same name in the TimeLine control. However, this property is never set to any real value (only bound to itself which does not make sense). Therefore, nothing is displayed.

Altri suggerimenti

Nearly there just need to simplify your understanding of Binding. Remember you have 2 lots of binding here

MainWindow implements INotify with Property StartTime TimeLine User Control has DP StartTime

So MainWindow is bound to property StartTime in MainWindow.cs NB Button to test change of time

<Grid>
    <StackPanel>          
        <timeline:TimeLine x:Name="myTime" StartTime="{Binding StartTime, Mode=TwoWay}" Height="100" /> 
        <Button Content="Change Time"  Width="200" Height="100" Click="Button_Click"/>                 
    </StackPanel>
</Grid>

Backend of MainWindow

public partial class MainWindow : Window, INotifyPropertyChanged
{
    #region Constructors

    public MainWindow()
    {
        InitializeComponent();           
        this.DataContext = this;
            Loaded += (s, args) =>
            {
                this.myTime.StartTime = "Some Time";
            };
    }

    #endregion

    #region Properties

    private string _startTime;
    public string StartTime
    {
        get
        {
            return _startTime;
        }
        set
        {
            _startTime = value;
            OnPropertyChanged("StartTime");
        }
    }

    #endregion       

    #region INotifyPropertyChanged Implementation

    public void OnPropertyChanged(string PropertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion INotify

     private void Button_Click(object sender, RoutedEventArgs e)
     {
        // We are changing usercontrol StartTime DP which updates or property in main             //window
        this.myTime.StartTime = "A new TIME";
     }
}

}

UserControl (bound to DP in backend)

<Grid>
    <Canvas>
        <TextBlock Text="{Binding StartTime}" />
    </Canvas>           
</Grid>

UserControl backend just has DP

public partial class TimeLine : UserControl
{
    #region Constructor

    public TimeLine()
    {
        InitializeComponent();           
    }

    public string StartTime
    {
        get { return (string)GetValue(StartTimeProperty); }
        set { SetValue(StartTimeProperty, value); }
    }

    // Using a DependencyProperty as the backing store for StartTime.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty StartTimeProperty =
        DependencyProperty.Register("StartTime", typeof(string), typeof(TimeLine), new PropertyMetadata(""));
}

Now when you click Change Time Button usercontrol updates MainWindow Property. Hope that helps

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top