3
votes

I'm having issues Binding a custom User Control up through my ViewModel in an MVVM Silverlight application. The basic control is a Date entry form with three text boxes. I am using the MVVM to get textbox data in three properties and then validate it to Date.

Binding Below control to MVVM works fine for me:

XAML of user Control: DateControl.xaml

<UserControl x:Class="DatePicker.DateControl"
    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">

    <Grid x:Name="LayoutRoot" Background="White"
          HorizontalAlignment="Center">
        <StackPanel Orientation="Horizontal">
            <StackPanel Orientation="Horizontal">
                <TextBox MaxLength="2" TabIndex="0" Style="{StaticResource BirthDDTextBoxStyle}"
                                Text="{Binding BirthDay,Mode=TwoWay,ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"/>
                <TextBlock  Style="{StaticResource TextBlockStyle_DateSeperator}"/>
                <TextBox MaxLength="2" TabIndex="1" Style="{StaticResource BirthDDTextBoxStyle}" Text="{Binding BirthMonth, Mode=TwoWay,ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"/>
                <TextBlock   Style="{StaticResource TextBlockStyle_DateSeperator}"/>
                <TextBox MaxLength="4" TabIndex="2" Style="{StaticResource BirthYYTextBoxStyle}" Text="{Binding BirthYear, Mode=TwoWay, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"/>
                <Button Content="Show" Width="100" Height="30" Click="Button_Click"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</UserControl>

DateControlViewModel:

namespace DatePicker
{
    public class DatePickerViewModel : EntityViewModel
    {
        public DatePickerViewModel()
        {

        }

        private DateTime birthDate;
        public DateTime BirthDate
        {
            get
            {

                return birthDate;
            }
            set
            {
                birthDate = value;
            }
        }
        private string birthDay;
        public string BirthDay
        {
            get { return birthDay; }
            set
            {
                birthDay = value;
                PropertyChangedHandler("BirthDay");
                ValidateBirthDay("BirthDay", value);
            }
        }

        private string birthMonth;
        public string BirthMonth
        {
            get { return birthMonth; }
            set
            {
                birthMonth = value;
                PropertyChangedHandler("BirthMonth");
                ValidateBirthMonth("BirthMonth", value);
            }
        }

        private string birthYear;
        public string BirthYear
        {
            get { return birthYear; }
            set
            {
                birthYear = value;
                PropertyChangedHandler("BirthYear");
                ValidateBirthYear("BirthYear", value);
            }
        }

        private void ValidateDOB()
        {
            ClearErrorFromProperty("BirthDay");
            ClearErrorFromProperty("BirthMonth");
            ClearErrorFromProperty("BirthYear");
            if (string.IsNullOrEmpty(BirthDay))
                ValidateBirthDay("BirthDay", BirthDay);
            if (string.IsNullOrEmpty(BirthMonth))
                ValidateBirthMonth("BirthMonth", BirthMonth);
            if (string.IsNullOrEmpty(BirthYear))
                ValidateBirthYear("BirthYear", BirthYear);
            if (!string.IsNullOrEmpty(BirthDay) && !string.IsNullOrEmpty(BirthMonth) && !string.IsNullOrEmpty(BirthYear))
                SetDateOfBirth();
        }

        private void ValidateBirthDay(string propertyName, string value)
        {
            ClearErrorFromProperty(propertyName);
            if (String.IsNullOrEmpty(value))
                AddErrorForProperty(propertyName, "Resources.PatientImport.EmptyDayMessage");
            else
                if (Common.IsNumber(value))
                {
                    int day = Convert.ToInt32(value);
                    if (day > 31 || day < 1)
                        AddErrorForProperty(propertyName, "Resources.PatientImport.InvalidDayMessage");
                }
                else
                    AddErrorForProperty(propertyName, "Resources.PatientImport.InvalidDayMessage");
        }

        private void ValidateBirthMonth(string propertyName, string value)
        {
            ClearErrorFromProperty(propertyName);
            if (String.IsNullOrEmpty(value))
                AddErrorForProperty(propertyName, "Resources.PatientImport.EmptyMonthMessage");
            else
                if (Common.IsNumber(value))
                {
                    int month = Convert.ToInt32(value);
                    if (month > 12 || month < 1)
                        AddErrorForProperty(propertyName, "Resources.PatientImport.InvalidMonthMessage");
                }
                else
                    AddErrorForProperty(propertyName, "Resources.PatientImport.InvalidMonthMessage");
        }

        private void ValidateBirthYear(string propertyName, string value)
        {
            ClearErrorFromProperty(propertyName);
            if (String.IsNullOrEmpty(value))
                AddErrorForProperty(propertyName, "Resources.PatientImport.EmptyYearMessage");
            else
                if (Common.IsNumber(value))
                {
                    int year = Convert.ToInt32(value);
                    if (year.ToString().Length > 4 || year.ToString().Length < 4)
                        AddErrorForProperty(propertyName, "Resources.PatientImport.InvalidYearMessage");
                }
                else
                    AddErrorForProperty(propertyName, "Resources.PatientImport.InvalidYearMessage");
        }

        private void SetDateOfBirth()
        {
            string dateString = BirthDay + "-" + BirthMonth + "-" + BirthYear;
            DateTime date;
            if (!DateTime.TryParse(dateString.ToString(), out date))
            {
                ClearErrorFromProperty("BirthDay");
                ClearErrorFromProperty("BirthMonth");
                ClearErrorFromProperty("BirthYear");
                AddErrorForProperty("BirthDay", "Resources.PatientImport.InvalidDayMessage");
                AddErrorForProperty("BirthMonth", "Resources.PatientImport.InvalidMonthMessage");
                AddErrorForProperty("BirthYear", "Resources.PatientImport.InvalidYearMessage");
            }
            else
                BirthDate = date;
        }
    }
}

I want to use this control in my MainPage.xaml:

<UserControl x:Class="DatePicker.MainPage"
    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:DatePicker"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <local:DateControl />
    </Grid>
</UserControl>

I want to define a Property in DateControl which I will use to bind my UserControl with ViewModel of MainPage.xaml

Something like:

<Grid x:Name="LayoutRoot" Background="White">
    <local:DateControl Date="{Binding BirthDate, Mode=TwoWay, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"/>
</Grid>

BirthDate is string Type Property.

How do I change my usercontrol so I can bind its property in MainPage.XAML using separate MVVM

1
You should read the editing help, the formatting was quite broken...H.B.
Why have you added the Date property to your UserControl? Views can exist without code at all, everything can be done using only view models.vortexwolf

1 Answers

1
votes

First you need to set the DataContext of your DateControl to the DateControlViewModel. In your DateControl xaml you can use this idea, or set it somewhere in code (like a constructor of the DateControl) or data bind the DataContext to the view model you want to use.

 <UserControl.Resources>
        <viewModels:ViewDisplayTemplatesViewModel x:Key="viewModel" />
    </UserControl.Resources>

    <UserControl.DataContext>
        <Binding Source="{StaticResource viewModel}"/>
    </UserControl.DataContext>

Secondly, you need to define a dependency property in your DateControl, as Aardvark mentioned in the comment. I use the propdp snippet (which I think comes with the Silverlight install, if not you can see it in this MSDN article). The Dependency property enables binding in xaml.