背水一戰 Windows 10 (21) - 綁定: x:Bind 綁定, x:Bind 綁定之 x:Phase, 使用綁定過程中的一些技巧


[源碼下載]


背水一戰 Windows 10 (21) - 綁定: x:Bind 綁定, x:Bind 綁定之 x:Phase, 使用綁定過程中的一些技巧



作者:webabcd


介紹
背水一戰 Windows 10 之 綁定

  • x:Bind 綁定
  • x:Bind 綁定之 x:Phase
  • 使用綁定過程中的一些技巧



示例
1、演示 x:Bind 綁定的相關知識點
Bind/BindDemo.xaml

<Page
    x:Class="Windows10.Bind.BindDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.Bind"
    xmlns:common="using:Windows10.Common"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <!--
                關於 x:Bind 的相關說明:
                1、Binding 是在運行時(RunTime)綁定;而 x:Bind 是在編譯時(BuildTime)綁定(其會在編譯時自動生成一些輔助代碼,參見對應的 g.cs 代碼),所以 x:Bind 的效率要高
                2、x:Bind 是強類型綁定,類型必須比配;Binding 在綁定時如果類型不必配,會做自動轉換
                3、x:Bind 只具有 Binding 的一部分功能,如下: Path, Mode. FallbackValue, TargetNullValue, Converter, ConverterParameter, ConverterLanguage
                4、x:Bind 的 Mode 的默認值是 OneTime;Binding 的 Mode 的默認值是 OneWay
                5、x:Bind 的數據上下文是其所屬的 Page 或 UserControl, 不能為 x:Bind 指定其他的數據上下文
                6、x:Bind 在 DataTemplate 中使用時,必須指定 DataTemplate 的 DataType;而 Binding 則不必
                7、x:Bind 只能在 xaml 中使用,無法在 CodeBehind 中使用
                8、x:Bind 支持事件綁定到方法
                9、仔細看看上面的說明,x:Bind 與 Binding 的大部分不同的本質原因是,他們一個是編譯時綁定,一個是運行時綁定
            -->
            
            <!--綁定到屬性-->
            <TextBlock Name="textBlock" Text="{x:Bind Path=CurrentEmployee.Name, Mode=OneWay}" Margin="5" />

            <!--事件綁定到方法,無參數-->
            <Button Content="修改 CurrentEmployee 的 MyName" Click="{x:Bind EventBindNoArgs}" Margin="5" />

            <!--事件綁定到方法,參數與對應的事件的參數相同-->
            <Button Content="修改 CurrentEmployee 的 MyName" Click="{x:Bind EventBindRegularArgs}" Margin="5" />

            <!--事件綁定到方法,參數與對應的事件的參數相同,但是其中的事件參數為 object 類型-->
            <Button Content="修改 CurrentEmployee 的 MyName" Click="{x:Bind EventBindBaseArgs}" Margin="5" />

            <!--事件綁定到方法,也可以綁定到指定對象中的指定方法-->
            <Button Content="修改 CurrentEmployee 的 MyName" Click="{x:Bind CurrentEmployee.ChangeName}" Margin="5" />

            <!--在 DataTemplate 中使用 x:Bind 的注意事項:必須要指定 DataTemplate 的 DataType-->
            <ListView x:Name="listView" ItemsSource="{x:Bind AllEmployees}" Margin="5">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="common:Employee">
                        <TextBlock Text="{x:Bind Name}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

            <!--可以與索引器綁定-->
            <TextBlock Name="textBlock2" Text="{x:Bind Path=AllEmployees[0].Name, Mode=OneWay}" Margin="5" />

            <!--可以與 Element 綁定-->
            <TextBlock Name="textBlock3" Text="{x:Bind textBlock2.Text, Mode=OneWay}" Margin="5" />

        </StackPanel>
    </Grid>
</Page>

Bind/BindDemo.xaml.cs

/*
 * 演示 x:Bind 綁定的相關知識點
 */

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows10.Common;

namespace Windows10.Bind
{
    // x:Bind 的數據上下文就是它所屬的 Page 或 UserControl
    public sealed partial class BindDemo : Page
    {
        public BindDemo()
        {
            this.InitializeComponent();
        }

        // 事件綁定到方法,無參數
        private void EventBindNoArgs()
        {
            CurrentEmployee.Name = "wanglei" + new Random().Next(1000, 10000).ToString();
        }

        // 事件綁定到方法,參數與對應的事件的參數相同
        private void EventBindRegularArgs(object sender, RoutedEventArgs e)
        {
            CurrentEmployee.Name = "wanglei" + new Random().Next(1000, 10000).ToString();
        }

        // 事件綁定到方法,參數與對應的事件的參數相同,但是其中的事件參數為 object 類型
        private void EventBindBaseArgs(object sender, object e)
        {
            CurrentEmployee.Name = "wanglei" + new Random().Next(1000, 10000).ToString();
        }

        public Employee CurrentEmployee { get; set; } = new Employee() { Name = "wanglei", Age = 36, IsMale = true };

        public ObservableCollection<Employee> AllEmployees { get; set; } = TestData.GetEmployees(5);
    }
}


2、演示 x:Bind 綁定之 x:Phase 的相關知識點
Bind/PhaseDemo.xaml

<Page
    x:Class="Windows10.Bind.PhaseDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.Bind"
    xmlns:common="using:Windows10.Common"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>

        <!--
            關於 x:Phase 的相關說明:
            1、x:Phase 可以實現元素的分階段顯示,用於避免繪制大量數據時的卡頓
            2、ListView 和 GridView 均繼承自 ListViewBase
            3、在 windows 8.1 及以后版本中,ListViewBase 是支持 UI 虛擬化的
            4、x:Phase 只適用於 ListViewBase 中的 DataTemplate 里的元素
            5、x:Phase 屬於 x:Bind 的一項功能,所以其必須在指定了 x:Bind 的元素中使用才有效
            6、x:Phase 的作用是解決如下問題:當 ListViewBase 滾動時,如果一屏的 item 多,且每個 item 中的 element 也多,那么繪制時會占用大量的資源以至卡頓
            7、在 windows 8.1 及以后版本中,我們可以通過 ContainerContentChanging 事件手動控制列表項的呈現,而 x:Phase 的原理其實就是在編譯時自動生成這種手動控制代碼(參見對應的 g.cs 代碼)
               關於如何手動控制列表項的呈現,請參見 /Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo4.xaml
        -->
        
        <DataTemplate x:Key="PhasedFileTemplate" x:DataType="common:Employee">
            <StackPanel Width="200" Margin="10" Background="Blue">
                <TextBlock Margin="5" Text="{x:Bind Name}" Foreground="Red" x:Phase="9" />
                <TextBlock Margin="5" Text="{x:Bind Name}" Foreground="White" />
                <TextBlock Margin="5" Text="{x:Bind Name}" Foreground="Green" x:Phase="1" />
                <TextBlock Margin="5" Text="{x:Bind Name}" Foreground="Orange" x:Phase="2" />
                <TextBlock Margin="5" Text="{x:Bind Name}" Foreground="Orange" x:Phase="2" />
            </StackPanel>
        </DataTemplate>

    </Page.Resources>

    <Grid Background="Transparent">

        <!--
            ShowsScrollingPlaceholders="false" - 不顯示占位符
            ContainerContentChanging - 項容器的內容發生變化時觸發的事件(在本例中,其用於人為減慢每階段的顯示速度,以便演示)
        
            關於 ShowsScrollingPlaceholders, ContainerContentChanging 的詳細說明請參見 ListView, GridView 部分
        -->
        <GridView Name="gridView" Margin="10 0 10 10" ShowsScrollingPlaceholders="false" ContainerContentChanging="gridView_ContainerContentChanging"
                  ItemsSource="{x:Bind AllEmployees}" ItemTemplate="{StaticResource PhasedFileTemplate}" />

    </Grid>
</Page>

Bind/PhaseDemo.xaml.cs

/*
 * 演示 x:Bind 綁定之 x:Phase 的相關知識點
 */

using System.Collections.ObjectModel;
using System.Threading;
using Windows.UI.Xaml.Controls;
using Windows10.Common;

namespace Windows10.Bind
{
    public sealed partial class PhaseDemo : Page
    {
        public PhaseDemo()
        {
            this.InitializeComponent();
        }

        // 用於人為減慢每階段的顯示速度,以便演示
        private void gridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
        {
            AutoResetEvent h = new AutoResetEvent(false);
            h.WaitOne(1);

            uint phase = args.Phase;
            if (phase < 10)
                args.RegisterUpdateCallback(gridView_ContainerContentChanging);
        }

        // 數據源
        public ObservableCollection<Employee> AllEmployees { get; set; } = TestData.GetEmployees(1000);
    }
}


3、演示使用綁定過程中的一些技巧
Bind/Tips.xaml

<Page
    x:Class="Windows10.Bind.Tips"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.Bind"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    
    xmlns:common="using:Windows10.Common"
    Background="Red">

    <Page.Resources>
        <common:NullableBooleanToBooleanConverter x:Key="NullableBooleanToBooleanConverter" />
    </Page.Resources>
    
    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                
                <!--綁定附加屬性,注意要用括號括起來-->
                <TextBox Name="textBox1" Margin="5" Text="{Binding Path=(Grid.Row), RelativeSource={RelativeSource Self}}" Grid.Row="0" />

                <!--使用綁定時,可以在 Binding 或 x:Bind 后直接加 Path 的值,而不用寫“Path=”-->
                <TextBox Name="textBox2" Margin="5" Text="{Binding (Grid.Row), RelativeSource={RelativeSource Self}}" Grid.Row="1" />

                <!--在 CodeBehind 端綁定附加屬性-->
                <TextBox Name="textBox3" Margin="5" Grid.Row="2" />
            </Grid>

            <!--綁定自定義附加屬性(在 CodeBehind 端綁定自定義附加屬性暫時沒有成功)-->
            <TextBox Name="textBox4" Margin="5" local:MyAttachedProperty.SubTitle="custom attached property" Text="{Binding (local:MyAttachedProperty.SubTitle), RelativeSource={RelativeSource Self}}" />

            <!--綁定指定對象的指定屬性-->
            <TextBox Name="textBox5" Margin="5" Text="{Binding Margin.Top, RelativeSource={RelativeSource Self}}" />
            
            <!--
                Background 是 Brush 類型
                SolidColorBrush 繼承自 Brush
                Background.(SolidColorBrush.Color) 的意思就是將 Background 的 Brush 類型轉換為 SolidColorBrush 類型並取其 Color 屬性
            -->
            <TextBox Name="textBox6" Margin="5" Text="{x:Bind Background.(SolidColorBrush.Color)}" />

            <!--
                x:Bind 沒有 ElementName,那如何與其他元素綁定呢,像下面這樣即可
            -->
            <TextBox Name="textBox7" Margin="5" Text="{x:Bind textBox6.Text}" />

            <!--
                x:Bind 如何綁定到其他元素的附加屬性呢,像下面這樣即可
            -->
            <TextBox Name="textBox8" Margin="5" Text="{x:Bind textBox2.(Grid.Row)}" />

            <!--
                x:Bind 如何綁定到其他元素的自定義附加屬性呢,像下面這樣即可
            -->
            <TextBox Name="textBox9" Margin="5" Text="{x:Bind textBox4.(local:MyAttachedProperty.SubTitle)}" />

            <!--
                在本例中 CurrentEmployee 是 object 類型,要將他轉換為 Employee 類型,然后再使用其屬性,寫法如下
            -->
            <TextBox Name="textBox10" Margin="5" Text="{x:Bind CurrentEmployee.(common:Employee.Name)}" />

            <!--
                Binding 也可以這么寫(x:Bind 不支持這么寫)
            -->
            <TextBox Name="textBox11" Margin="5">
                <TextBox.Text>
                    <Binding Path="Text" ElementName="textBox6" />
                </TextBox.Text>
            </TextBox>

            <!--
                下面的示例用於演示如何綁定到 DataContext 對象的某個屬性
            -->
            <TextBox Name="textBox12" DataContext="{x:Bind CurrentEmployee}" Text="{Binding Name}" Margin="5" />
            
            <!--
                下面的示例用於演示如何直接綁定到 DataContext 對象(而不是綁定 DataContext 對象的某個屬性)
            -->
            <TextBox Name="textBox13" DataContext="{x:Bind MyName}" Text="{Binding}" Margin="5" />

            <!--
                Binding 綁定時,如果數據類型不一致,會嘗試自動轉換,比如此例:bool? 會被自動自動轉換為 bool
            -->
            <TextBox Name="textBox14" Text="我是 textBox14" IsReadOnly="{Binding IsChecked, ElementName=chk1}" Margin="5" />
            <CheckBox Name="chk1" Content="textBox14 IsReadOnly" IsChecked="True" Margin="5 0 0 0" />

            <!--
                x:Bind 是編譯時的強類型綁定,如果數據類型不一致,不會自動轉換,比如此例:要通過 Converter 把 bool? 轉換為 bool
            -->
            <TextBox Name="textBox15" Text="我是 textBox15" IsReadOnly="{x:Bind chk2.IsChecked, Mode=TwoWay, Converter={StaticResource NullableBooleanToBooleanConverter}}" Margin="5" />
            <CheckBox Name="chk2" Content="textBox15 IsReadOnly" IsChecked="True" Margin="5 0 0 0" />

            <!--
                再看看綁定此種路徑時的寫法,要理解
            -->
            <Rectangle Name="rectangle1" Height="20" Fill="Orange" Margin="5" />
            <TextBox Name="textBox16" Margin="5 0 0 0" Text="{x:Bind rectangle1.(Shape.Fill).(SolidColorBrush.Color)}" />

            <!--
                再來個更長的看看,要理解
            -->
            <Rectangle Name="rectangle2" Height="20" Width="100" HorizontalAlignment="Left" Fill="Orange" Margin="5">
                <!--
                    注:這里必須先要聲明出 ScaleTransform,這樣才能與之綁定(否則運行時會報錯)
                -->
                <Rectangle.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform />
                    </TransformGroup>
                </Rectangle.RenderTransform>
            </Rectangle>
            <TextBox Name="textBox17" Margin="5 0 0 0" Text="{x:Bind rectangle2.(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX), Mode=TwoWay}" />

        </StackPanel>
    </Grid>
</Page>

Bind/Tips.xaml.cs

/*
 * 用於演示使用綁定過程中的一些技巧
 *
 * 在這里插一句:
 * 在 xaml 使用的 {Binding}, {x:Bind}, {StaticResource} 之類的這種帶大括號的語法被稱為標記擴展(Markup Extension),在 uwp 中無法開發自定義標記擴展(但是在 wpf 中是可以的)
 */

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows10.Common;

namespace Windows10.Bind
{
    public sealed partial class Tips : Page
    {
        public Tips()
        {
            this.InitializeComponent();

            this.Loaded += Tips_Loaded;
        }

        private void Tips_Loaded(object sender, RoutedEventArgs e)
        {
            BindingAttachedProperty();
        }

        // 在 CodeBehind 端綁定附加屬性
        private void BindingAttachedProperty()
        {
            Binding binding = new Binding()
            {
                Path = new PropertyPath("(Grid.Row)"), // 注意要有括號,另外在 CodeBehind 端綁定自定義附加屬性暫時沒有成功
                Source = textBox3
            };
            BindingOperations.SetBinding(textBox3, TextBox.TextProperty, binding);
        }

        // 通過 x:Bind 綁定時,要做轉換
        public object CurrentEmployee { get; set; } = new Employee() { Name = "wanglei", Age = 36, IsMale = true };

        public string MyName { get; set; } = "webabcd";
    }



    /// <summary>
    /// 用於附加屬性的演示
    /// </summary>
    public class MyAttachedProperty
    {
        // 獲取附加屬性
        public static string GetSubTitle(DependencyObject obj)
        {
            return (string)obj.GetValue(SubTitleProperty);
        }

        // 設置附加屬性
        public static void SetSubTitle(DependencyObject obj, string value)
        {
            obj.SetValue(SubTitleProperty, value);
        }

        // 注冊一個附加屬性
        public static readonly DependencyProperty SubTitleProperty =
            DependencyProperty.RegisterAttached(
                "SubTitle", // 附加屬性的名稱
                typeof(string), // 附加屬性的數據類型
                typeof(MyAttachedProperty), // 附加屬性所屬的類
                new PropertyMetadata("", PropertyMetadataCallback)); // 指定附加屬性的默認值,以及值發生改變時所調用的方法

        private static void PropertyMetadataCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            object newValue = args.NewValue; // 發生改變之后的值
            object oldValue = args.OldValue; // 發生改變之前的值
        }
    }
}



OK
[源碼下載]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM