none
Silverlight Binding or UserControl in DataGrid

    Question

  • I am trying to get my user control to bind to the property provided but am not having any luck so far in either the header or the cell. I have pieced this together from multiple examples on the web but cannot figure out why this is not working. It works great as a control on the page, just not in the datagrid.


    DateTimePicker XAML

    <UserControl x:Class="ProjectDeckHand.CustomControls.DateTimePicker"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
        xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">

        <Grid x:Name="LayoutRoot">
            <TextBlock x:Name="Display" MouseLeftButtonDown="Display_MouseLeftButtonDown" Text="{Binding SelectedDateTime, Mode=TwoWay, StringFormat='MM/dd/yy hh:mm tt'}"  Margin="5"></TextBlock>
            <Popup x:Name="MyPopup" VerticalOffset="0" HorizontalOffset="0">
                <Border BorderBrush="Black" BorderThickness="1" Background="AliceBlue" >
                    <StackPanel x:Name="PopUpPanel" Height="Auto" Width="Auto" Orientation="Vertical" Margin="5" >
                        <sdk:DatePicker x:Name="DatePart"></sdk:DatePicker>
                        <toolkit:TimePicker x:Name="TimePart"></toolkit:TimePicker>
                        <Button x:Name="ClosePopup" Content="Close" Margin="5" />
                    </StackPanel>
                </Border>
            </Popup>
        </Grid>

    </UserControl>


    DateTimePicker CS

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;


    namespace ProjectDeckHand.CustomControls
    {
        public partial class DateTimePicker : UserControl
        {

            public DateTimePicker()
            {
                this.DataContext = this;
                InitializeComponent();
                DatePart.SelectedDateChanged += new EventHandler<SelectionChangedEventArgs>(DatePicker_SelectedDateChanged);
                TimePart.ValueChanged += new RoutedPropertyChangedEventHandler<DateTime?>(TimePicker_ValueChanged);
                Loaded += new RoutedEventHandler(Page_Loaded);
            }

            void Page_Loaded(object sender, RoutedEventArgs e)
            {
                ClosePopup.Click += new RoutedEventHandler(ClosePopup_Click);
            }

            void ClosePopup_Click(object sender, RoutedEventArgs e)
            {
                MyPopup.IsOpen = false;
            }

            private void Display_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                MyPopup.IsOpen = true;
            }

            #region SelectedDateTime dependency property

            public DateTime? SelectedDateTime
            {
                get
                {
                    return (DateTime?)GetValue(SelectedDateTimeProperty);
                }
                set
                {
                    SetValue(SelectedDateTimeProperty, value);
                }
            }

            public static readonly DependencyProperty SelectedDateTimeProperty =
                DependencyProperty.Register("SelectedDateTime",
                typeof(DateTime?),
                typeof(DateTimePicker),
                new PropertyMetadata(null, new PropertyChangedCallback(SelectedDateTimeChanged)));

            private static void SelectedDateTimeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
                DateTimePicker me = sender as DateTimePicker;

                if (me != null)
                {
                    me.DatePart.SelectedDate = (DateTime?)e.NewValue;
                    me.TimePart.Value = (DateTime?)e.NewValue;
                }
            }

            #endregion

            private void TimePicker_ValueChanged(object sender, RoutedPropertyChangedEventArgs<DateTime?> e)
            {
                if (DatePart.SelectedDate != TimePart.Value)
                {
                    DatePart.SelectedDate = TimePart.Value;
                }

                if (SelectedDateTime != TimePart.Value)
                {
                    SelectedDateTime = TimePart.Value;
                }
            }

            private void DatePicker_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
            {
                // correct the new date picker date by the time picker's time
                if (DatePart.SelectedDate.HasValue && TimePart.Value.HasValue)
                {
                    // get both values
                    DateTime datePickerDate = DatePart.SelectedDate.Value;
                    DateTime timePickerDate = TimePart.Value.Value;

                    // compare relevant parts manually
                    if (datePickerDate.Hour != timePickerDate.Hour
                        || datePickerDate.Minute != timePickerDate.Minute
                        || datePickerDate.Second != timePickerDate.Second)
                    {
                        // correct the date picker value
                        DatePart.SelectedDate = new DateTime(datePickerDate.Year,
                            datePickerDate.Month,
                            datePickerDate.Day,
                            timePickerDate.Hour,
                            timePickerDate.Minute,
                            timePickerDate.Second);

                        // return, because this event handler will be executed a second time
                        return;
                    }
                }

                // now transfer the date picker's value to the time picker
                // and dependency property
                if (TimePart.Value != DatePart.SelectedDate)
                {
                    TimePart.Value = DatePart.SelectedDate;
                }

                if (SelectedDateTime != DatePart.SelectedDate)
                {
                    SelectedDateTime = DatePart.SelectedDate;
                }
            }
        }
    }

    DataGrid XAML

     <sdk:DataGrid CanUserSortColumns="False" x:Name="dgSamples" ItemsSource="{Binding SamplePMs}" toolkit:DockPanel.Dock="Bottom" Margin="0,10,0,0" AutoGenerateColumns="False" >
                <sdk:DataGrid.Columns>
                    <sdk:DataGridTemplateColumn >
                        <sdk:DataGridTemplateColumn.HeaderStyle>
                            <Style TargetType="prim:DataGridColumnHeader">
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <StackPanel Orientation="Horizontal">
                                                <StackPanel>
                                                    <TextBlock>Collection Date</TextBlock>
                                                    <Border BorderBrush="LightGray" BorderThickness="1" MinHeight="24" Background="White">
                                                        <my:DateTimePicker x:Name="dpCollectionDate" SelectedDateTime="{Binding Path=DataContext.DefaultDateTime, Mode=TwoWay}" />
                                                    </Border>
                                                </StackPanel>
                                                <HyperlinkButton  Click="hlCopyCollectionDateDownwards_Click"  CommandParameter="{Binding ElementName=dpCollectionDate, Path=SelectedDateTime}" VerticalAlignment="Bottom" Margin="5,0,0,3">
                                                    <Image Source="Graphics/DownArrow.png" VerticalAlignment="Bottom" />
                                                </HyperlinkButton>
                                            </StackPanel>

                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </sdk:DataGridTemplateColumn.HeaderStyle>
                        <sdk:DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <StackPanel>
                                    <my:DateTimePicker SelectedDateTime="{Binding Path=DataContext.CollectionDate, Mode=TwoWay}" Width="100" Height="22"/>
                                    <!--<TextBlock Text="{Binding CollectionDate}" Foreground="red"></TextBlock>-->
                                    <sdk2:DatePicker SelectedDate="{Binding CollectionDate}"></sdk2:DatePicker>
                                </StackPanel>
                            </DataTemplate>
                        </sdk:DataGridTemplateColumn.CellTemplate>
                    </sdk:DataGridTemplateColumn>
                </sdk:DataGrid.Columns>
            </sdk:DataGrid>

    Tuesday, May 31, 2011 11:05 PM

Answers

  • Well, you hav another mistake in your UserControl, you set the UserControl's DataContext to this in its constructor. And when we use the UserControl in the DataGrid, the DataGrid will set the control;s DataContext to the current bound item object, and it override the DataContext value in its constructor.

    I will recommend you to design the control as CustomControl with a template, however, for UserControl, please view the following simple sample:

    MainWindow.xaml:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
        <DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False">
          <DataGrid.Columns>
            <DataGridTemplateColumn Header="Date" Width="*">
              <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <local:DateTimePicker SelectedDate="{Binding Date}"/>
                </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
          </DataGrid.Columns>
        </DataGrid>
      </Grid>
    </Window>
    

     

    MainWindow C#:

      public partial class MainWindow : Window
      {
        public MainWindow()
        {
          InitializeComponent();
     
          this.DataContext = new List<Item>()
          {
            new Item(){Date= DateTime.Now},
            new Item(){Date= DateTime.Now.AddDays(1)},
            new Item(){Date= DateTime.Now.AddDays(2)},
            new Item(){Date= DateTime.Now.AddDays(3)},
            new Item(){Date= DateTime.Now.AddDays(4)}
          };
        }
      }
     
      public class Item
      {
        public DateTime Date { getset; }
      }

     

     

    UserControl.xaml:

     

    <UserControl x:Class="WpfApplication1.DateTimePicker"
        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>
     <TextBlock x:Name="Display" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedDate, Mode=TwoWay, StringFormat='MM/dd/yy'}"  Margin="5"></TextBlock>
     </Grid>
    </UserControl>
    

     

    UserControl C#:

      public partial class DateTimePicker : UserControl
      {
        public DateTimePicker()
        {
          InitializeComponent();
        }
     
        public DateTime SelectedDate
        {
          get { return (DateTime)GetValue(SelectedDateProperty); }
          set { SetValue(SelectedDateProperty, value); }
        }
        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register("SelectedDate"typeof(DateTime), typeof(DateTimePicker), new UIPropertyMetadata(DateTime.Now));
     
      }

     

    Download: http://cid-51b2fdd068799d15.office.live.com/self.aspx/.Public/Samples%5E_2011/20110603%5E_BindingWPFUserControlDP.zip

     

    And please view this thread has the same issue: http://social.msdn.microsoft.com/Forums/en-us/wpf/thread/30a873b6-c2d4-4c34-bb87-291de482cd53

     

    PS. Move to Off-topic after two days since it is a SL related question.

     

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    • Marked as answer by Adrient Friday, June 03, 2011 6:21 PM
    Friday, June 03, 2011 5:52 AM

All replies

  • Not sure this function in WPF is same with Silverlight, for the Silverlight question, you could post it on the official SL forums: http://forums.silverlight.net/

    In WPF, the UserControl does not provide the custom template, so we cannot use the TemplateBinding in the XAML. For this scenario, we should bind the UserControl property in the XMAL by ElementName or RelativeSource.

    <TextBlock x:Name="Display" MouseLeftButtonDown="Display_MouseLeftButtonDown" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedDateTime, Mode=TwoWay, StringFormat='MM/dd/yy hh:mm tt'}" Margin="5"></TextBlock>
    
    
    
    OR
    
    
    <UserControl x:Name="MyControl" ...>
    ...
       <TextBlock x:Name="Display" MouseLeftButtonDown="Display_MouseLeftButtonDown" Text="{Binding ElementName=MyControl, Path=SelectedDateTime, Mode=TwoWay, StringFormat='MM/dd/yy hh:mm tt'}" Margin="5"></TextBlock>
    ...
    

     

    Sincerely,

     


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, June 01, 2011 6:14 AM
  • I tried both options.

    #1 would not compile complaining that AncestorType is not part of RelativeSource

     

    #2 compiled and ran but did not bind

    Wednesday, June 01, 2011 2:27 PM
  • For #1, in VS 2008, it is a visual studio designer issue in WPF as I know, please ignore it and continue to compile the code.

    For #2, should work, and you set the binding in the DataGrid as "my:DateTimePicker SelectedDateTime="{Binding Path=DataContext.CollectionDate, Mode=TwoWay}"" Please remove the "DataContext", since without the Source, the Path will find the current DataContext.DataContext.CollectionDate property value, the second DataContext is needless.

     

    Still recommend you to SL forum, since my suggestion may work for WPF application only,

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, June 01, 2011 5:34 PM
  • I am thinking the error is more in the user control. If I place

    <

     

    sdk2:DatePicker SelectedDate="{Binding CollectionDate}"></sdk2:DatePicker

    >

     

    in the same CellTemplate as my user control, this does display the correct value.

    I am using VS2010, Silverlight 4, if it makes any difference.

    Thursday, June 02, 2011 5:20 PM
  • Well, you hav another mistake in your UserControl, you set the UserControl's DataContext to this in its constructor. And when we use the UserControl in the DataGrid, the DataGrid will set the control;s DataContext to the current bound item object, and it override the DataContext value in its constructor.

    I will recommend you to design the control as CustomControl with a template, however, for UserControl, please view the following simple sample:

    MainWindow.xaml:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
        <DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False">
          <DataGrid.Columns>
            <DataGridTemplateColumn Header="Date" Width="*">
              <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <local:DateTimePicker SelectedDate="{Binding Date}"/>
                </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
          </DataGrid.Columns>
        </DataGrid>
      </Grid>
    </Window>
    

     

    MainWindow C#:

      public partial class MainWindow : Window
      {
        public MainWindow()
        {
          InitializeComponent();
     
          this.DataContext = new List<Item>()
          {
            new Item(){Date= DateTime.Now},
            new Item(){Date= DateTime.Now.AddDays(1)},
            new Item(){Date= DateTime.Now.AddDays(2)},
            new Item(){Date= DateTime.Now.AddDays(3)},
            new Item(){Date= DateTime.Now.AddDays(4)}
          };
        }
      }
     
      public class Item
      {
        public DateTime Date { getset; }
      }

     

     

    UserControl.xaml:

     

    <UserControl x:Class="WpfApplication1.DateTimePicker"
        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>
     <TextBlock x:Name="Display" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedDate, Mode=TwoWay, StringFormat='MM/dd/yy'}"  Margin="5"></TextBlock>
     </Grid>
    </UserControl>
    

     

    UserControl C#:

      public partial class DateTimePicker : UserControl
      {
        public DateTimePicker()
        {
          InitializeComponent();
        }
     
        public DateTime SelectedDate
        {
          get { return (DateTime)GetValue(SelectedDateProperty); }
          set { SetValue(SelectedDateProperty, value); }
        }
        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register("SelectedDate"typeof(DateTime), typeof(DateTimePicker), new UIPropertyMetadata(DateTime.Now));
     
      }

     

    Download: http://cid-51b2fdd068799d15.office.live.com/self.aspx/.Public/Samples%5E_2011/20110603%5E_BindingWPFUserControlDP.zip

     

    And please view this thread has the same issue: http://social.msdn.microsoft.com/Forums/en-us/wpf/thread/30a873b6-c2d4-4c34-bb87-291de482cd53

     

    PS. Move to Off-topic after two days since it is a SL related question.

     

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    • Marked as answer by Adrient Friday, June 03, 2011 6:21 PM
    Friday, June 03, 2011 5:52 AM
  • You nailed the problem on the head. The problem was setting the datacontext in the constructor. Once I removed that line of code, everything worked as I expected. Thank you!!!
    Friday, June 03, 2011 6:21 PM