3
votes

(Edited to fix the syntax error of the DP - the problem still persists though)

I'm having some problems with Databinding to a Dependency Property inside a simple custom usercontrol and wonder if some of you experts can tell me where I'm going wrong.

Basically, inside my usercontrol I have a label and two buttons. The label is bound to my Dependency Property and when you click the buttons, the count increments or decrements depending on the button clicks and then is displayed by the label.

If I do all this inside a page without a usercontrol, it's all working correctly. However once I move it to the usercontrol it starts acting strangely. The initial count of 0 is displayed at start as expected, and when I click the button the revised count is displayed on the first click. However if I click the same button again, the dependency property is updated, but the value is not displayed (the label goes blank). If I click the other button though, then the revised count is displayed, but just for the first click - it's all very strange and I'm stuck!

If I put a breakpoint on the 'TheCountProperty_Changed' method, I can verify that the property value is getting updated correctly..

Here's the XAML for my custom control:

<UserControl
x:Class="DPTest.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DPTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
x:Name="TestControl"
x:Uid="TestControl">

<Grid>
    <TextBlock x:Name="lblCount" HorizontalAlignment="Left" Margin="0,0,0,0" TextWrapping="Wrap" Text="{Binding Path=TheCount, ElementName=TestControl, Mode=OneWay}" VerticalAlignment="Top" FontSize="36"/>
    <Button x:Name="btnLess" Content="&lt;" HorizontalAlignment="Left" Margin="0,71,0,0" VerticalAlignment="Top" Width="75" Click="btnLess_Click" ClickMode="Press"/>
    <Button x:Name="btnMore" Content="&gt;" HorizontalAlignment="Left" Margin="91,71,0,0" VerticalAlignment="Top" Width="75" Click="btnMore_Click" ClickMode="Press"/>

</Grid>

And here's the CS for it:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace DPTest
{
public sealed partial class UserControl1 : UserControl
{
    public static readonly DependencyProperty TheCountProperty = DependencyProperty.Register("TheCount", typeof(int), typeof(UserControl1), new PropertyMetadata(0, new PropertyChangedCallback(TheCountProperty_Changed)));

    public int TheCount
    {
        get { return (int)GetValue(TheCountProperty); }
        set { SetValue(TheCountProperty, value); }
    }
    private static void TheCountProperty_Changed(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {

    }

    public UserControl1()
    {
        this.InitializeComponent();
    }

    private void btnLess_Click(object sender, RoutedEventArgs e)
    {
        //lblCount.Text = (Convert.ToInt32(lblCount.Text) - 1).ToString();
        this.TheCount -= 1;
    }

    private void btnMore_Click(object sender, RoutedEventArgs e)
    {
        //lblCount.Text = (Convert.ToInt32(lblCount.Text) + 1).ToString();
        this.TheCount += 1;
    }
}
}

I'm assuming that my Databinding syntax is slightly incorrect, but what should it be?

(EDIT) - The plot thickens! Wanted to add that I have just tried the exact same syntax in a standard WPF application and everything works as expected. So I guess the question is, have I found a bug with WinRT/XAML or is the syntax different using that platform?

(Edit #2 sorry for all the edits, but I'm still trying to work this out!) I'm starting to believe it's a display issue or bug with the button control. I have just added an extra button, but not wired up any click event code for it or anything - It's basically just a button that does nothing. Here's the interesting thing:

  • If I launch the test app, click the btnMore button, then TheCount get incremented and the Textblock is updated as expected. If I then click the same button again, TheCount gets incremented but the Text value of the lblCount becomes an empty string.
  • If however I launch the test app and press the btnMore, then press the unused button, and keep repeating that, then the value keeps incrementing and the TextBlock will keep updating - as long as I don't press btnMore twice in a row, but press the blank button (or indeed just focus the cursor into a blank textbox or any other UI thing), then the TextBlock will continue to display the incrementing number. The second I click the btnMore more than once, then the value of lblCount becomes an empty string.

This is very wierd....

3

3 Answers

2
votes

The third argument to the DependencyProperty looks like it should be 'UserControl1' and not 'UserControl'.

After spinning up Win 8 and trying the code for myself I saw the same issue you were having.

From that I was able determine two things:

1) Your dependency property was basically correct. I could observe the value of the property increase and decrease as the buttons were clicked. That indicated to me that the problem was some sort of refresh issue.

2) Moving the code from the UserControl into the Main xaml page (in my test app, it was the the default 'Blank.xaml' page) allowed the number to refresh properly.

It appears to me that there is some sort of refresh bug with the beta (err..'consumer preview') that you can workaround by structuring your application a little differently.

1
votes

This looks like some synchronization issue in WinRT - I used a converter to check what I was getting and it seemed like the int value was getting set to null. Once I modified the code to invoke the updates to the property asynchronously - it started working fine.

XAML:

<UserControl
    x:Class="DPTest.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DPTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    x:Name="TestControl"
    x:Uid="TestControl">

    <Grid>
        <Grid.Resources>
            <local:IntToStringConverter
                x:Key="IntToStringConverter" />
        </Grid.Resources>
        <TextBlock
            x:Name="lblCount"
            HorizontalAlignment="Left"
            Margin="0,0,0,0"
            TextWrapping="Wrap"
            Text="{Binding Path=TheCount, Converter={StaticResource IntToStringConverter}, ElementName=TestControl, Mode=OneWay}"
            VerticalAlignment="Top"
            FontSize="36" />
        <Button
            x:Name="btnLess"
            Content="&lt;"
            HorizontalAlignment="Left"
            Margin="0,71,0,0"
            VerticalAlignment="Top"
            Width="75"
            Click="btnLess_Click"
            ClickMode="Release" />
        <Button
            x:Name="btnMore"
            Content="&gt;"
            HorizontalAlignment="Left"
            Margin="91,71,0,0"
            VerticalAlignment="Top"
            Width="75"
            Click="btnMore_Click"
            ClickMode="Release" />
    </Grid>
</UserControl>

Code behind:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace DPTest
{
    public sealed partial class UserControl1 : UserControl
    {
        public static readonly DependencyProperty TheCountProperty =
            DependencyProperty.Register(
                "TheCount",
                typeof(int),
                typeof(UserControl1),
                new PropertyMetadata(
                    0,
                    new PropertyChangedCallback(TheCountProperty_Changed)));

        public int TheCount
        {
            get { return (int)GetValue(TheCountProperty); }
            set { SetValue(TheCountProperty, value); }
        }

        private static void TheCountProperty_Changed(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {

        }

        public UserControl1()
        {
            this.InitializeComponent();
        }

        private void btnLess_Click(object sender, RoutedEventArgs e)
        {
            //lblCount.Text = (Convert.ToInt32(lblCount.Text) - 1).ToString();
            this.Dispatcher.InvokeAsync(
                Windows.UI.Core.CoreDispatcherPriority.Low,
                (s, a) => this.TheCount -= 1,
                this,
                null);
        }

        private void btnMore_Click(object sender, RoutedEventArgs e)
        {
            //lblCount.Text = (Convert.ToInt32(lblCount.Text) + 1).ToString();
            this.Dispatcher.InvokeAsync(
                Windows.UI.Core.CoreDispatcherPriority.Low,
                (s, a) => this.TheCount += 1,
                this,
                null);
        }
    }

    public class IntToStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            return value.ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }

}
0
votes

I did something very similar by inheriting from RichTextBox.

http://www.wpfsharp.com/2011/08/01/loading-a-richtextbox-from-an-rtf-file-using-binding-or-a-richtextfile-control/

I see that I didn't make my dependency property readonly (not sure if that matters)

Also, I assume you intend to populate this:

private static void TheCountProperty_Changed(DependencyObject source, DependencyPropertyChangedEventArgs e)
{

}