1

I have a custom "DateRangeSelector" control derived from ComboBox. This is a drop-down control with the following filters:
1. Today
2. Next three days
3. Next three weeks
4. Custom Range(Allows the user to set a custom date range)

Now this "DateRangeSelector" control is added to another XAML(ActivityListMenuControlView.xaml) as:

<DateRangeSelector:DateRangeSelectorControl x:Name="DateRangeSelector"
    Grid.Column="1"
    Margin="10 0 0 0"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    AutomationProperties.AutomationId="AID_TaskListDateRangeSelector"
    DateRangeUpdatedCmd="{Binding Path=DateRangeSelectionUpdatedCommand}"
    TodayDateUpdatedCmd="{Binding Path=TodayDateUpdatedCommand}"
    FontSize="{StaticResource TaskListMenuFontSize}"
    RangeOptions="{Binding Path=DateRangeSelectionOptions,
    Mode=OneTime}"
    SelectedDateRange="{Binding Path=SelectedRange,
    Mode=TwoWay}"
    Visibility="{Binding Path=ShowFilterOptions,
    Converter={StaticResource boolToVisibility}}" />

As evident from the code above i have created a command "TodayDateUpdatedCmd" in "DateRangeSelector" intended to update the "Today" filter in this control whenever the system date changes and binded to the command "TodayDateUpdatedCommand" in "ActivityListMenuControlViewModel".
Code to update the date is there in the method "SetDateValues" in "DateRangeSelector" itself. I am just confused on how to excute this method from "ActivityListMenuControlViewModel"? Please help.

UPDATE: DateRangeSelector is simply a class with no view/viewmodel. Here is the code:

public class DateRangeSelectorControl : ComboBox, INotifyPropertyChanged
{
                  public static readonly DependencyProperty TodayDateUpdateCmdProperty = DependencyProperty.Register("TodayDateUpdatedCmd", typeof(ICommand), typeof(DateRangeSelectorControl),
    new PropertyMetadata(null));

    public ICommand TodayDateUpdatedCmd
    {
        get { return (ICommand)this.GetValue(TodayDateUpdateCmdProperty); }
        set
        {
            this.SetValue(TodayDateUpdateCmdProperty, value);
        }
    }

         /// <summary>
    /// 
    /// </summary>
    private void SetDateValues()
    {
        DateTime todaysDate = DateTime.Now;

        TodayText = Utility.GetStringFromResource("TodayLabel") + " (" + todaysDate.ToShortDateString() + ")";

        NextThreeDaysText = Utility.GetStringFromResource("NextThreeDaysLabel") + " (" + todaysDate.ToShortDateString() + " - " + todaysDate.AddDays(3).ToShortDateString() + ")";

        NextWeekText = Utility.GetStringFromResource("NextWeekLabel") + " (" + todaysDate.ToShortDateString() + " - " + todaysDate.AddDays(7).ToShortDateString() + ")";

        SetCustomDateRangeText();
    }
}

As evident from the code above, i have first registered a dependency property "TodayDateUpdateCmdProperty" and the command property "TodayDateUpdatedCmd" which is used in "ActivityListMenuControlView.xaml" as seen in the XAML snippet. Further i need to execute the method "SetDateValues" in DateRangeSelector class to update the today date. Now please help me out how to achieve this?

UPDATE: As per the suggestions from @GazTheDestroyer i made the changes into the code and now not using any command.But now getting the runtime XamlParseException with the below details:

"'The invocation of the constructor on type 'VMS.Nexus.Client.Common.Controls.DateRangeSelector.DateRangeSelectorControl' that matches the specified binding constraints threw an exception.' Line number '45' and line position '14'."}

InnerException: {"Default value type does not match type of property 'TodayDate'."}

This exception is thrown in ActivityListMenuControlView.xaml where i created DateRangeSelector. Please help

sandy
  • 616
  • 2
  • 9
  • 20

2 Answers2

0

When writing WPF and MVVM in particular, it is common for developers to use a form of delegate ICommand that we can declare in our view models. You can find out implementation details for the popular RelayCommand in the Relaying Command Logic section of the WPF Apps With The Model-View-ViewModel Design Pattern page on MSDN. You can find a basic example of its use in the How can I use the RelayCommand in wpf? question here on Stack Overflow.

You can therefore define your ICommand instances in your view model and data bind them to your controls. In the case of your DateRangeSelector, you could simply declare a DependencyProperty of type ICommand and data bind to that from your view model... maybe something like this:

<DateRangeSelector:DateRangeSelectorControl TodayDateUpdatedCmd="{Binding Command}">
    ...
</DateRangeSelector:DateRangeSelectorControl>

And in your view model:

public ICommand Command
{
    get { return new ActionCommand(action => DoSomething(),
        canExecute => CanDoSomething(); }
}

UPDATE >>>

Quite simply put, there is no need to extend the ComboBox class in this (or almost any) instance in WPF. See the Control Authoring Overview page on MSDN for more information on why there is no need to do this.

So the basic idea, is that you move the functionality from the ComboBox to another class... a view model class. From there you can then data bind to the ComboBox and provide the functionality. In WPF, we manipulate data, not UIElements. So rather than defining all of your 'filters' in a control, define them in code.

First, you need a collection property containing the available dates:

public ObservableCollection<DateTime> AvailableDates
{
    get { return availableDates; }
    set { availableDates = value; NotifyPropertyChange("AvailableDates"); }
}

Next, you should have an enum with a value that represents each filter type. You could add a property of this type to your view model which could control the items that appear in the ComboBox each time it is changed:

public FilterType FilterType
{
    get { return filterType; }
    set
    {
        filterType = value; 
        NotifyPropertyChange("FilterType");
        FillAvailableDatesDependantOnFilterType();
    }
}

...

private void FillAvailableDatesDependantOnFilterType()
{
    AvailableDates = new ObservableCollection<DateTime>();
    if (FilterType == "Today") AvailableDates.Add(DateTime.Now.Date);
    ...
}

Hopefully you now have a better idea.

Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Please see the update i provided for DateRangeSelector code.Note that i do not have any viewmodel defined for the same. If you can help me out with some code snippet in context to DateRangeSelector and ActivityListMenuControlViewModel would be great! – sandy Apr 25 '14 at 10:30
0

Commands are supposed to be fired by the control, not used as notification mechanisms to the control which you seem to be attempting.

If your control needs to react to some changing variable, then it should expose a DependencyProperty and react to changes on that. eg, in the code of DateRangeSelectorControl

public static readonly new DependencyProperty TodaysDateProperty=
            DependencyProperty.Register("TodaysDate", typeof(DateTime), typeof(DateRangeSelectorControl), new PropertyMetadata(null, TodaysDateChanged));

public new DateTime TodaysDate
{
    get { return (DateTime)GetValue(TodaysDateProperty); }
    set { SetValue(TodaysDateProperty, value); }
}

private static void TodaysDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ((DateRangeSelectorControl)d).TodaysDateChanged((DateTime)e.NewValue);
}

private void TodaysDateChanged(DateTime newDate)
{
    //update your control here
}

Your ViewModel can then simply expose a TodaysDate property that can be bound to this DependencyProperty.

GazTheDestroyer
  • 20,722
  • 9
  • 70
  • 103
  • I do not have any ViewModel created for DateRangeSelector. It is only defined in Themes\Generic.xaml and just a .cs file(DateRangeSelector.cs) – sandy Apr 25 '14 at 10:16
  • I know. The code I posted is in DateRangeSelector.cs, not a view model. The ViewModel I refer to would be your ActivityListMenuControlViewModel or similar. – GazTheDestroyer Apr 25 '14 at 10:18
  • "Your ViewModel can then simply expose a TodaysDate property that can be bound to this DependencyProperty." As you posted this line, how can i expose "TodaysDate" property if i do not have any viewmodel. If you can help me out with some code snippet in context to "DateRangeSelector" and "ActivityListMenuControlViewModel" would be great! – sandy Apr 25 '14 at 10:25
  • As I explained in my previous comment, TodaysDate would be exposed by ActivityListMenuControlViewModel. – GazTheDestroyer Apr 25 '14 at 10:39
  • Thank you. I still have one doubt, i have provided the binding in "ActivityListMenuControlView.xaml" to "TodaysDate"(DateRangeSelector) with a command say "ICommand TodayDateUpdatedCommand"(defined in ActivityListMenuControlViewModel) then how should i go ahead now? Any code would be really helpful. – sandy Apr 25 '14 at 10:57
  • As I said, do not use commands. Just bind a normal property on the ViewModel, to a DependencyProperty on the control. No commands anywhere. – GazTheDestroyer Apr 25 '14 at 11:07
  • My only confusion is how to execute method "TodayDateChanged" in DateRangeSelector? I have now create a property in viewmodel as: public DependencyObject TodayDate { get { return (DependencyObject)GetValue(DateRangeSelectorControl.TodayDateProperty); } set { SetValue(DateRangeSelectorControl.TodayDateProperty, value); } } how to use this property now so that finally the method TodaysDateChanged get executed? I am little new to this. – sandy Apr 25 '14 at 11:53
  • :I made the changes as per your suggestions. I am getting runtime exception: "Default value type does not match type of property 'TodayDate'." type : XamlParseException. This is being thrown inside the XAML(ActivityListMenuControlView.xaml) where i created "DateRangeSelector" control. Please help. – sandy Apr 28 '14 at 06:34