将数据绑定到Silverlight ComboBox的IValueConverter

时间:2012-02-20 15:04:11

标签: c# silverlight data-binding combobox ivalueconverter

我有一个ComboBox,允许用户选择每小时偏移量(0,3,6或9)。但是,他们所看到的需要显示为绝对时间,这是通过将偏移量添加到基准时间得出的。例如如果基准时间为“0600”,则用户可以选择“0600”,“0900”,“1200”和“1500”。

我正在使用IValueConverter将此偏移时间转换为绝对时间通过将值绑定到转换器的自定义属性,将基准时间传递给转换器。 (见下面的代码)。

现在这通常可以正常工作,除了ComboBox中最初选择的值的情况;这总是使用UtcNow的默认BaseTime,并且不使用绑定值。通过在代码中设置断点,我可以看到直到调用Convert whch转换初始值之后才设置BaseTime依赖项属性。

这是我正在使用的转换器类:

public class ForecastTimeConverter : DependencyObject, IValueConverter
{
     // Register the dependency property we need for the BaseTime property.
     public DependencyProperty BaseTimeProperty = DependencyProperty.Register(
            "BaseTime", 
            typeof(DateTime), 
            typeof(ForecastTimeConverter),
            new PropertyMetadata(DateTime.UtcNow, BaseTimeChanged)
       );

    private static void BaseTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
            // this method here just so I can set a breakpoint to see when the property is set.
    }

    public DateTime BaseTime
    {
        get { return (DateTime)GetValue(BaseTimeProperty);}
        set { SetValue(BaseTimeProperty, value); }
    }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string forecast_time;
        if (value is string)
        {
            try
            {
                // get forecast period, in hours.
                int hours = System.Convert.ToInt32(value as string);
                // add forecast period to base time to get final forecast time.
                DateTime forecastTime = BaseTime + new TimeSpan(hours, 0, 0);
                forecast_time = String.Format("{0:HHmm}z", forecastTime);
            }
            catch
            {
                forecast_time = "?";
            }    
        }
        else
        {
            throw new NotImplementedException("Can't convert from type '" + typeof(ValueType) + "'");
        }
        return forecast_time;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

完整的XAML和UserControl源相当大,所以这里只是相关的位:

<UserControl.Resources>
    <status:ForecastTimeConverter x:Key="ForecastTimeConverter" BaseTime="{Binding Path=CurrentBaseTime}" />
</UserControl.Resources>
...
<ComboBox x:Name="forecastPeriodCombo" Grid.Row="0" Grid.Column="1" Width="100" SelectionChanged="OnforecastPeriodChanged" >
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource ForecastTimeConverter}}"/>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

XAML背后的相关代码,简化:

public partial class MyControl : UserControl
{
    public MyControl()
    {
        InitializeComponent();
        this.Loaded += OnLoaded;
    }

    public void OnLoaded(object sender, EventArgs e)
    {
        forecastPeriodCombo.Items.Clear();
        List<string> values = new List<string>();
        values.Add("0");
        values.Add("3");
        values.Add("6");
        values.Add("9");
        forecastPeriodCombo.ItemsSource = values;
        forecastPeriodCombo.SelectedIndex = 1;
    }
}

问题是转换器的BaseTime属性的绑定直到之后才会触发UserControl的Loaded事件,因此显示ComboBox时,而不是看到“0900 “(从BaseTime偏移3小时)作为当前值,我看到更像”17:47“的东西,(从UtcNow偏移3小时)。当我单击ComboBox时,下拉列表将填入正确的时间。由于事件的顺序,这只是初始值。

调用OnLoaded,填充ComboBox,设置SelectedIndex,调用Convert,然后设置convert的BaseTime属性(太晚了!)。

我如何实现我需要的效果?我应该在其他事件上填充ComboBox吗?或者是否有更好的方法将基准时间传递给转换器?

2 个答案:

答案 0 :(得分:0)

您需要通过绑定来定义组合框的ItemsSource以使转换器工作。

<ComboBox x:Name="forecastPeriodCombo" ItemsSource={Binding ObservableCollectionWithValues, Converter={StaticResource ForecastTimeConverter}} ... >

“ObservableCollectionWithValues”是视图模型中的属性(如果使用mvvm)或后面的代码中的属性(实际上这不是正确的方法)。如果你不使用mvvm,那么也添加this.DataContext = this;在你控制的构造函数中。

转换器怎么样,因为我知道不可能对Resources使用绑定(你只能绑定到另一个静态资源)。这意味着您的转换器不会设置BaseTime属性。尝试使用ConverterParameter将基准时间传递给转换器。

答案 1 :(得分:0)

这是一个老问题,但希望可以帮助任何人找到这个页面。

使对象(在本例中为CurrentBaseTime)成为视图模型的公共属性,并确保视图模型继承INotifyPropertyChanged。加载值(在我的例子中,它是组合框的查找表),然后在加载后设置属性(提高属性更改)。

然后加载您的模型。在我的例子中,我需要在viewmodel中加载三个值得元数据的查找表,然后加载要加载的模型。 View然后调用预填充元数据的转换器(使用依赖属性)。

ViewModel需要引发属性更改,否则转换器会遇到null,即上面的问题。