1

我有一个带有文本框的 WPF 窗口,使用标准 WPF 数据绑定到对象。这一切都很好,问题是这样的:

用户正在输入时间估计,我想让用户选择以小时或分钟为单位输入数据(通过下拉列表)。我想在几分钟内保存数据,如果用户选择小时,将他们的输入乘以 60 并存储数据。

如何使用 WPF 数据绑定来实现这一点?这甚至可能吗?

编辑

例如,如果下拉列表设置为小时,90 分钟将显示为 1.5,但如果选择 miutes,则显示为 90。

4

5 回答 5

3

您可以在窗口上使用特殊属性:

<ComboBox x:Name="Units">
    <sys:String>Hours</sys:String>
    <sys:String>Minutes</sys:String>
</ComboBox>
<TextBox x:Name="Value" Text="{Binding Path=Estimate, ElementName=ThisWindow}" />

然后实现特殊属性:

public double Estimate
{
    get
    {
        switch(this.Units.SelectedItem as String)
        {
        case "Hours":
            return this.estimate / 60.0;
        default:
            return this.estimate;
        }
    }

    set
    {
        switch(this.Units.SelectedItem as String)
        {
        case "Hours":
            this.estimate = value * 60.0;
            break;
        default:
            this.estimate = value;
            break;
        }

        this.OnPropertyChanged("Estimate");
    }
}
于 2009-08-16T19:47:30.513 回答
3

在首先完全误解了这个问题之后,这是我获得第一个赏金的努力;)

<Window.Resources>        
    <local:DivisionConverter x:Key="HoursConverter" Divisor="60"/>

    <DataTemplate DataType="{x:Type local:MyObject}">
        <StackPanel Orientation="Horizontal">
            <TextBox x:Name="InputBox" Text="{Binding Path=Estimate}" Width="80"/>
            <ComboBox x:Name="InputUnit" SelectedItem="Minutes">
                <System:String>Minutes</System:String>
                <System:String>Hours</System:String>
            </ComboBox>
        </StackPanel>            
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding ElementName=InputUnit, Path=SelectedItem}" Value="Hours">
                <Setter TargetName="InputBox" Property="Text" Value="{Binding Path=Estimate, Converter={StaticResource HoursConverter}}"/>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>        
</Window.Resources>
<Grid>
    <ContentControl VerticalAlignment="Top">
        <local:MyObject Estimate="120"/>
    </ContentControl>
</Grid>

一般来说,我不喜欢专门的转换器:一段时间后,您会忘记哪个转换器到底做了什么,并且每次您认为需要一个转换器时,您最终都会通过转换器代码,或者更糟糕的是,您构建了第二个完全相同的转换器。所以现在我只在其他所有方法都失败时才使用它们。

相反,我定义了一个通用的DivisionConverter,它将 Convert 方法中的值相除,并将 ConvertBack 方法中的值相乘,正如您所期望的那样,无需查看此处的代码;)

另外,我使用 DataTemplate 和 Triggers,而其他人使用 MultiBinding 或者(更糟糕的是)在 getter 和 setter 中添加了这个 UI 逻辑。这将所有这些相对简单的 UI 逻辑保留在一个可监督的地方。

现在只是为了完成,这是代码隐藏:

public class MyObject
{
    public double Estimate { get; set; }
}


public class DivisionConverter : IValueConverter
{
    public double Divisor { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double doubleValue = (double)value;
        return doubleValue / Divisor;            
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {           
        double inputValue;
        if (!double.TryParse((string)value, NumberStyles.Any, culture, out inputValue)) 
            return 0;

        return inputValue * Divisor;
    }
}
于 2009-08-17T19:54:53.903 回答
1

您可以使用 MultiValueConverter :

XAML

        <ComboBox ItemsSource="{StaticResource values}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock>
                      <TextBlock.Text>
                        <MultiBinding Converter="{StaticResource minutToHours}">
                          <Binding/>
                          <Binding Path="IsChecked" ElementName="chkHours"/>
                        </MultiBinding>
                      </TextBlock.Text>
                    </TextBlock>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
        <CheckBox Name="chkHours" Content="Show hours"/>

转换器的 C# 代码

public class MinuteToHoursConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int minutes = 0;
        if (values[0] is TimeSpan)
        {
            minutes = (int)((TimeSpan)values[0]).TotalMinutes;
        }
        else
        {
            minutes = System.Convert.ToInt32(values[0]);
        }
        bool showHours = System.Convert.ToBoolean(values[1]);
        if (showHours)
            return (minutes / 60.0).ToString();
        else
            return minutes.ToString();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }

    #endregion
}

但是这个解决方案可能不是最优的......恕我直言,如果您使用 MVVM 模式,最好的方法是在 ViewModel 中创建一个额外的属性来计算正确的值

于 2009-08-16T19:57:38.880 回答
0

也许我想多了,但是由于组合框既有显示值又有实际值(用户幕后),为什么不让您的估算列表有两列……分别是小时和分钟. 然后,根据选中的模式,只需将“显示”绑定更改为相应的模式。这样,它将始终存储您想要的幕后分钟数。没有其他转换为/从。

于 2009-08-19T18:32:54.643 回答