狀況 CONTEXT
當我們寫了一個 UserControl 控制項,如何透過資料繫結方式動態控制該控制項?
準備 PREPARE
-
新增一個名稱 DEMO 的 WPF App 專案 。
-
新增一個名稱為 MyTextBlock 的 UserControl 控制項,並加入一個 TextBlock 控制項,其 Xaml 如下:
<UserControl x:Class="DEMO.MyTextBlock" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:DEMO" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <TextBlock Text="It's alive!!!" /> </Grid> </UserControl>CodeBehide 的程式預設應該如下:
using System.Windows.Controls; namespace DEMO { /// <summary> /// Interaction logic for MyTextBlock.xaml /// </summary> public partial class MyTextBlock : UserControl { public MyTextBlock() { InitializeComponent(); } } }
在使用啟動視窗插入 MyTextBlock 控制項。
<Window x:Class="DEMO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DEMO"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:MyTextBlock />
</Grid>
</Window>
方案 SOLUTION
一、 UserControl 繫結特定屬性
使用者控制項 ( MyTextBlock ) 直接繋結到容器物件 ( MainWindow ) 的屬性。
-
調整 MyTextBlock 控制項
將原來固定的文字內容改為繫結方式決定。
<TextBlock Text="{Binding Description}" /> -
調整 MainWindow 視窗
- XAML
<local:MyTextBlock x:Name="MyTextBlockControl" /> -
Code
建立提供繫結的屬性,並在建構元指定控制項資料來源。
public MainWindow() { InitializeComponent(); this.MyTextBlockControl.DataContext = this; } public string Description { get; set; }= "It's alive!!! by Binding MainWindow.Description";
- XAML
二、 UserControl 繫結 UserControl 屬性
- 在 MyTextBlock 建立 Descrption 相依屬性 。
public readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(nameof(Description), typeof(string), typeof(MyTextBlock), new PropertyMetadata()); public string Description { get { return (string)this.GetValue(DescriptionProperty); } set { this.SetValue(DescriptionProperty, value ); } } -
調整 MainWindow 視窗
<local:MyTextBlock Description="It's alive!!! by xaml setting" /> -
設定繫結方式:
繫結方式可以透過程式或 XAML 完成,在此例子中效果相同。
-
透過程式設定繫結:
- MyTextBlock 的 XAML
<TextBlock Text="{Binding Description}" /> -
MyTextBlock 的 Code
初始程式中指定控制項的 DataContext 為自已,並建立對應的相依屬性。
public MyTextBlock() { InitializeComponent(); // 指定資料來源。 this.DataContext = this; }
- MyTextBlock 的 XAML
-
透過 XAML 設定繫結:
調整的地方有兩個,首先加上 UserControl 的名稱,然後調整 TextBlock.Text 的繫結語法,透過 ElementName 設定指定資料來源。
<UserControl x:Class="DEMO.MyTextBlock" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:DEMO" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" x:Name="MainControl"> <Grid> <TextBlock Text="{Binding Description, ElementName=MainControl }" /> </Grid> </UserControl>
-
三、 UserControl 繫結指定屬性
-
調整 MyTextBlock 控制項
這部分可以參考「 UserControl 繫結 UserControl 屬性」說明,透過 Code 或 XAML 達成,下面以 XAML 示範。
- Code
public readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(nameof(Description), typeof(string), typeof(MyTextBlock), new PropertyMetadata()); public string Description { get { return (string)this.GetValue(DescriptionProperty); } set { this.SetValue(DescriptionProperty, value ); } } -
XAML
<UserControl x:Class="DEMO.MyTextBlock" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:DEMO" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" x:Name="MainControl"> <Grid> <TextBlock Text="{Binding Description, ElementName=MainControl }" /> </Grid> </UserControl>
- Code
-
調整 MainWindow 視窗
- XAML
<<UserControl x:Class="DEMO.MyTextBlock" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:DEMO" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" x:Name="MainControl" > <Grid> <TextBlock Text="{Binding Description, ElementName=MainControl }" /> </Grid> </UserControl> -
Code
public string Description { get; set; } = "It's alive!!! by MainWin.Description";
- XAML
分析 ANALYSIS
方案一最簡單,但是控制項繫結的對象被定義在控制項內,使用的容器物件指定 DataContext 時配合控制項的設定,若是無法知道控制項繫結設定,就無法或難以正確的給定繫結資料,僅適合在快速建立且後續不維護的單一專案。
方案二透過相依屬性的建立,提供控制項的資料繫結來源可由容器自訂的彈性,方案三則由方案二延伸而來,展示容器自訂的部分也由透過繫結完成,兩種型式都可視情況選用,例如按鍵的說明,通常不會在執行階段異動,採用方案二的方式,在 XAML 直接給定即可,兼顧方便與直覺;但如果是操作訊息之類,會在執行階段異動,若採用方案二,在異動邏輯會有操作控制項的必要,如此可能打破 WPF 前端的 MVVM 架構,此時採用方案三並繫結到相依屬性後,就可以透過重設屬性來更新控制項,達成以資料更新進行控制項操作的 MVVM 架構。