6

我正在开发一个发票系统,并且正在使用一个DataGrid项目条目。我希望用户能够以不同的单位(例如英寸、英尺、米)输入数量,并将输入的数量转换为商品的库存单位。我的第一个想法是实现一个IMultiValueConverter,但我从昨天开始就一直在尝试,但无法弄清楚。

我的 Convert 方法确实有效,我实际上已经拿走了身体,将它卡在另一种方法中并测试了它的输出。我的程序在该ConvertBack方法中崩溃,我是 C# 新手,我从未见过有人真正实现该方法,所以我想知道为什么它强迫我实现它(我猜这是因为我的 Quantity 绑定不是oneway),但我实际上需要转换后的值才能返回到源对象的 Quantity 属性。

我在实现该方法时确实没有问题ConvertBack,但是传递给该方法的 value 参数是用户输入的值,而不是转换后的值,这是为什么呢?如果不是转换后的值,因此我无法真正弄清楚如何倒退,因为我只能访问用户输入的内容,而不是该方法中所需的单位。

我意识到我对转换器的理解可能还有很长的路要走,但是如果有人可以帮助我了解我的想法在哪里,我将不胜感激,以及针对我的问题的任何潜在解决方法/解决方案。

提前致谢!

页面/PurchaseInvoicePage.xaml

<DataGrid x:Name="ItemsGrid" Grid.Row="2" Grid.ColumnSpan="3" PreviewKeyDown="ItemsGrid_PreviewKeyDown" ItemsSource="{Binding Items}" FontSize="11" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" RowHeaderWidth="0" GridLinesVisibility="None" CanUserResizeRows="False" CanUserResizeColumns="False" CanUserReorderColumns="False">
    <DataGrid.Columns>

        <DataGridTemplateColumn x:Name="ItemNoColumn" Width="150" Header="Item No." IsReadOnly="True">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBox Cursor="Arrow" Width="130" BorderThickness="0" Background="Transparent" IsReadOnly="True" Text="{Binding Number}" />
                        <Image Cursor="Hand" MouseDown="ItemSelectionButton_Click" Width="12" Source="/Images/Icons/SearchBlack.png" />
                    </StackPanel>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <!-- question relative xaml starts here -->
        <DataGridTextColumn x:Name="QuantityColumn" Width="70" Header="Quantity">
            <DataGridTextColumn.Binding>
                <MultiBinding Converter="{StaticResource unitConverter}">
                    <Binding Path="Quantity" />
                    <Binding Path="Units" Mode="OneWay" />
                </MultiBinding>
            </DataGridTextColumn.Binding>
        </DataGridTextColumn>
        <!-- question relative xaml ends here -->

        <DataGridTextColumn x:Name="OrderColumn" Width="70" Header="Order" Binding="{Binding QuantityOrdered}" />
        <DataGridTextColumn x:Name="BackOrderColumn" Width="70" Header="B/O" Binding="{Binding QuantityBackOrdered}" />
        <DataGridTextColumn x:Name="UnitsColumn" Width="60" Header="Units" Binding="{Binding Units}" IsReadOnly="True" />
        <DataGridTextColumn x:Name="DescriptionColumn" Width="200" Header="Description" Binding="{Binding Description}" />
        <DataGridTextColumn x:Name="PriceColumn" Width="90" Header="Price" Binding="{Binding Price}" />
        <DataGridComboBoxColumn x:Name="TaxColumn" Width="50" Header="Tax" SelectedValueBinding="{Binding TaxCodeID}" DisplayMemberPath="Code" SelectedValuePath="ID" />
        <DataGridTextColumn x:Name="AmountColumn" Width="90" Header="Amount" Binding="{Binding Amount}" IsReadOnly="True" />
        <DataGridTextColumn x:Name="LinkedColumn" Width="90" Header="Linked" Binding="{Binding SalesOrderID}" />
    </DataGrid.Columns>
</DataGrid>

Fx/IMultiValueConverter.cs

public class UnitConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameters, CultureInfo culture)
    {
        double num = 0;
        // get the quantity value, and try parsing it
        string str = values[0].ToString().ToLower();
        bool parsed = double.TryParse(str, out num);

        // if it parses, no need to convert, return the value
        if (parsed)
            return num.ToString();

        // if it doesnt parse, get the last character in the value
        // this character indicates the units being entered
        // this will be either "(inches), f(eet), or m(eters) 
        string suffix = str.Substring(str.Length - 1);
        // get the value, without thhe last character
        str = str.Substring(0, str.Length - 1);
        // try parsing the value now
        parsed = double.TryParse(str, out num);

        // if it doesn't parse, the formats incorrect, return 0
        if (!parsed)
            return (0).ToString();

        // get the desired units, (the second value in my multibinding)
        string units = values[1].ToString().ToLower();

        // if either the entry suffix or the desired units are empty, just return
        // the number without converting
        if (string.IsNullOrEmpty(suffix) || string.IsNullOrEmpty(units))
            return num;

        // convert from inches to feet
        if (suffix == "\"" && units == "feet")
            return (num / 12).ToString();
        // convert from inches to meters
        else if (suffix == "\"" && units == "meters")
            return (num * 0.0254).ToString();
        // convert from feet to meters
        else if (suffix == "f" && units == "meters")
            return (num * 0.3048).ToString();
        // convert from meters to feet
        else if (suffix == "m" && units == "feet")
            return (num / 0.3048).ToString();

        // if we reachd this far, the user probably entered something random,
        // show an error and return 0
        MessageBox.Show("Failed to convert between the two units.");
        return (0).ToString();
    }

    public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
    {
        // now this is where my program is crashing, 
        // for some reason I need to actually implement the ConvertBack function
        // but when I try to popup the value being passed to this function,
        // it is the value the user entered e.g "20f" and not the converted value
        // should it not be the converted value? Why do I even need to implement this
        // how should i go backwards from here...
        string str = value.ToString();
        // MessageBox.Show(str);
        return new object[] { 0 }; // btw, I'm only returning one value, because the second binding is oneway

    }
4

2 回答 2

9

有趣的是,几周前我使用 IMultiValueConverter 实现了一个单位转换器。当我去挖掘它,看看我是否可以帮助你时,让我提出一个理论:

也许你已经翻转Convert并且ConvertBack

Convert:将模型中的值转换为绑定源(您的数据网格)

ConvertBack:将用户的输入转换为模型

因此,您可以期望在ConvertBack函数的参数中提供用户输入。

现在让我看看能不能找到那个转换器...

是 MSDN 文档

更新:所以我找到了我的转换器,记忆又回来了。我遇到了您刚刚遇到的相同问题:如何将所需的单位传递给 convert back 函数。老实说,我对我的解决方案不是 100% 满意,但我会与您分享。

ValueConverter我见过的大多数实现都是无状态的,因此您可以将它们用作StaticResource多个绑定。我在我的实现中添加了一个单位字段来跟踪单位——这牺牲了它的无状态性:

public class UnitConverter : IMultiValueConverter
{
    private string _units;

    public object Convert(object[ ] values, Type targetType, object parameter, CultureInfo culture)
    {
         //Initialize
         _units = values[1];
         //Convert
    }

    public object Convert(object[ ] values, Type targetType, object parameter, CultureInfo culture)
    {
         //Use _units to convert back
    }
}

不幸的是,您必须为您使用的每个绑定创建一个转换器:

<DataGridTextColumn.Binding>
   <MultiBinding>
      <Binding Path="Quantity" />
      <Binding Path="Units" Mode="OneWay" />
      <MultiBinding.Converter>
         <yourNameSpace:UnitConverter/>
      </MultiBinding.Converter>
   </MultiBinding>
</DataGridTextColumn.Binding>

我很想自己看到一个更好的解决方案,但这对我有用。

于 2011-08-16T14:14:02.690 回答
5

我在尝试调试我自己的单位转换器时遇到了这篇文章,该转换器使用与 Gene C 类似的技术来“记住”单位是什么以便转换回来。就我而言,我正在转换角度(RADIANS、DEGREES)和距离(NATIVE_DIST、MM、INCHES)。大多数时候一切都很好,但其他时候转换是完全错误的。

事实证明,值转换器默认是共享的。这意味着 Gene 示例中的“_units”将设置为上次调用“Convert”时使用的任何单位。就我而言,如果我从 NATIVE_DIST->MM 转换距离(记住原始单位是“NATIVE_DIST”),然后立即为与角度关联的另一个控件调用“ConvertBack”,“记住”单位将是“NATIVE_DIST” " 而不是我想要的角度单位。

该解决方案包括更改转换器的 .xaml 声明:

改变这个

<local:UnitsConverter x:Key="UnitsConverter"/>

对此

<local:UnitsConverter x:Key="UnitsConverter" x:Shared="false"/>
于 2012-02-06T15:44:03.937 回答