zoukankan      html  css  js  c++  java
  • WPF 基础

    1. 源与路径

    1. 把控件作为 binding 源与 binding 标记拓展;
    2. 控制 Binding 的方向及数据更新;
    3. Binding 的路径 Path;
    4. 没有路径的 Binding;
    5. 为 Binding 指定源的几种方法;
    6. 没有 Source 的 Binding;
    7. 使用集合对象作为列表控件的 ItemsSource;
    8. 使用 ADO.NET 对象作为 Binding 的源;
    9. 使用 XML 的数据作为 Binding 的源;
    10. 使用 LINQ 的检索结果作为 Binding 的源:
    11. 使用 ObjectDataProvider 对象作为 Binding 的 Source;
    12. 使用 Binding 的 RelativeSource;

    Binding 作为数据的桥梁,两端分别是源 (Source) 和目标 (Target),同时 Target 作为对象可能会有多个属性值,通过 路径 (Path) 指定通过 Binding 送达 UI 元素的属性;
    source:可以为集合对象、ADO.NET 对象、XML 数据、LINQ 检索结果、ObjectDataProvider 对象等;

    1.1 把控件作为 binding 源与 binding 标记拓展

    <Slider x:Name="slider" Maximum="100" Minimum="0"/>
    <TextBlock x:Name="textBlock" Text="{Binding ElementName=slider, Path=Value}"/>
    <!--Binding 构造器本身接收 Path 作为参数,因此-->
    <TextBlock x:Name="textBlock" Text="{Binding Value, ElementName=slider}"/>
    
    //等价于后台:
    this.textBlock.SetBinding(TextBlock.TextProperty, new Binding("Value") { ElementName = "slider" });
    

    1.2 控制 Binding 的方向及数据更新

    Binding 的属性 Mode 的枚举值决定 Binding 的更新方向:

    • OneWay
    • OneWayToSource
    • OneTime
    • TwoWay
    • Default :不同控件的默认值不一样,比如文本框和复选框默认双向绑定,TextBlock 单向

    Binding 的属性 UpdateSourceTrigger 的枚举值决定 Binding 的触发数据更新的条件:

    • Explicit :调用 System.Windows.Data.BindingExpression.UpdateSource 方法时才更新
    • PropertyChanged
    • LostFocus
    • Default : 大多数默认值是 PropertyChanged,而 System.Windows.Controls.TextBox.Text 是 LostFocus

    NotifyOnSourceUpdated、NotifyOnTargetUpdated bool值
    当值从绑定目标传输到绑定源时是否引发 System.Windows.Data.Binding.SourceUpdated 事件;
    当值从绑定源传输到绑定目标时是否引发 System.Windows.Data.Binding.TargetUpdated 事件。
    如下面例子:修改 slider 的值,引发 TargetUpdated 事件;
    修改 textBox 的值,引发 SourceUpdated 事件;可以记录。

    <TextBox x:Name="textBlox" 
             TargetUpdated="textBlock_TargetUpdated" 
             SourceUpdated="textBlock_SourceUpdated" 
             Text="{Binding Path=Value, ElementName=slider, UpdateSourceTrigger=PropertyChanged,
                    NotifyOnSourceUpdated=True,
                    NotifyOnTargetUpdated=True}"/>
    

    1.3 Binding 的路径 Path

    除了上面所说,还支持多级路径

    <TextBox x:Name="textBox2" Text="{Binding Path=Text.Length, ElementName=textBox, Mode=OneWay}"/>
    //等价于:
    this.textBox2.SetBinding(TextBox.TextProperty, new Binding("Text.Length") { Source = this.textBox, Mode = BindingMode.OneWay });
    
    <TextBox x:Name="textBox2" Text="{Binding Path=Text.[3], ElementName=textBox, Mode=OneWay}"/>
    
    List<string> stringList = new List<string>() { "Bob", "Long" };
    this.xx1.SetBinding(TextBox.TextProperty, new Binding("/") { Source = stringList });//Bob
    this.xx2.SetBinding(TextBox.TextProperty, new Binding("/Length") { Source = stringList, Mode = BindingMode.OneWay });//3
    this.xx3.SetBinding(TextBox.TextProperty, new Binding("/[2]") { Source = stringList, Mode = BindingMode.OneWay });//b
    

    1.4 没有路径的 Binding

    比如 int,string 等本身就是数据,因此用 Path=. 来表示,在 xaml 中可以省略,在 C# 中不能省略

    <Window.Resources>
        <sys:String x:Key="myString">悟空的小脾气</sys:String>
    </Window.Resources>
    <TextBlock Text="{Binding Path=., Source={StaticResource myString}}"/>
    <TextBlock Text="{Binding ., Source={StaticResource myString}}"/>
    <TextBlock Text="{Binding Source={StaticResource myString}}"/>
    
    <TextBlock x:Name="knowNull"/>
    
    this.knowNull.SetBinding(TextBlock.TextProperty, new Binding(".") { Source = this.FindResource("myString")});
    
    // 4 个 TextBlock 都展示 "悟空的小脾气"
    

    1.5 没有 Source 的 Binding

    1.5.1 无 Source 的 Binding

    当一个 Binding 只知道自己的 Path,而不知自己的 Source 时,会沿着 UI 元素树一层一层树的根部找过去,直到找到有设置 DataContext 的元素。

    <Window ..>
        <StackPanel>
            <StackPanel.DataContext>
                <local:Student Id="6" Age="20"/>
            </StackPanel.DataContext>
            <Grid>
                <StackPanel>
                    <TextBlock Text="{Binding Id}" />
                    <TextBlock Text="{Binding Age}" />
                </StackPanel>
            </Grid>
        </StackPanel>
    </Window
    

    1.5.2 无 Path 无 Source 的 Binding

    <Window ..>
        <StackPanel>
            <StackPanel.DataContext>
                <sys:String>Hello~</sys:String>
            </StackPanel.DataContext>
            <Grid>
                <StackPanel>
                    <TextBlock Text="{Binding}" />
                </StackPanel>
            </Grid>
        </StackPanel>
    </Window
    

    1.5.3 测试 DataContext 往下传递

    <Grid>
        <Grid.DataContext>
            <local:Student Id="1000" Age="100"></local:Student>
        </Grid.DataContext>
        <StackPanel>
            <StackPanel.DataContext>
                <local:Stu Id="6" Play="ball"></local:Stu>
            </StackPanel.DataContext>
            <TextBlock Text="{Binding Id}"/>
            <TextBlock Text="{Binding Play}"/>
            <TextBlock Text="{Binding Age}"/>
        </StackPanel>
    </Grid>
    // 6
    // ball
    // (空)
    
    把 StackPanel 的 DataContext 去掉
    // 1000
    // (空)
    // 100
    

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

    • 当我们没有为 ListBox 的 ItemTemplate 指定 DataTemplate 时,
      ListBox 在获得 ItemsSource 后会创建等量的 ListBoxItem 并自动设置 DataTemplate。
    • 在获取 DataTemplate 的过程中会以 DisplayMemberPath 属性值作为 Path 创建 Binding。
    • Binding 的目标是 ListBoxItem 的内容插件(TextBox)

    1.7 使用 ADO.NET 对象作为 Binding 的源

    <ListBox x:Name="listbox" DisplayMemberPath="Id" ItemsSource={Binding dataView}></ListBox>
    
    <ListView x:Name="listview" ItemsSource={Binding dataView}>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id}" Width="100"/>
                <GridViewColumn Header="CameraEncode" DisplayMemberBinding="{Binding CameraEncode}"  Width="100"/>
                <GridViewColumn Header="Pan" DisplayMemberBinding="{Binding Pan}" Width="100"/>
            </GridView>
        </ListView.View>
    </ListView>
    
    DataView dataView {get;set;}//再添加更新通知。
    System.Data.DataTable dt = this.Load();
    dataView = dt.DefaultView;
    

    并不能展示数据,但行数是正确的。

    1.8 使用 XML 的数据作为 Binding 的源

    前面提 x:XData 时有实现过一次 ListBox 绑定到 XmlDataProvider 的方式;

    <ListBox x:Name="listbox" Grid.Row="5" I
    temsSource="{Binding Source={StaticResource XMlData}, XPath=/Super/Colors/Color}"/>           
    

    这次提供 C# 版;

    XmlDataProvider xdp = new XmlDataProvider();
    xdp.Source = new Uri(@"D:RawData.xml");
    xdp.XPath = @"/StudentList/Student";
    
    this.listbox.DataContext = xdp;
    this.listbox.SetBinding(ListView.ItemsSourceProperty, new Binding());
    

    设置 XmlDataProvider.Source 的效果等同于 XmlDataProvider.Document:

    xdp.Source = new Uri(@"D:RawData.xml");
    =
    System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
    doc.Load(@"D:RawData.xml");
    xdp.Document = doc;
    

    1.9 使用 LINQ 的检索结果作为 Binding 的源

    1.9.1 LINQ from List

    <ListView x:Name="listViewStrs" Height="100" Margin="100">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Id" Width="60" DisplayMemberBinding="{Binding Id}" />
                <GridViewColumn Header="Name" Width="60" DisplayMemberBinding="{Binding Name}" />
            </GridView>
        </ListView.View>
    </ListView>
    
    ...
    
    List<Student> listStr = new List<Student>()
    {
        new Student() { Id = "11", Name = "YaoMing"},
        new Student() { Id = "24", Name = "Kobe"},
        new Student() { Id = "8", Name = "Kobe-Young"},
    };
    listViewStrs.ItemsSource = from str in listStr where str.Name.StartsWith("K") select str;
    

    1.9.2 LINQ + XML

    listbox.ItemsSource = 
        from element in xdoc.Descendants("Student")
        where element.Attribute("Name").Value.StartsWith("T")
        select new Student()
        {
            Id = int.Parse(element.Attribute("Id").Value),
            Name = element.Attribute("Name").Value
        };
    

    1.9.3 LINQ + DataTable

    listbox.ItemsSource = 
        from row in dt.Row.Cast<DataRow>()
        where Convert.ToString(row["Name"]).StartsWith("T") 
        select new Student()
        {
            Id = int.Parse(row["Id"].ToString()),
            Name = row["Name"].ToString()
        };
    

    1.10 使用 ObjectDataProvider 对象作为 Binding 的 Source

    1.10.1 介绍一波 ObjectDataProvider

    class Calculator
    {
        public string Add(string arg1, string arg2)
        {
            double x = 0;
            double y = 0;
            double z = 0;
    
            if (double.TryParse(arg1, out x) && double.TryParse(arg2, out y))
            {
                z = x + y;
                return z.ToString();
            }
    
            return "Input Error";
        }
    }
    
    ObjectDataProvider dp = new ObjectDataProvider();
    dp.ObjectInstance = new Calculator();
    dp.MethodName = "Add";
    dp.MethodParameters.Add("10");
    dp.MethodParameters.Add("15");
    MessageBox.Show(dp.Data.ToString()); // 25
    

    1.10.2 使用 ObjectDataProvider 作为 Binding 的 Source

    <StackPanel Grid.Row="10">
        <TextBox x:Name="textBlockArg1"/>
        <TextBox x:Name="textBlockArg2"/>
        <TextBox x:Name="textBlockResult"/>
    </StackPanel>
    
    ObjectDataProvider dp = new ObjectDataProvider();
    dp.ObjectInstance = new Calculator();
    dp.MethodName = "Add";
    dp.MethodParameters.Add("0");
    dp.MethodParameters.Add("0");
    
    Binding bindingToArg1 = new Binding("MethodParameters[0]")
    {
        Source = dp,
        BindsDirectlyToSource = true,//告诉Binding对象只负责把从UI得到的数据写入Source(odp)而不是写入odp对象包装的Calculator对象中,默认 false
        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
    };
    
    Binding bindingToArg2 = new Binding("MethodParameters[1]")
    {
        Source = dp,
        BindsDirectlyToSource = true,
        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
    };
    
    Binding bindingToResult = new Binding(".") { Source = dp };
    
    this.textBlockArg1.SetBinding(TextBox.TextProperty, bindingToArg1);
    this.textBlockArg2.SetBinding(TextBox.TextProperty, bindingToArg2);
    this.textBlockResult.SetBinding(TextBox.TextProperty, bindingToResult);
    

    1.11 使用 Binding 的 RelativeSource

    <Grid x:Name="g3">
        <Grid x:Name="g2">
            <Grid x:Name="g1">
                <StackPanel x:Name="s2">
                    <StackPanel x:Name="s1">
                        <TextBox x:Name="textBlock1"/>
                        <TextBox x:Name="textBlock2"/>
                        <TextBox x:Name="textBlock3"/>
                        <TextBox x:Name="textBlock4"/>
                        <TextBox x:Name="textBlock5" Text="{Binding Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType={x:Type StackPanel}}}"/>
                        <TextBox x:Name="textBlock6" Text="{Binding Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=2,AncestorType={x:Type StackPanel}}}"/>
                        <TextBox x:Name="textBlock7" Text="{Binding Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=3,AncestorType={x:Type Grid}}}"/>
                        <TextBox x:Name="textBlock8" Text="{Binding Name, RelativeSource={RelativeSource Mode=Self}}"/>
                    </StackPanel>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
    
    RelativeSource rs = new RelativeSource();
    rs.AncestorLevel = 1;
    rs.AncestorType = typeof(StackPanel);
    Binding binding = new Binding("Name") { RelativeSource = rs };
    
    this.textBlock1.SetBinding(TextBox.TextProperty, binding);
    
    RelativeSource rs2 = new RelativeSource();
    rs2.AncestorLevel = 2;
    rs2.AncestorType = typeof(StackPanel);
    Binding binding2 = new Binding("Name") { RelativeSource = rs2 };
    
    this.textBlock2.SetBinding(TextBox.TextProperty, binding2);
    
    RelativeSource rs3 = new RelativeSource();
    rs3.AncestorLevel = 3;
    rs3.AncestorType = typeof(Grid);
    Binding binding3 = new Binding("Name") { RelativeSource = rs3 };
    
    this.textBlock3.SetBinding(TextBox.TextProperty, binding3);
    
    RelativeSource rs4 = new RelativeSource();
    rs4.Mode = RelativeSourceMode.Self;
    Binding binding4 = new Binding("Name") { RelativeSource = rs4};
    
    this.textBlock4.SetBinding(TextBox.TextProperty, binding4);
    
    // s1
    // s2
    // g3
    // textblock4
    // s1
    // s2
    // g3
    // textblock8
    

    从 textBlock3.Text 展示的是 "g3",可以看出 RelativeSource.AncestorLevel 表示从自身开始,往根结点方向找父节点,找第 x 个 AncestorType 的元素;
    如果是找自身,就用 RelativeSource.Mode = RelativeSourceMode.Self;
    RelativeSource 有三个静态属性:PreviousData、Self、TemplateParent,分别对应 RelativeSourceMode 的三个同名称的枚举值,在 DataTemplate 中常用到,通过 ILSpy 可以看到源代码:

    public static RelativeSource TemplatedParent
    {
        get
        {
            if (s_templatedParent == null)
            {
                s_templatedParent = new RelativeSource(RelativeSourceMode.TemplatedParent);
            }
            return s_templatedParent;
        }
    }
    
  • 相关阅读:
    mexHttpBinding协议 【发布元数据终结点】
    Revit API创建一个拷贝房间内对象布局命令
    Revit API判断是不是柱族模板
    Revit API封装一个通用函数“过名称找元素”
    Revit手工创建族
    Revit API根据参数类型取得参数的值
    Revit Family API 创建参考平面
    Revit Family API 添加参数与尺寸标注
    osgearth earth文件规范-符号参考
    Revit API创建几何实体Solid并找到与之相交的元素
  • 原文地址:https://www.cnblogs.com/MichaelLoveSna/p/14441742.html
Copyright © 2011-2022 走看看