views:

142

answers:

1

In my application the user can select how a date is displayed. Most of the standard datetime format strings can be selected. My problem now is that the average user doesnt understand the difference between "m" and "D". What I want to do is change this so that, as excel does, rather than showing the format string, I show how an arbitrary date would look like using that format.

The WPF combobox SelectedItem is bound to a dependency property in the date format picker clas, which the other control containing this date picker also binds to.

Example:

  1. The user selects "January, 15" so the dependency property is set to "m".
  2. Through code behind, the value of the dependency property is set to "D", the combobox is updated to display "Thursday, 15 January 1970" as the selected item.

I tried using converters but ConvertBack was impossible since I cant extract a format string used to create a certain date.

+1  A: 

You could create a class DateFormatChoice that contains a property for the format code (e.g., "m" or "D") and a property for the current date formatted in that way.

public class DateFormatChoice {
    public string FormatCode { get; private set; }
    public string CurrentDateExample {
        get { return DateTime.Now.ToString( FormatCode ) }
    }

    public DateFormatChoice( string standardcode ) {
        FormatCode = standardcode;
    }
}

You bind your ComboBox to a collection of these using CurrentDateExample in either your DataTemplate or as the ComboBox's DisplayMemberPath. You can either use these objects directly with your date format picker class and the DatePicker binds to the FormatCode property of the chosen DateFormatChoice object, or you can set the ValueMemberPath property on the original ComboBox to the FormatCode property and use SelectedValue on the ComboBox for getting/setting what is chosen. Not using ValueMember might be a little easier.


Here's a more full example. It uses the DateFormatChoice class above.

First, a data collection.

public class DateFormatChoices : List<DateFormatChoice> {
    public DateFormatChoices() {
        this.Add( new DateFormatChoice( "m" ) );
        this.Add( new DateFormatChoice( "d" ) );
        this.Add( new DateFormatChoice( "D" ) );
    }
}

Then I made simple ViewModel for the Window:

public class ViewModel : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged = ( s, e ) => {
    }; // the lambda ensures PropertyChanged is never null

    public DateFormatChoices Choices {
        get;
        private set;
    }

    DateFormatChoice _chosen;
    public DateFormatChoice Chosen {
        get {
            return _chosen;
        }
        set {
            _chosen = value;
            Notify( PropertyChanged, () => Chosen );
        }
    }

    public DateTime CurrentDateTime {
        get {
            return DateTime.Now;
        }
    }

    public ViewModel() {
        Choices = new DateFormatChoices();
    }

    // expression used to avoid string literals
    private void Notify<T>( PropertyChangedEventHandler handler, Expression<Func<T>> expression ) {
        var memberexpression = expression.Body as MemberExpression;
        handler( this, new PropertyChangedEventArgs( memberexpression.Member.Name ) );
    }
}

I didn't have a date picker control that accepted the standard string format codes, so I made a quite dumb UserControl (with many corners cut) just to demonstrate its receipt of the format code. I gave it a dependency property called DateFormatProperty of type string and specified a value changed callback in the UIPropertyMetadata.

<Grid>
    <TextBlock Name="datedisplayer" />
</Grid>

The callback:

private static void DateFormatChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e ) {
    var uc = obj as UserControl1;
    string code;
    if ( null != ( code = e.NewValue as string ) ) {
        uc.datedisplayer.Text = DateTime.Now.ToString( code );
    }
}

And this is how I tied it all together in the Window.

<StackPanel>
    <StackPanel.DataContext>
        <local:ViewModel />
    </StackPanel.DataContext>
    <ComboBox
        ItemsSource="{Binding Choices}" DisplayMemberPath="CurrentDateExample"
        SelectedItem="{Binding Chosen, Mode=TwoWay}"/>
    <local:UserControl1
        DateFormatProperty="{Binding Chosen.FormatCode}" />
</StackPanel>
Joel B Fant