5-2.Binding指定源

WPF绑定源详解

5-2.Binding指定源

指定源的集中方法:

  • 普通对象
  • 集合
  • ADO.NET
  • XML
  • 依赖对象
  • DataContext
  • 通过ElementName
  • 通过RelativeSource
  • ObjectDataProvider
  • Linq

DataContext作为源

DataContext是WPF控件的基类属性,也就是WPF树形结构都具有这个属性,当一个Binding只有Path而没有Source时,会沿着UI元素树一路向树的根部搜索过去,看哪个节点的DataContext具有Path所指向的属性,如果有就把这个对象作为自己的Source,如果没有就一直找下去。

如果不需要数据时,Source属性也可以直接不写Text="{Binding Path=Name}"

自动向根节点查询的原理:DataContext是一个依赖属性,依赖属性的特点是当没有为控件的某个属性显式赋值时,控件会把自己容器的属性当做自己的属性值,也就是属性值沿着UI元素向下传递了。

使用场景:

  1. UI上多个控件关注同一对象
  2. 当Source的对象不能被直接访问时,窗体B想访问窗体A的控件,但是窗体A的控件是Private,这时可以把窗体A的控件作为A的DataContext,因为DataContext属性是Public。

使用集合对象作为列表控件的源

只要为一个ItemsControl对象设置了ItemsSource,就会自动迭代其中的数据元素,并为每个元素准备一个条目容器(条目容器就是数据的外衣),并使用Binding在条目容器和数据元素之间建立联系。

//为listBox设置Binding
this.listBoxStudents.ItemsSource = stuList;
this.listBoxStudents.DisplayMemberPath="Name";
//当DisplayMemberPath属性被赋值后,ListBox在获得ItemsSource的时候就会创建等量的ListBoxItem条目容器
//并以DisplayMemberPath属性值为Path创建Binding,Binding的目标是ListBoxItem的内容插件(一个TextBox)。

以上创建Binding的过程是在DisplayMemberTemplateSelector类中的SelectTemplate方法中完成的,该方法的返回值为DataTemplate类型,ListBox的ItemTemplate属性的类型是DataTemplate。

自定义设置DataTemplate案例

 <StackPanel>
        <TextBlock Text="id"/>
        <TextBox x:Name="txtBoxId" Text="{Binding Path=SelectedItem.Id, ElementName=listBox}"/>
        <TextBlock Text="student List"/>
        <ListBox x:Name="listBox">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Id}"/>
                    <TextBlock Text="{Binding Name}"/>
                    <TextBlock Text="{Binding Age}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
</StackPanel>

在构造函数中指定源this.listBox.ItemsSource = stuList;

在使用集合作为列表的ItemsSource时,一般考虑用ObservableCollection<T>代替List<T>,因为ObservableCollection<T>实现了INotifyCollectionChanged和INotifyPropertyChanged接口,能把集合的变化立刻通知控件进行显示。

ADO.NET对象作为源

this.listView.ItemsSource = stuList;
DataTable dt = Load();
this.listView.ItemsSource = dt.DefaultView;//DataView实现了IEnumerable接口

//不能直接将dt作为ItemsSource使用
//但是当把DataTable放在一个对象的DataContext中,并把ItemsSource与一个既没有指定Source又没有指定Path的Binding关联起来
//Binding会自动找到它的DefaultView作为自己的Source来使用
<StackPanel>
    <ListView x:Name="listView">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Id" Width="60" DisplayMemberBinding="{Binding Id}"/>
                <GridViewColumn Header="Name" Width="60" DisplayMemberBinding="{Binding Name}"/>
                <GridViewColumn Header="Age" Width="60" DisplayMemberBinding="{Binding Age}"/>
            </GridView>
        </ListView.View>
    </ListView>
</StackPanel>

ListView是ListBox的派生类,ListView的View属性是一个ViewBase类型(GridView的基类)。

GridView的内容属性是Columns(GridViewColumnCollection类型对象),GridViewColumn对象一个重要属性是DisplayMemberBinding,功能类似于ListBox的DisplayMemberPath属性。如果用更复杂的结构来表示Header和数据,可以为GridViewColumn设置HeaderTemplate和CellTemplate属性,类型都是DataTemplate。

XML作为源

使用XML数据作为Binding的Source时要使用XPath属性而不是Path属性来指定数据的来源

<!--在文件中的xml-->
<StudentList>
    <Student Id="1">
    	<Nmae>Tim</Nmae>
    </Student>
    <Student Id="2">
    	<Nmae>Tom</Nmae>
    </Student>
    <Student Id="3">
    	<Nmae>Vina</Nmae>
    </Student>
</StudentList>


<StackPanel>
    <ListView x:Name="listView">
        <ListView.View>
            <GridView>
                <!--@符号表示使用的Attribut,不加@符合表示子级元素-->
                <GridViewColumn Header="Id" Width="80" DisplayMemberBinding="{Binding XPath=@Id}"/>
                <GridViewColumn Header="Name" Width="80" DisplayMemberBinding="{Binding XPath=Name}"/>
            </GridView>
        </ListView.View>
    </ListView>
</StackPanel>
XmlDocument doc = new XmlDocument();
doc.Load(xmlPath);

XmlDataProvider xdp = new XmlDataProvider();
xdp.Document = doc;//也可以直接写成xdp.Source = new Uri(xmlPath)


xdp.XPath = @"StudentList/Student";
this.listView.DataContext = xdp;
this.listView.SetBinding(ListView.ItemsSourceProperty, new Binding());

也可以把XML数据和XMLDataProvider对象直接写在XAML代码中

<Window.Resources>
    <XmlDataProvider x:Key="xdp" XPath="FileSystem/Folder">
        <x:XData>
            <FileSystem xmlns="">
                <Folder Name="PRO">
                    <Folder Name="Win">
                        <Folder Name="A"/>
                        <Folder Name="A2"/>
                        <Folder Name="A3"/>
                        <Folder Name="A4"/>
                    </Folder>
                </Folder>
                <Folder Name="PRO1">
                    <Folder Name="Win1">
                        <Folder Name="A1"/>
                        <Folder Name="A12"/>
                        <Folder Name="A13"/>
                        <Folder Name="A14"/>
                    </Folder>
                </Folder>
            </FileSystem>
        </x:XData>
    </XmlDataProvider>
</Window.Resources>
<StackPanel>
    <TextBlock Text="ok"/>
    <TreeView ItemsSource="{Binding Source={StaticResource xdp}}" >
       
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding XPath=Folder}">
                <TextBlock Text="{Binding XPath=@Name}"/>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</StackPanel>

LINQ结果作为源

Linq查询结果是一个IEnumberable<T>,可以作为列表控件的ItemsSource使用。

this.listView.ItemsSource = from stu in stuList where stu.Name.StartsWith("T") Select stu;

ObjectDataProvider作为源

当需要的数据没有被暴露出来,如设置了private,此时就需要使用ObjectDataProvider,它把对象作为数据源提供给Binding,前面用的XmlDataProvider和ObjectDataProvider的父类都是DataSourceProvider抽象类。

<StackPanel>
    <TextBox x:Name="txtArg1"/>
    <TextBox x:Name="txtArg2"/>
    <TextBox x:Name="txtResult"/>
</StackPanel>
public class Calculator
{
    public string Add(string a,string b)
    {
        return a + b;
    }
}


private void SetBindging1()
{
    //创建并配置ObjectDataProvider对象
    ObjectDataProvider odp = new ObjectDataProvider();
   // odp.ObjectInstance = new Calculator();//把一个Calculator对象包装在了ObjectInstance中
    //另一种方式
    //odp.ObjectType = typeof(Calculator);
    //odp.ConstructorParameters.Add("0");
    //odp.ConstructorParameters.Add("0");
    odp.MethodName = "Add";
    odp.MethodParameters.Add("0");
    odp.MethodParameters.Add("0");

    //以ObjectDataProvider为source创建Binding
    Binding bindingToArg1 = new Binding("MethodParameters[0]") {//path是ObjectDataProvider对象MethodParameters属性的第一个元素
        Source = odp,
        BindsDirectlyToSource =true,//Binding只负责把从UI收集到的数据写入Source(ObjectDataProvider对象),而不是被Provider包装到的Calculator对象
        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged //一旦修改立即传回source
    };
    Binding bindingToArg2 = new Binding("MethodParameters[1]")
    {
        Source = odp,
        BindsDirectlyToSource = true,
        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
    };

    Binding bindingToResult = new Binding(".") { Source = odp };

    //将Binding关联到UI元素上
    this.txtArg1.SetBinding(TextBox.TextProperty, bindingToArg1);
    this.txtArg2.SetBinding(TextBox.TextProperty, bindingToArg2);
    this.txtResult.SetBinding(TextBox.TextProperty, bindingToResult);
}

  • 将枚举作为Combobox的ItemsSource的案例
<ObjectDataProvider
    x:Key="CombinationTypeEnum"
    MethodName="GetValues"
    ObjectType="{x:Type sys:Enum}">
    <ObjectDataProvider.MethodParameters>
        <x:Type TypeName="model:CombinationType" />
    </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

<!--使用-->
<ComboBox ItemsSource="{Binding Source={StaticResource WorkConditionGradeEnum}}"/>

上面ObjectDataProvider对应的C#代码为Array a= System.Enum.GetValues(typeof(CombinationType));
一般情况下,认为数据从哪里来,哪里就是source,到哪去哪里就是Target。所以会认为前两个TextBox应该是ObjectDataProvider的数据源,但实际上,3个TextBox都是以ObjectDataProvider为数据源,前两个TextBox只是在数据流上做了限制。

使用Binding的RelativSource

当不确定源的名字,但是知道源和目标在UI上的相对关系,这时便可以使用RelativeSource属性。

RelativSource属性的数据类型为RelativeSource,可以通过这个类控制搜索相对数据源的方式。

 <Grid x:Name="g1" Background="Red" Margin="10">
     <DockPanel x:Name="d1" Background="Gray" Margin="10">
         <Grid x:Name="g2" Background="Blue" Margin="10">
             <DockPanel x:Name="d2" Background="Yellow" Margin="10">
                 <TextBox x:Name="txtBox" Background="Green" Margin="10"/>
             </DockPanel>
         </Grid>
     </DockPanel>
 </Grid>

RelativeSource rs = new RelativeSource(RelativeSourceMode.FindAncestor);
rs.AncestorLevel = 1;//设置偏移量
rs.AncestorType = typeof(Grid);//源类型
Binding binding = new Binding("Name") { RelativeSource = rs };
this.txtBox.SetBinding(TextBox.TextProperty, binding);

在XAML的等效代码

<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Grid},AncestorLevel=1},Path=Name}"/>

关联自身Name属性

RelativeSource rs = new RelativeSource(RelativeSourceMode.Self);
Binding binding = new Binding("Name") { RelativeSource = rs };
this.txtBox.SetBinding(TextBox.TextProperty, binding);


Name}"/>`

[外链图片转存中…(img-enyq0nVc-1757199624767)]

关联自身Name属性

RelativeSource rs = new RelativeSource(RelativeSourceMode.Self);
Binding binding = new Binding("Name") { RelativeSource = rs };
this.txtBox.SetBinding(TextBox.TextProperty, binding);

[外链图片转存中…(img-LsH9Uzgt-1757199624767)]

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.example.demo.mapper.UserMapper.selectById at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:235) ~[mybatis-3.5.9.jar:3.5.9] at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:53) ~[mybatis-3.5.9.jar:3.5.9] at org.apache.ibatis.binding.MapperProxy.lambda$cachedInvoker$0(MapperProxy.java:108) ~[mybatis-3.5.9.jar:3.5.9] at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) ~[na:1.8.0_65] at org.apache.ibatis.util.MapUtil.computeIfAbsent(MapUtil.java:35) ~[mybatis-3.5.9.jar:3.5.9] at org.apache.ibatis.binding.MapperProxy.cachedInvoker(MapperProxy.java:95) ~[mybatis-3.5.9.jar:3.5.9] at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86) ~[mybatis-3.5.9.jar:3.5.9] at com.sun.proxy.$Proxy63.selectById(Unknown Source) ~[na:na] at com.baomidou.mybatisplus.extension.service.IService.getById(IService.java:292) ~[mybatis-plus-extension-3.5.1.jar:3.5.1] at com.baomidou.mybatisplus.extension.service.IService$$FastClassBySpringCGLIB$$f8525d18.invoke(<generated>) ~[mybatis-plus-extension-3.5.1.jar:3.5.1] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.23.jar:5.3.23] at org.springframework.aop.framework.CglibAopProxy.invokeMethod(CglibAopProxy.java:386) ~[spring-aop-5.3.23.jar:5.3.23] at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:85) ~[spring-aop-5.3.23.jar:5.3.23] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:704) ~[spring-aop-5.3.23.jar:5.3.23] at com.example.demo.service.serviceImpl.UserServiceImpl$$EnhancerBySpringCGLIB$$323e7f8d.getById(<generated>) ~[classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_65] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_65] at sun.reflect
最新发布
03-08
<think>好的,我现在需要帮助用户解决MyBatis中出现的Invalid bound statement错误,具体是UserMapper的selectById方法找不到。这个问题看起来是MyBatis常见的绑定异常,通常和Mapper接口与XML文件的映射有关。让我仔细想想可能的原因和解决方法。 首先,用户提到了使用MyBatis-Plus时出现这个问题,可能涉及多数据或ShardingSphere的配置,根据引用[3],自定义数据配置可能导致原生BaseMapper方法失效。所以需要检查SqlSessionFactory的配置是否正确,是否漏掉了MyBatis-Plus的插件或配置。 然后,引用[4]提到了几个常见原因,比如XML中的namespace是否正确对应Mapper接口,方法名是否和XML中的id一致,编译后的XML是否存在,以及实体类是否有@TableId注解。这些都是需要逐步检查的点。 接下来,我需要将这些可能的原因整理成一个清晰的步骤,帮助用户逐一排查。首先要检查XML的namespace和方法id是否正确,然后确保XML文件被正确打包到target目录,接着检查依赖和构建配置,比如Maven的资过滤设置,最后是MyBatis-Plus的配置是否正确,尤其是多数据情况下是否配置了分页插件等。 此外,用户的问题可能涉及实体类注解,比如是否使用了@TableId来指定主键,这会影响selectById方法的使用。所以需要提醒用户检查实体类是否有正确的注解。 最后,总结常见的解决步骤,并给出相关的引用链接,方便用户进一步查阅。可能还需要生成相关问题,帮助用户扩展了解类似的问题,比如其他方法的绑定异常或配置问题。</think>### 如何解决 MyBatis 的 `Invalid bound statement (not found)` 错误? #### 问题分析 错误信息 `Invalid bound statement (not found): UserMapper.selectById` 表明 MyBatis 无法找到与 `UserMapper` 接口中 `selectById` 方法对应的 SQL 映射。以下是分步排查和解决方法: --- #### 1. 检查 Mapper 接口与 XML 映射的对应关系 - **确认 XML 文件的 `namespace`** XML 文件中的 `namespace` 必须与 Mapper 接口的全限定名一致。例如: ```xml <!-- UserMapper.xml --> <mapper namespace="com.example.mapper.UserMapper"> <select id="selectById" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> </mapper> ``` - **检查方法名与 XML 标签的 `id` 是否一致** Mapper 接口中的方法名(如 `selectById`)必须与 XML 中 `<select>` 标签的 `id` 完全一致,区分大小写[^4]。 --- #### 2. 确保 XML 文件被正确编译到 `target` 目录 - **检查构建工具配置** 如果使用 Maven,需在 `pom.xml` 中添加资过滤配置,确保 XML 文件被复制到 `target/classes`: ```xml <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build> ``` 执行 `mvn clean compile` 后,检查 `target/classes` 目录是否存在 `UserMapper.xml`。 --- #### 3. 验证 MyBatis-Plus 配置 - **检查实体类注解** 确保实体类的字段(如主键 `id`)添加了 `@TableId` 注解: ```java @Data @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; private String name; } ``` - **多数据或 ShardingSphere 的特殊处理** 如果项目使用了多数据或 ShardingSphere,需手动配置 `SqlSessionFactory` 并注入 MyBatis-Plus 插件(如分页插件)[^3]: ```java @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setPlugins(new PaginationInterceptor()); // 添加分页插件 return factory.getObject(); } ``` --- #### 4. 其他常见问题 - **依赖冲突** 检查是否存在 MyBatis 和 MyBatis-Plus 版本冲突,建议使用统一版本: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> ``` - **IDE 缓存问题** 清理 IDE 缓存并重新构建项目(如 IntelliJ 的 `File > Invalidate Caches`)。 --- #### 总结步骤 1. 检查 XML 的 `namespace` 和方法 `id` 是否正确。 2. 确认 XML 文件被编译到 `target` 目录。 3. 验证实体类注解和 MyBatis-Plus 配置。 4. 清理并重新构建项目。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值