Saurav Deb Purkayastha
Saurav Deb Purkayastha

Reputation: 159

Data binding in C# (WP8.1) while setting datacontext of stackpanel not working

Code that does not work:

<ListView x:Name="list_lapTimes" 
          ScrollViewer.VerticalScrollMode="Auto"
          Grid.Row="1"
          ItemsSource="{Binding RecTimes}">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <StackPanel Orientation="Horizontal" 
                            Grid.Column="0" 
                            HorizontalAlignment="Center"
                            DataContext="SplitTime">
                    <TextBlock Text="{Binding Hours, Converter={StaticResource TimeConverter}}" 
                               FontSize="30"></TextBlock>
                    <TextBlock FontSize="30">:</TextBlock>
                    <TextBlock Text="{Binding Minutes, Converter={StaticResource TimeConverter}}" 
                               FontSize="30"></TextBlock>
                    <TextBlock FontSize="30">:</TextBlock>
                    <TextBlock Text="{Binding Seconds, Converter={StaticResource TimeConverter}}" 
                               FontSize="30"></TextBlock>
                    <TextBlock FontSize="15" VerticalAlignment="Bottom">.</TextBlock>
                    <TextBlock Text="{Binding Milliseconds, Converter={StaticResource TimeConverter}, ConverterParameter=something}" 
                               FontSize="15"
                               VerticalAlignment="Bottom"></TextBlock>
                </StackPanel>
                
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Code that does work:

<ListView x:Name="list_lapTimes" 
          ScrollViewer.VerticalScrollMode="Auto"
          Grid.Row="1"
          ItemsSource="{Binding RecTimes}">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <StackPanel Orientation="Horizontal" 
                            Grid.Column="0" 
                            HorizontalAlignment="Center">
                    <TextBlock Text="{Binding SplitTime.Hours, Converter={StaticResource TimeConverter}}" 
                               FontSize="30"></TextBlock>
                    <TextBlock FontSize="30">:</TextBlock>
                    <TextBlock Text="{Binding SplitTime.Minutes, Converter={StaticResource TimeConverter}}" 
                               FontSize="30"></TextBlock>
                    <TextBlock FontSize="30">:</TextBlock>
                    <TextBlock Text="{Binding SplitTime.Seconds, Converter={StaticResource TimeConverter}}" 
                               FontSize="30"></TextBlock>
                    <TextBlock FontSize="15" VerticalAlignment="Bottom">.</TextBlock>
                    <TextBlock Text="{Binding SplitTime.Milliseconds, Converter={StaticResource TimeConverter}, ConverterParameter=something}" 
                               FontSize="15"
                               VerticalAlignment="Bottom"></TextBlock>
                </StackPanel>
                
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Below is a part of my Page class -->

public ObservableCollection<TimeSnap> RecTimes { get; set; }

public MainPage()
{
    this.InitializeComponent();

    this.NavigationCacheMode = NavigationCacheMode.Required;
    sw = new Stopwatch(Dispatcher);
    RecTimes = new ObservableCollection<TimeSnap>();
    DataContext = this;
}

And here is the TimeSnap class -->

public class TimeSnap
{
    public TimeSpan LapTime { get; set; }
    public TimeSpan SplitTime { get; set; }
}

The difference between the two xaml codes is that in the first one I set the DataContext of the StackPanel inside the DataTemplate to SplitTime and then use simply the name of the properties in the TextBlocks inside the stackpanel (e.g. Hours).

In the second xaml code I do not set any DataContext of the StackPanel and in the textboxes I use the full path in the binding for textblocks (e.g. SplitTime.Hours).

The second approach works but, I need it to work the first way. From what I know the both approaches should mean the same binding path. But in the first approach I get the following binding error:

Error: BindingExpression path error: 'Hours' property not found on 'Windows.Foundation.IReference`1<String>'. BindingExpression: Path='Hours' DataItem='Windows.Foundation.IReference`1'; target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')

Error: BindingExpression path error: 'Minutes' property not found on 'Windows.Foundation.IReference`1<String>'. BindingExpression: Path='Minutes' DataItem='Windows.Foundation.IReference`1'; target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')

Error: BindingExpression path error: 'Seconds' property not found on 'Windows.Foundation.IReference`1<String>'. BindingExpression: Path='Seconds' DataItem='Windows.Foundation.IReference`1'; target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')

Error: BindingExpression path error: 'Milliseconds' property not found on 'Windows.Foundation.IReference`1<String>'. BindingExpression: Path='Milliseconds' DataItem='Windows.Foundation.IReference`1'; target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')

Upvotes: 0

Views: 651

Answers (2)

Saurav Deb Purkayastha
Saurav Deb Purkayastha

Reputation: 159

I finally solved it by creating a wrapper class for TimeSpan. I believe that TimeSpan being a struct was the problem. I am not sure however. Anyway I added the following class -->

public class TimeSpanCustom
{
    TimeSpan ts;
    public int Hours { get; set; }
    public int Minutes { get; set; }
    public int Seconds { get; set; }
    public int Milliseconds { get; set; }

    public TimeSpanCustom(int days, int hours, int minutes, int seconds, int milliseconds)
    {
        this.Hours = hours;
        this.Minutes = minutes;
        this.Seconds = seconds;
        this.Milliseconds = milliseconds;
        ts = new TimeSpan(days, hours, minutes, seconds, milliseconds);
    }

    public TimeSpanCustom Subtract(TimeSpanCustom ts)
    {
        TimeSpan minuend = new TimeSpan(0, ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
        TimeSpan result = this.ts.Subtract(minuend);
        return new TimeSpanCustom(0, result.Hours, result.Minutes, result.Seconds, result.Milliseconds);
    }
}

And changed the TimeSnap class to use TimeSpanCustom instead of TimeSpan.

public class TimeSnap
{
    public TimeSpanCustom LapTime { get; set; }
    public TimeSpanCustom SplitTime { get; set; }
}

Now the 1st xaml code works.

Upvotes: 0

fillobotto
fillobotto

Reputation: 3785

You have to use the same syntax when you bind to a DataContext:

<StackPanel Orientation="Horizontal" 
      Grid.Column="0" 
      HorizontalAlignment="Center"
      DataContext="{Binding SplitTime}">

The rest of your code looks fine to me.

UPDATE If you are binding the model to the Page.DataContext, I suggest to go with:

<StackPanel Orientation="Horizontal" 
      Grid.Column="0" 
      HorizontalAlignment="Center"
      DataContext="{Binding DataContext.SplitTime, ElementName=list_lapTimes}">

UPDATE 2 StackPanel does not need any DataContext binding because it automatically inherits the data from the ListView. For each TextBox you should use:

<TextBlock Text="{Binding SplitTime, Converter={StaticResource TimeSecondConverter}}" 
                               FontSize="30"></TextBlock>

' And use a different Converter to display seconds, minutes etc.

Upvotes: 0

Related Questions