3

I am trying to learn WPF on my own, and it is a bit of a struggle. I need to know how to set the value of an attached property via binding. The attached properties Grid.Row and Grid.RowSpan are the destination for the binding, not the source. Similar questions have been asked on StackOverflow, but they were asked and answered by people who really know WPF, or they involve complications like value converters. I haven't found an answer that is applicable and comprehensible to me.

In this case, I have a grid that represents a full day's schedule, and I want to add events to it. Each event will start at a particular grid row and span a number of rows, depending on the event's start time and duration. My understanding is that you have to use dependency properties as the source for bindings, so my event object, ActivityBlock, has the StartIncrement and DurationIncrements dependency properties to describe its position on the grid.

That's it, that's all I want to do: make a UserControl position itself within a grid via bindings.

I believe my problem is most likely in my MainWindow XAML, setting up the bindings incorrectly on the grid. (Note that I create the grid rows programmatically in the constructor, so don't look for them in the XAML below).

When I run my application, the event is created, but it doesn't show up in the proper location on the grid, as if Grid.Row and Grid.RowSpan are never being updated. Is binding Grid.Row and Grid.RowSpan impossible? If not, what am I doing wrong?

Here is MainWindow.xaml, where my problem is most likely to be:

<Window x:Class="DayCalendar.MainWindow" 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" xmlns:local="clr-namespace:DayCalendar" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" Activated="Window_Activated" > <DockPanel LastChildFill="True"> <Rectangle Width="50" DockPanel.Dock="Left"/> <Rectangle Width="50" DockPanel.Dock="Right"/> <Grid x:Name="TheDay" Background="#EEE"> <ItemsControl ItemsSource="{Binding Path=Children}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style TargetType="ContentPresenter"> <Setter Property="Grid.Row" Value="{Binding Path=StartIncrement}" /> <Setter Property="Grid.RowSpan" Value="{Binding Path=DurationIncrements}" /> </Style> </ItemsControl.ItemContainerStyle> </ItemsControl> </Grid> </DockPanel> </Window> 

Here is the code-behind file for the MainWindow:

using System; using System.Windows; using System.Windows.Controls; using DayCalendar.MyControls; namespace DayCalendar { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var rowDefs = TheDay.RowDefinitions; rowDefs.Clear(); for ( int ix = 0; ix < ( 60 / ActivityBlock.MINUTES_PER_INCREMENT ) * 24; ix += 1 ) { rowDefs.Add( new RowDefinition() ); } } private void Window_Activated( object sender, EventArgs e ) { if ( m_firstActivation ) { m_firstActivation = false; var firstActivity = new ActivityBlock(); var today = DateTime.Now.Date; var startTime = today.AddHours( 11.5 ); var durationSpan = new TimeSpan( 1, 0, 0 ); firstActivity.SetTimeSpan( startTime, durationSpan ); TheDay.Children.Add( firstActivity ); } } private bool m_firstActivation = true; } } 

Here is the ActivityBlock code:

using System; using System.Windows; using System.Windows.Controls; namespace DayCalendar.MyControls { public partial class ActivityBlock : UserControl { public int StartIncrement { get { return ( int ) GetValue( StartIncrementProperty ); } set { SetValue( StartIncrementProperty, value ); } } public int DurationIncrements { get { return ( int ) GetValue( DurationIncrementsProperty ); } set { SetValue( DurationIncrementsProperty, value ); } } public ActivityBlock() { InitializeComponent(); } public void SetTimeSpan( DateTime startTime, TimeSpan duration ) { int startMinute = startTime.Hour * 60 + startTime.Minute; int durationMinutes = ( int ) duration.TotalMinutes; StartIncrement = startMinute / MINUTES_PER_INCREMENT; DurationIncrements = Math.Max( 1, durationMinutes / MINUTES_PER_INCREMENT ); } static ActivityBlock() { var thisType = typeof( ActivityBlock ); var affectsArrangeAndMeasure = FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure; int startIncrementDefault = 0; StartIncrementProperty = DependencyProperty.Register( nameof( StartIncrement ), startIncrementDefault.GetType(), thisType, new FrameworkPropertyMetadata( startIncrementDefault, affectsArrangeAndMeasure ) ); int durationIncrementsDefault = 1; DurationIncrementsProperty = DependencyProperty.Register( nameof( DurationIncrements ), durationIncrementsDefault.GetType(), thisType, new FrameworkPropertyMetadata( startIncrementDefault, affectsArrangeAndMeasure ) ); } public const int MINUTES_PER_INCREMENT = 6; // 1/10th of an hour static public readonly DependencyProperty StartIncrementProperty; static public readonly DependencyProperty DurationIncrementsProperty; } } 

The corresponding XAML isn't interesting, but I include it in case it is needed:

<UserControl x:Class="DayCalendar.MyControls.ActivityBlock" 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" xmlns:local="clr-namespace:DayCalendar.MyControls" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Border BorderThickness="3" BorderBrush="Black" Background="Chartreuse"> <DockPanel LastChildFill="True"> <TextBlock TextWrapping="WrapWithOverflow">Event Description</TextBlock> </DockPanel> </Border> </UserControl> 
1
  • 1
    I have a vague clue about what you want to achieve. But you're mixing CodeBehind with xaml so hard, that it is impossible to achieve. Ask yourself one thing. Will i do everything in CodeBehind and be able to access every control i wish, or should i switch to MVVM, since DataBinding makes only sense there. Commented Aug 2, 2016 at 16:08

1 Answer 1

1

It is possible to Bind Grid.Row here is a simple example below with MVVM,

View

<Window x:Class="WpfApplication3.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="20"/> <RowDefinition Height="20"/> <RowDefinition Height="20"/> </Grid.RowDefinitions> <TextBlock Text="Hello" Grid.Row="{Binding RowNumber}"></TextBlock> </Grid> </Window> 

ViewModel

public class ViewModel { public ViewModel() { RowNumber = 2; } public int RowNumber { get; set; } } 

I have set the DataContext to View from CodeBehind. like below, which DataContext is linked to ViewModel class. we can set DataContext in different ways.

xaml.cs

public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new ViewModel(); } } 

When you launch the application it will set the RowNumber to two and show the TextBlock on the 3rd row. later on you can change the row by updating RowNumber from ViewModel

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, Abin. Your example View is very clear, but I do have questions about the ViewModel. In your example, what links ViewModel to the TextBlock? If I create a second TextBlock in code and add it as a child, how do I link it to a new ViewModel so that each TextBlock will show up in the correct space on the grid?
I have edited the answer to show how to set dataContext from codebehind. for adding more textblock is like adding a new textblock to a collection of TextBlocks so you can add any container to hold it. you have used ItemsControl in the question so its a collection that can grow the controls or elements inside it based on the number of the collection which is binded to the itemsource of it. You need to use Styles if you need to set properties in ItemsControl. hope it help. and google is your best friend.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.