2

I am trying to build my own calendar control in WPF/XAML, both as an exercise and to be used in a hobby project. This calender will be a grid where each cell obviously represents a day in the chosen month. Each cell should be able to display a list of items. The inputs for the calendar should be an identification of a month and a list of days. Those "days" will be of a custom type, something like

public class CalendarDay
{
    public int DayNumber {get; set;}
    public List<DayItem> Items {get; set;}
}

where a DayItem could represent something like an appointment or a todo.

I'm implementing this as a user control. The XAML for this calendar is a ControlTemplate that contains a UniformGrid of 1x7 for the day names (data bound to a collection 7 strings) and a UniformGrid of 6x7 for the days (data bound to a collection of CalendarDay).

A view (user control) that contains this calendar conceptually looks like this:

<UserControl name="myView" ... xmlns:cal="clr-namespace:the calendar namespace">

    <Grid>
        <cal:Calendar Days="{Binding DaysWithItems}" CurrentMonth="{Binding DisplayMonth}" />
    </Grid>

</UserControl>

As I'm applying MVVM, myView will have a DataContext that is set to some view model class that has a property DaysWithItems (a list of CalenderDay instances) and a property DisplayMonth.

Ideally, the consumer of this calendar control should only have to provide the two inputs as mentioned. Moreover, DaysWithItems should, from myView's point of view (pun is coincidental), be a list of 28, 29, 30 or 31 elements, depending on the month. This means that the list should somehow be padded to 42 items. I think this should be the responsibility of the calendar control, not myView's view model.

Note that I'm also not provding the day names. This too should be the responsibility of the calendar control. This shouldn't be provided explicitly.

Here's my problem. If, in the calendar's control template, I want to bind to the string collection for the day names and the 42 element collection of CalendarDay, the datacontext should be the Calendar class itself (because of the responsibilities I explained earlier). On the other hand, in myView, I'm binding the calendar to myView's DaysWithItems (the (logical) collection that contains 28..31 elements), so there the calendar's datacontext should be myView's view model.

Can I use some sort of internal datacontext (= "internal" to the control template) and also some sort of external datacontext (= a datacontext as provided by the consumer of the calendar control)?

bvgheluwe
  • 853
  • 7
  • 25

2 Answers2

2

The easiest way to bind to something other than your DataContext is by using ElementName

You can do the following:

Example for UserControl.xaml.cs:

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



        public int MyProperty
        {
            get { return (int)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register("MyProperty", typeof(int), typeof(UserControl1), new PropertyMetadata(0));


    }

UserControl.Xaml

<UserControl x:Class="GridSplitterTest.UserControl1"
             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" x:Name="MyControl"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Button Content="{Binding ElementName=MyControl, Path=MyProperty}"></Button>
    </Grid>
</UserControl>

By using ElementName and pointing to the UserControl you are bypassing the DataContext and able to bind to properties inside your UC.

Amit Raz
  • 5,370
  • 8
  • 36
  • 63
2

Here's the down-low on these kind of data bindings. When you want to data bind to a property from an object that is set as the DataContext, you use a normal Binding Path:

<TextBlock Text="{Binding PropertyOfDataContextObject}" />

However, when you also want to data bind to a property (from inside a UserControl that is declared in that UserControl (or Window)) you should use a RelativeSource Binding:

<TextBlock Text="{Binding PropertyOfUserControl, RelativeSource={RelativeSource 
    AncestorType={x:Type YourPrefix:YourUserControl}}}" />

You can alternatively name a control and use the Binding.ElementName Property to reference it:

<TextBlock Text="{Binding PropertyOfNamedControl, ElementName=NameOfControl}" />
Sheridan
  • 68,826
  • 24
  • 143
  • 183