如何在CheckBox中使ContentPresenter中的绑定工作?

时间:2017-03-09 12:56:57

标签: c# wpf xaml

CheckBox中的绑定似乎有问题。它抛出一个XamlParseException,并说双向绑定需要Path或XPath。以下是ContentPresenter进行评估。

<ContentPresenter Content="{Binding PropertyValue}">
    <ContentPresenter.Resources>
        <DataTemplate DataType="{x:Type System:String}">
            <TextBox Text="{Binding}" MinWidth="100" MaxWidth="300"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type System:Boolean}">
            <CheckBox IsChecked="{Binding}"/>
        </DataTemplate>
    </ContentPresenter.Resources>
</ContentPresenter>

我希望实现的是PropertyValue,它是一个多态对象,它的数据类型,相应的控件将在运行时创建。例如,如果PropertyValue是布尔属性,则控件将是一个复选框。如果用户切换复选框,则更改应反映在PropertyValue上。如果外部依赖项修改了PropertyValue(例如,INotifyPropertyChanged),则复选框也必须反映该更改。因此,我需要一种方法来实现双向绑定。

1 个答案:

答案 0 :(得分:0)

错误非常精确......您不能双向绑定当前的DataContext,而只能绑定上下文中的属性。

<DataTemplate DataType="{x:Type ClassTypeContainingABoolean}">
    <CheckBox IsChecked="{Binding YourBooleanProperty}"/>
</DataTemplate>

<DataTemplate DataType="{x:Type System:Boolean}">
    <CheckBox IsChecked="{Binding Mode=OneWay}"/>
</DataTemplate>

取决于你真正想做的事情。

整件事与ContentPresenter无关。

如果要在决定模板之前检查datacontext对象,可以使用TemplateSelector

public class MyTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var elem = container as FrameworkElement;
        if (item == null || elem == null)
        {
            return null;
        }
        var t = item.GetType();
        var prop = t.GetProperty("PropertyValue");
        if (prop == null)
        {
            return null;
        }
        // select template by the type of the PropertyValue parameter
        if (typeof(bool?).IsAssignableFrom(prop.PropertyType))
        {
            return elem.FindResource("BooleanContextTemplate") as DataTemplate;
        }
        if (typeof(string).IsAssignableFrom(prop.PropertyType))
        {
            return elem.FindResource("StringContextTemplate") as DataTemplate;
        }
        return base.SelectTemplate(item, container);
    }
}

数据上下文初始化,例如:

public class MyContextData<T>
{
    public T PropertyValue { get; set; }
}

// initialize DataContext somewhere
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    grid1.DataContext = new MyContextData<bool>() { PropertyValue = true };
    //grid1.DataContext = new MyContextData<string>() { PropertyValue = "ABC" };
}

Xaml示例:

<Window.Resources>
    <local:MyTemplateSelector x:Key="MyContextDataTemplateSelector"/>

    <DataTemplate x:Key="StringContextTemplate">
        <TextBox Text="{Binding PropertyValue}" MinWidth="100" MaxWidth="300"/>
    </DataTemplate>

    <DataTemplate x:Key="BooleanContextTemplate">
        <CheckBox IsChecked="{Binding PropertyValue}"/>
    </DataTemplate>
</Window.Resources>

<Grid x:Name="grid1">
    <ContentPresenter Content="{Binding}" ContentTemplateSelector="{StaticResource MyContextDataTemplateSelector}"/>
</Grid>

如您所见,模板选择器通过DataTemplate引用的FindResource("BooleanContextTemplate")需要在某处定义。 templateselector本身也是在资源中创建的,并传递给ContentPresenter.ContentTemplateSelector属性。

请注意,ContentPresenter.Content绑定不再定位YourBooleanProperty,而此属性是模板的一部分。因此,最终,您的数据需要以某种确定性的方式包含在内,关于涉及的属性名称。