将数据绑定到 Silverlight ComboBox 的 IValueConverter
我有一个组合框,允许用户选择每小时偏移量(0、3、6 或 9)。然而,他们看到的需要显示为绝对时间,这是通过将偏移量添加到基准时间而得出的。例如,如果基准时间是“0600”,则用户可以从“0600”、“0900”、“1200”和“1500”中进行选择。
我正在使用 IValueConverter 将此偏移时间转换为绝对时间。通过将值绑定到转换器的自定义属性,将基本时间传递给转换器。 (请参阅下面的代码)。
现在,除了组合框中最初选择的值的情况之外,这通常工作得很好;这始终使用 UtcNow 的默认 BaseTime,并且不使用绑定值。通过在代码中设置断点,我可以看到 BaseTime 依赖属性直到调用 Convert 来转换初始值之后才设置。
这是我正在使用的转换器类:
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 小时)。当我单击组合框时,下拉列表中会填充正确的时间。由于事件的顺序,这只是初始值是错误的。
调用 OnLoaded,填充 ComboBox,设置 SelectedIndex,调用 Convert,然后设置转换的 BaseTime 属性(太晚了!)。
怎样才能达到我需要的效果呢?我应该在其他事件中填充组合框吗?或者是否有更好的方法将基准时间传递给转换器?
I have a ComboBox that allows the user to select an hourly offset (0, 3, 6 or 9). However, what they see needs to be shown as an absolute time, which is derived by adding the offset to a base time. e.g. if the base time is "0600", the user gets to choose from "0600", "0900", "1200" and "1500".
I'm using an IValueConverter to convert this offset time to an absolute time The base time is passed to the converter by binding a value to a custom property of the converter. (see below for the code).
Now this generally works fine except for the case of the initially selected value in the ComboBox; this always uses the default BaseTime of UtcNow, and doesn't use the bound value. By setting breakpoints in the code I can see that the BaseTime dependency property isn't set until after the call to Convert whch converts the initial value.
This is the converter class I'm using:
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();
}
}
The full XAML and UserControl source is rather large, so here are just the relevant bits:
<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>
And the relevant code behind the XAML, simplified:
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;
}
}
The problem is that the binding of the BaseTime property of the converter isn't done until after the Loaded event fires for the UserControl, so the when the ComboBox is displayed, instead of seeing "0900" (3hrs offset from BaseTime) as the current value, I see something more like "17:47", (3 hours offset from UtcNow). When I click on the ComboBox the drop-down is populated with the correct times. It's just the initial value that is wrong due to the order of events.
OnLoaded is called, ComboBox is filled, SelectedIndex is set, Convert is called, then the BaseTime property of the convert is set (too late!).
How can I acheive the effect I require? Should I be populating the ComboBox on some other event? Or is there perhaps a better way to pass the basetime to the converter?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您需要通过绑定定义组合框的 ItemsSource 才能使 Converter 工作。
“ObservableCollectionWithValues”是视图模型中的属性(如果您使用 mvvm)或后面代码中的属性(这实际上不是正确的方法)。如果您不使用 mvvm,则还添加 this.DataContext = this;在你的控件的构造函数中。
转换器怎么样,据我所知,不可能对资源使用绑定(您只能绑定到另一个静态资源)。这意味着您的转换器将不会获得 BaseTime 属性集。尝试使用 ConverterParameter 将基准时间传递给转换器。
You need to define ItemsSource of you combobox via binding to get Converter work.
"ObservableCollectionWithValues" is property in your view model (if you use mvvm) or property in your code behind (which is actually not correct approach). If you not use mvvm then add also this.DataContext = this; in constructor of your control.
What about converter, as I know it is not possible to use binding for Resources (you can bind only to another static resource). This means your converter will not get BaseTime property set. Try to use ConverterParameter instead to pass base time to the converter.
这是一个老问题,但希望可以帮助任何找到此页面的人。
使对象(在本例中为 CurrentBaseTime)成为视图模型的公共属性,并确保视图模型继承 INotifyPropertyChanged。加载值(在我的例子中,它是组合框的查找表),然后在加载后设置属性(引发属性更改)。
然后加载您的模型。就我而言,我需要将三个包含元数据的查找表加载到视图模型中,然后加载模型。然后,视图调用预先填充了元数据的转换器(使用依赖属性)。
ViewModel 需要引发属性更改,否则转换器将陷入 null,即上述问题。
This is an old question, but hopefully can help anyone finding this page.
Make the object (in this case CurrentBaseTime) a public Property of the View Model and make sure the view model inherits INotifyPropertyChanged. Load the values (in my case, it was a lookup table for the combobox) and then set the Property after the load (raising the property change).
Then load your model. In my case, I need three lookup tables worth of metadata to be loaded in the viewmodel and then the model to be loaded. The View then calls the converters which are prepopulated with metadata (using the Dependency Property).
The ViewModel needs to raise the Property changes otherwise the converters a stuck with null, i.e. the issue above.