C# 在可重用用户控件中绑定转换器参数

C# 在可重用用户控件中绑定转换器参数,c#,wpf,binding,ivalueconverter,C#,Wpf,Binding,Ivalueconverter,我正在尝试创建一个可重用的用户控件(用于数据输入),其中有两个文本框,它们通过IValueConvertor链接到每个文本框 下面的XAML是原始的普通代码。这就是我试图在用户控件中重现的内容 <WrapPanel> <TextBlock Text="Length of Fence"/> <TextBox Name="Metric" Width="50" Text="{Binding Path=LengthFence, Mode=TwoWay}"/&

我正在尝试创建一个可重用的用户控件(用于数据输入),其中有两个文本框,它们通过
IValueConvertor
链接到每个文本框

下面的XAML是原始的普通代码。这就是我试图在用户控件中重现的内容

<WrapPanel>
    <TextBlock Text="Length of Fence"/>
    <TextBox Name="Metric" Width="50" Text="{Binding Path=LengthFence, Mode=TwoWay}"/>
    <TextBlock Text="Meters"/>
    <TextBox Text="{Binding ElementName=Metric, Path=Text, Converter={StaticResource MetersToInches}, StringFormat=N8}"/>
    <TextBlock Text="Inches"/>
</WrapPanel>
这就是这个XAML的样子:

现在,我制作了一个可重用的
UserControl
,它有三个用于标签字符串的依赖属性
Label
Value
用于在ViewModel中绑定属性,以及
Units
——一个用于显示输入单位的字符串属性

<UserControl ...
             x:Name="parent">
    <StackPanel DataContext="{Binding ElementName=parent}">
        <TextBlock Text="{Binding Path=Label}"/>
        <TextBox Text="{Binding Path=Value}"/>
        <TextBlock Text="{Binding Path=Units}"/>
    </StackPanel>

但是,此可重用控件只能处理输入的第一个
文本框。我不知道如何绑定第二个
文本框中的
IValueConvertor
。我需要这样做,因为我想绑定其他转换器,如米到英尺,千克到磅,等等

我已经读到,
ConvertorParameter
无法绑定,因为它不是依赖属性,我不确定是否可以使用多重绑定,主要是因为我不知道如何正确使用它

如果您能告诉我如何执行此操作,或将我引导到StackOverflow上的相应链接或解决此问题的其他位置,我将非常感激。或者如果有更好的方法


非常感谢

不确定如何将多个转换器绑定到一个控件中。如果我没有错的话,您希望构建一个控件,当用户输入特定值时,您需要以不同的单位显示它。如果是这种情况,您可以创建一个转换器,转换器参数为“m”、“cm”、“inch”等,并在此基础上返回结果。在本例中,您将有4,5个控件,每个控件都有相同的转换器绑定,但转换器值不同。如果不清楚,您需要进一步的指导,请告知

多值绑定

要回答您的第6点,请参阅下面的示例多绑定转换器及其在xaml中的实现。我已经构建了一个简单的RolesFilter,它将从xaml获取不同的输入作为对象[],因为我已经知道需要什么样的数据,所以我正在转换器中转换它们

public class RolesFilter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            FlipperObservableCollection<Role> _roles = (FlipperObservableCollection<Role>)values[0]; //Input
            Department _dept_param = values[1] as Department;
            bool _filter = (bool)values[2];
            string _id = "NA";
            if (values.Count() == 4 && values[3] is string) _id = (string)values[3] ?? "NA";

            //If we need a filter, then without department, it should return empty results
            if (!_filter) return _roles; //If no filter is required, then don't worry, go ahead with input values.
            if (_dept_param == null) return new FlipperObservableCollection<Role>(); //If department is null, then 
            List<Role> _filtered_list = _roles.ToList().Where(p => p.department.id == _dept_param.id && p.id != _id)?.ToList() ?? new List<Role>();
            return new FlipperObservableCollection<Role>(_filtered_list);
        }
        catch (Exception)
        {
            throw;
        }
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
公共类角色过滤器:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
尝试
{
FlipperObservableCollection _roles=(FlipperObservableCollection)值[0];//输入
部门_dept_param=作为部门的值[1];
bool _filter=(bool)值[2];
字符串_id=“NA”;
如果(values.Count()==4&&values[3]为字符串)_id=(字符串)值[3]??“NA”;
//如果我们需要一个过滤器,那么没有部门,它应该返回空结果
如果(!\u filter)返回_roles;//如果不需要过滤器,那么不用担心,继续输入值。
如果(_dept_param==null)返回新的FlipperObservableCollection();//如果department为null,则
列表(过滤)列表=(角色)列表().其中(p=>p.department.id==(部门)参数id&&p.id!=(id))?.ToList()?新建列表();
返回新的FlipperObservableCollection(\u筛选的\u列表);
}
捕获(例外)
{
投掷;
}
}
公共对象[]转换回(对象值,类型[]目标类型,对象参数,CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}
我在xaml中使用多值转换器,如下所示。在这里,我根据另一个组合框和复选框过滤组合框的itemsource。这只是一个示例,在您的示例中,您可以创建一个具有不同单位值的组合框。根据用户选择,您可以使用转换器并将值返回到文本框

<ComboBox Height="30" SelectedItem="{Binding reports_to, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
                        <ComboBox.ItemsSource>
                            <MultiBinding Converter="{StaticResource roles_filter}">
                                <Binding Source="{StaticResource SingletonData__}" Path="roles" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
                                <Binding Path="department" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
                                <Binding ElementName="cbx_filter" Path="IsChecked"/>
                                <Binding Path="id" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
                            </MultiBinding>
                        </ComboBox.ItemsSource>
                        <ComboBox.ItemTemplate>
                            <DataTemplate>
                                <WrapPanel>
                                    <TextBlock Text="{Binding department.name}"/>
                                    <TextBlock Text=" - "/>
                                    <TextBlock Text="{Binding name}"/>
                                </WrapPanel>
                            </DataTemplate>
                        </ComboBox.ItemTemplate>
                    </ComboBox>

首先,不要将
文本框
彼此绑定(如问题开头的原始代码),而是将每个
文本框
绑定到相同的支持属性,在
用户控件
中,该属性是

至于如何实现多个绑定,您可能不需要
多绑定

我们必须首先选择一个“标准”度量单位-这将是实际存储在属性和任何数据库或文件中的单位。我假设这个标准单位是米(m)。
IValueConverter
可用于在米和其他距离单位之间进行转换,并使用
ConverterParameter
指定要转换为/从哪个其他单位进行转换

这里有一个很好的例子让你开始

public enum DistanceUnit { Meter, Foot, Inch, }

public class DistanceUnitConverter : IValueConverter
{
    private static Dictionary<DistanceUnit, double> conversions = new Dictionary<DistanceUnit, double>
    {
        { DistanceUnit.Meter, 1 },
        { DistanceUnit.Foot, 3.28084 },
        { DistanceUnit.Inch, 39.37008 }
    };

    //Converts a meter into another unit
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return conversions[(DistanceUnit)parameter] * (double)value;
    }

    //Converts some unit into a meter 
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) { return 0; }

        double v;

        var s = value as string;

        if (s == null)
        {
            v = (double)value;
        }
        else
        {
            if (s == string.Empty) { return 0; }
            v = double.Parse(s);
        }

        if (v == 0) { return 0; }

        return v / conversions[((DistanceUnit)parameter)];
    }
}

距离单位
枚举
和内部
转换
字典可以扩展为更多度量单位。或者,您可以使用已经包含所有这些内容的第三方库,例如。

而不是将转换器保存在mainwindow.xaml代码中,而是将它们保存在单独的.cs文件中。您的第6点是正确的,您不能绑定到转换器参数,多重绑定就是答案。再次感谢。我已经把他们转移到了一个单独的班级。嗨,基思,谢谢你快速而详细的回复。这太棒了!每天我都在学习新的东西,谢谢你教我一个新的技巧。Th
public enum DistanceUnit { Meter, Foot, Inch, }

public class DistanceUnitConverter : IValueConverter
{
    private static Dictionary<DistanceUnit, double> conversions = new Dictionary<DistanceUnit, double>
    {
        { DistanceUnit.Meter, 1 },
        { DistanceUnit.Foot, 3.28084 },
        { DistanceUnit.Inch, 39.37008 }
    };

    //Converts a meter into another unit
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return conversions[(DistanceUnit)parameter] * (double)value;
    }

    //Converts some unit into a meter 
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) { return 0; }

        double v;

        var s = value as string;

        if (s == null)
        {
            v = (double)value;
        }
        else
        {
            if (s == string.Empty) { return 0; }
            v = double.Parse(s);
        }

        if (v == 0) { return 0; }

        return v / conversions[((DistanceUnit)parameter)];
    }
}
<StackPanel>
    <StackPanel.Resources>
        <local:DistanceUnitConverter x:Key="DistCon"/>
    </StackPanel.Resources>

    <StackPanel Orientation="Horizontal">
        <TextBox Text="{Binding Distance, Converter={StaticResource DistCon}, ConverterParameter={x:Static local:DistanceUnit.Meter}}" MinWidth="20"/>
        <TextBlock>m</TextBlock>
    </StackPanel>

    <StackPanel Orientation="Horizontal">
        <TextBox Text="{Binding Distance, Converter={StaticResource DistCon}, ConverterParameter={x:Static local:DistanceUnit.Foot}}" MinWidth="20"/>
        <TextBlock>ft</TextBlock>
    </StackPanel>
</StackPanel>