在双向绑定中使用当前DataContext的IValueConverter

6

我正在使用一个转换器,将字符串转换为我们的时间格式,但是遇到了一些问题。转换器本身运行良好,实现方式如下:

    [ValueConversion(typeof(string), typeof(SimpleTime))]
    public class StringToSimpleTimeConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // convert from string to SimpleTime and return it
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // convert value from SimpleTime to string and return it
        }
    }

XAML使用转换器时,需要在usercontrol.resources中像这样包含转换器本身:
<converter:StringToSimpleTimeConverter x:Key="stringToSimpleTimeConverter"/>

如果遇到该属性(我在后台使用wpf工具包中的datagrid),则会使用simpletime的数据模板进行编辑:
<DataTemplate x:Key="SimpleTimeEditingTemplate">
        <TextBox Text="{Binding, Converter={StaticResource stringToSimpleTimeConverter}, Mode=TwoWay}"/>
</DataTemplate>

我遇到的问题是,如果是双向转换器(我需要在两个方向上使用),则绑定中需要指定路径,但我想设置的属性已经是当前的DataContext - 那我应该指定什么路径呢?
我能想到的唯一解决方法是,在SimpleTime中引入一个虚拟属性,它只获取当前的SimpleTime或设置它。
public class SimpleTime
{
    ...
    public SimpleTime Clone
    {
        get { return new SimpleTime(_amount, _format); }
        set { this._amount = value._amount; this._format = value._format; }
    }
}

并通过绑定到该内容进行操作

 <TextBox Text="{Binding Clone, Converter={StaticResource stringToSimpleTimeConverter}, Mode=TwoWay}"/>

这个方法虽然可以正常工作,但并不是一个合适的解决方案,特别是如果我需要多次转换...

非常感谢您的帮助!祝好,manni

3个回答

5
我认为你可以这样解决它。
<TextBox Text="{Binding Path=DataContext,
                        RelativeSource={RelativeSource Self},
                        Converter={StaticResource stringToSimpleTimeConverter}, 
                        Mode=TwoWay}"/>

我尝试过使用父用户控件来绑定数据上下文的方式,但是在我的数据结构中,simpletime属性的设置从未被调用。虽然IValueConverter正确地将字符串转换为SimpleTime并显示在控件中,但这种情况如何发生呢?请问有人知道吗? - manni
@manni:我用这个制作了一个测试应用程序,对我来说似乎完全正常。它绑定到自己的DataContext,所以我认为它没有不工作的理由。你确定你的ConvertBack方法从未被调用过吗? - Fredrik Hedblad
1
感谢您的努力。我认为我的意思没有表达清楚:如果我在GUI中更改值,我的ConvertBack和Convert方法会被调用,这很好。但是我正在设置的属性(simpletime属性)的属性设置器没有被调用(断点未触发)。我现在会去看一下,非常感谢您的帮助,我真的很感激。 - manni
我已经构建了另一个示例,当直接使用您的方法时,它可以正常工作。但是在DataTemplate或DataGridColumn中使用它时,似乎出现了某个错误。再次感谢您的帮助。 - manni
为什么不直接使用"{Binding Converter={StaticResource stringToSimpleTimeConverter}, Mode=TwoWay}",即去掉逗号呢? - Tavian Barnes

0

我知道这是一个老问题,但如果有人偶然发现了这个问题,正确的路径应该是Path=.

<DataTemplate x:Key="SimpleTimeEditingTemplate">
        <TextBox Text="{Binding Path=., Converter={StaticResource stringToSimpleTimeConverter}, Mode=TwoWay}"/>
</DataTemplate>

0

不要在你的类中引入一个虚拟属性,为什么不创建一个容器类,像下面这样:

public class Container
{
    public Object DataItem { get; set; }

    //...
}

并像这样使用它:

<TextBox Text="{Binding DataItem, Converter={StaticResource stringToSimpleTimeConverter}, Mode=TwoWay}"/>

这不会破坏/改变你现有的类,并且仍然允许你做你想做的事情。

好主意,但对于我的数据结构来说并不是很可行。我不想把每个属性都放进容器里,而且父List对象直接绑定到DataGrid上,它会为类中的每个属性选择一个要显示和编辑的列,这些列将直接作为数据上下文使用属性。 - manni
1
我认为你正在做的是将每个属性设置为ControlDataContext,并使用{Binding}进行绑定而不指定路径。如果这就是你正在做的事情,那么它是错误的。你应该设置父控件的DataContext,所有子控件都会继承它。只在子控件中使用不同路径的绑定。 - decyclone
我曾认为DataGridColumns是这样工作的。我在DataGrid中用于显示或编辑GridCell的DataTemplate不能将DataContext设置为完整结构,因为那意味着它必须知道应用于哪个属性的名称。我想要的是将DataTemplate应用于特定类型而不知道其名称,因此我的CodeBehind设置了DataGridColumn的绑定并设置了其用于显示和编辑的DataTemplates。DataTemplate本身适用于多个PropertyName,具体取决于属性的类型。 - manni
经过多次尝试和思考,我得出了结论。它是错误的,因为它不能切换绑定整个属性,而是应该在父数据上下文中切换属性。我需要改变我的设计来实现这一点。感谢您的帮助,可惜我不能将两个答案都标记为正确,再次感谢。 - manni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接