user2008572
user2008572

Reputation: 72

Unset dependency on binding to dependency property

I am trying to bind to a dependency property of my user control from my user control however it doesn't seem to be working as the converter keeps throwing an unset dependency property error

The dependency property

   public DateTime? DisplayedDate
    {
        get { return (DateTime?)base.GetValue(DisplayedDateProperty); }
        set { base.SetValue(DisplayedDateProperty, value); }
    }

    public static readonly DependencyProperty DisplayedDateProperty =
      DependencyProperty.Register("DisplayedDate", typeof(DateTime?), typeof(SideBarUser), new FrameworkPropertyMetadata()
      {
          BindsTwoWayByDefault = true,
          DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });

The XAML Binding

 <UserControl.Resources>
     <sys:Int32 x:Key="Test">1</sys:Int32>
     <Converters:DateCountConverter x:Key="DateCountConverter"/>
 </UserControl.Resources>



 <TextBlock DataContext="{Binding RelativeSource={RelativeSource Self}}"
            TextAlignment="Center">
            <TextBlock.Text> 
               <MultiBinding Converter="{StaticResource DateCountConverter}">
                   <Binding Path="DisplayedDate"   />                        
                   <Binding  Source="{StaticResource Test}"  />
               </MultiBinding>
            </TextBlock.Text>
 </TextBlock>

And finally the part it's failing at in the coverter

 DateTime date = (DateTime)values[0];

All together yields

System.InvalidCastException
Specified cast is not valid.
at System.Windows.Data.MultiBindingExpression.TransferValue()
   at System.Windows.Data.MultiBindingExpression.Transfer()
   at System.Windows.Data.MultiBindingExpression.UpdateTarget(Boolean includeInnerBindings)
   at System.Windows.Data.MultiBindingExpression.AttachToContext(Boolean lastChance)
   at System.Windows.Data.MultiBindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(Boolean lastChance)
   at MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance)
   at MS.Internal.Data.DataBindEngine.Run(Object arg)
   at MS.Internal.Data.DataBindEngine.OnLayoutUpdated(Object sender, EventArgs e)
   at System.Windows.ContextLayoutManager.fireLayoutUpdateEvent()
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.UIElement.UpdateLayout()
   at System.Windows.Interop.HwndSource.SetLayoutSize()
   at System.Windows.Interop.HwndSource.set_RootVisualInternal(Visual value)
   at System.Windows.Interop.HwndSource.set_RootVisual(Visual value)
   at MS.Internal.DeferredHwndSource.ProcessQueue(Object sender, EventArgs e)

I cannot seem to get this to work for the life of me. Am I missing something? When debugging with another instance of Visual Studio it comes up that it is an Unset Dependency property

Edit: When I comment out everything and just have

<TextBlock Text="{Binding Path=DisplayedDate, RelativeSource={RelativeSource Self}}" />

It works just fine displaying the display date. My confusion level is too great to cope with right now

EDIT EDIT: Converter code

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      DateTime? date = (DateTime?)values[0];
//ToDo move most of the logic inside AppointmentsViewModel class to handle date filtering
                AppointmentsViewModel MyAppointments  = new AppointmentsViewModel();
                String Count;

                int SelectionType = (int)values[1];
                //Note To Self Make Enum
                switch (SelectionType)
                {
                    case 0:
                      Count =  MyAppointments.Appointments.Where(x => date != null && x.Beginning.HasValue && date.HasValue
                          && x.Beginning.Value.Month == date.Value.Month
                             && x.Beginning.Value.Year  == date.Value.Year ).Count().ToString();
                        break;
                    case 1:
                        Count = MyAppointments.Appointments.Where(x => date != null && x.Test.HasValue && date.HasValue
                            && x.Test.Value.Month == date.Value.Month
                             && x.Test.Value.Year == date.Value.Year).Count().ToString();
                        break;
                  //ETC
                    default:
                        Count =  MyAppointments.Appointments.Where(x => date != null && x.End.HasValue
                            && date.HasValue && x.End.Value.Month == date.Value.Month
                             && x.End.Value.Year == date.Value.Year).Count().ToString();
                        break;
                }
                return Count;
            }

            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }

Upvotes: 1

Views: 1587

Answers (1)

vesan
vesan

Reputation: 3369

There are several problems with your code. I had to make some assumptions here, so hopefully I'm correct.

The converter

Your converter assumes that it will get 2 values of certain types. You want to be a little careful with that. Especially the first value, which comes from a binding, might be DependencyProperty.UnsetValue if the binding has not been set yet.

So, you probably want to check if the values are correct before you start doing the actual conversion, for example:

if (values.Length != 2 || !(values[0] is DateTime?)|| !(values[1] is int))
{
    return DependencyProperty.UnsetValue;
}

You should not have your converter throw exceptions, because they are treated as uncaught run-time exceptions and will terminate your application unless you have some global exception handler (see this question).

The control

Now, I assume that your DisplayedDate property is defined on your UserControl. If so, then this line:

<TextBlock DataContext="{Binding RelativeSource={RelativeSource Self}}"

will set the DataContext to this TextBlock, so when you later go to retrieve the DisplayedDate property, it will not be found. You can fix this in 2 ways:

1) You use the ancestor-lookup binding:

"{Binding RelativeSource={RelativeSource AncestorType=local:UserControl1}}"

Of course, replace local:UserControl1 with the namespace and name of your control.

2) You define the content of the UserControl as a Template instead, then use {RelativeSource TemplatedParent}, which will point to the "owner" of the template, in this case, your UserControl:

<UserControl.Template>
    <ControlTemplate>
        <TextBlock DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
        TextAlignment="Center">
            <TextBlock.Text>
                <MultiBinding Converter="{StaticResource DateCountConverter}">
                    <Binding Path="DisplayedDate"   />
                    <Binding  Source="{StaticResource Test}"  />
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </ControlTemplate>
</UserControl.Template>

Just put this in your XAML instead of the <TextBlock>...</TextBlock> part.

Maybe there are some other issues, but testing with a simple converter this worked for me.

Upvotes: 2

Related Questions