四、數據綁定
(一)數據綁定基礎
- 綁定源和目標:數據綁定建立了 UI 元素(綁定目標)屬性與數據源(綁定源)之間的聯系。例如,將一個
TextBox
的Text
屬性綁定到一個對象的某個屬性上。綁定源可以是對象的屬性、集合、XML 數據等,綁定目標通常是 UI 元素的依賴屬性。 - 綁定模式:WPF 支持三種綁定模式:
- OneWay:數據從綁定源流向綁定目標。當綁定源屬性值發生變化時,綁定目標屬性會自動更新,但綁定目標的變化不會影響綁定源。例如,將一個只讀的文本框綁定到一個數據模型中的屬性,用于顯示數據。
<TextBox Text="{Binding MyReadOnlyProperty, Mode=OneWay}"/>
- TwoWay:數據在綁定源和綁定目標之間雙向流動。當綁定源屬性值變化時,綁定目標更新;當綁定目標屬性值變化時,綁定源也會相應更新。常用于需要用戶輸入并更新數據模型的場景,如編輯文本框。
<TextBox Text="{Binding MyEditableProperty, Mode=TwoWay}"/>
- OneTime:數據在初始化時從綁定源流向綁定目標,之后綁定源的變化不會再影響綁定目標。適用于數據在應用程序運行過程中不會改變的情況,如顯示應用程序版本號等。
<TextBlock Text="{Binding AppVersion, Mode=OneTime}"/>
(二)實現數據綁定
- 創建數據源:通常會創建一個 ViewModel 類來作為數據源。ViewModel 類應該實現
INotifyPropertyChanged
接口,以便在屬性值發生變化時通知綁定目標更新。例如:
public class UserViewModel : INotifyPropertyChanged
{private string _name;public string Name{get { return _name; }set{_name = value;OnPropertyChanged(nameof(Name));}}public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged(string propertyName){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}
- 在 XAML 中設置綁定:在 XAML 文件中,將 UI 元素的屬性綁定到 ViewModel 的屬性上。假設在窗口的代碼隱藏文件中創建了
UserViewModel
的實例,并將其設置為窗口的DataContext
:
public partial class MainWindow : Window
{public MainWindow(){InitializeComponent();UserViewModel viewModel = new UserViewModel();DataContext = viewModel;}
}
在 XAML 中:
<TextBox Text="{Binding Name}"/>
這樣,TextBox
的Text
屬性就與UserViewModel
中的Name
屬性綁定在一起了,當Name
屬性值改變時,
TextBox
?中的文本會自動更新,反之,當用戶在?TextBox
?中輸入新的文本時,Name
?屬性的值也會相應更新(如果綁定模式是?TwoWay
)。
(三)綁定到集合
在實際應用中,經常需要將 UI 元素綁定到集合數據上,例如將?ListView
?或?ComboBox
?綁定到一個?ObservableCollection
。ObservableCollection
?是一個動態集合,當集合中的元素發生添加、刪除或修改操作時,會自動通知綁定的 UI 元素進行更新。
以下是一個將?ListView
?綁定到?ObservableCollection
?的示例:
public class EmployeeViewModel
{public ObservableCollection<Employee> Employees { get; set; }public EmployeeViewModel(){Employees = new ObservableCollection<Employee>{new Employee { Name = "John Doe", Age = 30 },new Employee { Name = "Jane Smith", Age = 25 },new Employee { Name = "Bob Johnson", Age = 35 }};}
}public class Employee
{public string Name { get; set; }public int Age { get; set; }
}
在 XAML 中:
<Window.Resources><DataTemplate x:Key="EmployeeTemplate"><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding Name}" Margin="5"/><TextBlock Text="{Binding Age}" Margin="5"/></StackPanel></DataTemplate>
</Window.Resources>
<ListView ItemsSource="{Binding Employees}" ItemTemplate="{StaticResource EmployeeTemplate}"/>
在窗口的代碼隱藏文件中設置?DataContext
:
public partial class MainWindow : Window
{public MainWindow(){InitializeComponent();EmployeeViewModel viewModel = new EmployeeViewModel();DataContext = viewModel;}
}
這樣,ListView
?會顯示?Employees
?集合中的每個?Employee
?對象,并且使用?EmployeeTemplate
?定義的模板來呈現每個對象的?Name
?和?Age
?屬性。
(四)數據綁定的其他特性
- 綁定轉換器:當綁定源和綁定目標的類型不匹配時,或者需要對綁定值進行一些轉換時,可以使用綁定轉換器。綁定轉換器是實現了?
IValueConverter
?接口的類。例如,將一個布爾值轉換為字符串顯示:
public class BooleanToStringConverter : IValueConverter
{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){if (value is bool boolValue){return boolValue ? "Yes" : "No";}return null;}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){if (value is string stringValue){return stringValue == "Yes";}return false;}
}
在 XAML 中使用轉換器:
<Window.Resources><local:BooleanToStringConverter x:Key="BooleanToStringConverter"/>
</Window.Resources>
<TextBlock Text="{Binding IsActive, Converter={StaticResource BooleanToStringConverter}}"/>
- 綁定驗證:在用戶輸入數據時,需要對輸入的數據進行驗證,確保數據的有效性。WPF 提供了綁定驗證機制,可以通過實現?
IDataErrorInfo
?接口或使用?ValidationRule
?類來實現驗證。例如,驗證一個文本框輸入的是否為有效的整數:
public class IntegerValidationRule : ValidationRule
{public override ValidationResult Validate(object value, CultureInfo cultureInfo){if (int.TryParse(value as string, out _)){return ValidationResult.ValidResult;}return new ValidationResult(false, "請輸入有效的整數。");}
}
在 XAML 中應用驗證規則:
<Window.Resources><local:IntegerValidationRule x:Key="IntegerValidationRule"/>
</Window.Resources>
<TextBox><TextBox.Text><Binding Path="Age" UpdateSourceTrigger="PropertyChanged"><Binding.ValidationRules><local:IntegerValidationRule/></Binding.ValidationRules></Binding></TextBox.Text>
</TextBox>
當用戶輸入的不是有效的整數時,文本框會顯示驗證錯誤信息。
五、樣式和模板
(一)樣式基礎
樣式是一種用于集中設置 UI 元素屬性的機制,可以將一組屬性應用到多個元素上,實現統一的外觀風格。樣式可以定義在資源字典中,也可以直接在 XAML 文件中定義。
以下是一個簡單的樣式定義示例:
<Window.Resources><Style x:Key="MyButtonStyle" TargetType="Button"><Setter Property="Background" Value="LightBlue"/><Setter Property="Foreground" Value="White"/><Setter Property="FontSize" Value="14"/></Style>
</Window.Resources>
<Button Style="{StaticResource MyButtonStyle}" Content="Styled Button"/>
在這個示例中,定義了一個名為?MyButtonStyle
?的樣式,目標類型是?Button
。通過?Setter
?元素設置了按鈕的背景色、前景色和字體大小。然后將這個樣式應用到一個按鈕上。
(二)隱式樣式
隱式樣式是一種不指定?x:Key
?的樣式,它會自動應用到所有指定?TargetType
?的元素上。例如:
<Window.Resources><Style TargetType="Button"><Setter Property="Background" Value="Green"/><Setter Property="Foreground" Value="White"/></Style>
</Window.Resources>
<Button Content="Implicit Styled Button"/>
在這個示例中,定義了一個隱式樣式,目標類型是?Button
。所有在該窗口中的按鈕都會自動應用這個樣式。
(三)模板基礎
模板用于自定義 UI 元素的可視化結構。WPF 提供了兩種主要的模板類型:ControlTemplate
?和?DataTemplate
。
- ControlTemplate:用于自定義控件的外觀。例如,自定義一個按鈕的模板:
<Window.Resources><ControlTemplate x:Key="CustomButtonTemplate" TargetType="Button"><Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"><ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter Property="Background" Value="LightGray"/></Trigger></ControlTemplate.Triggers></ControlTemplate>
</Window.Resources>
<Button Template="{StaticResource CustomButtonTemplate}" Content="Custom Button"/>
在這個示例中,定義了一個?ControlTemplate
,通過?Border
?元素和?ContentPresenter
?元素來構建按鈕的外觀。TemplateBinding
?用于將模板中的屬性綁定到控件的實際屬性上。ControlTemplate.Triggers
?用于定義觸發條件,當鼠標懸停在按鈕上時,改變按鈕的背景色。
- DataTemplate:用于定義數據項的呈現方式。例如,將一個列表框中的數據項以自定義的方式顯示:
<Window.Resources><DataTemplate x:Key="PersonDataTemplate"><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding Name}" Margin="5"/><TextBlock Text="{Binding Age}" Margin="5"/></StackPanel></DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Persons}" ItemTemplate="{StaticResource PersonDataTemplate}"/>
在這個示例中,定義了一個?DataTemplate
,用于顯示?Persons
?集合中的每個?Person
?對象。DataTemplate
?中使用?StackPanel
?水平排列?Name
?和?Age
?屬性的文本塊。
(四)資源字典
資源字典是一種用于集中管理樣式、模板、畫筆等資源的機制。可以將資源字典定義在單獨的?.xaml
?文件中,然后在多個 XAML 文件中引用。
以下是一個資源字典文件?MyResources.xaml
?的示例:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Style x:Key="MyButtonStyle" TargetType="Button"><Setter Property="Background" Value="Orange"/><Setter Property="Foreground" Value="White"/></Style>
</ResourceDictionary>
在 XAML 文件中引用資源字典:
<Window.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="MyResources.xaml"/></ResourceDictionary.MergedDictionaries></ResourceDictionary>
</Window.Resources>
<Button Style="{StaticResource MyButtonStyle}" Content="Button from Resource Dictionary"/>
通過這種方式,可以實現資源的共享和復用,提高代碼的可維護性。
六、動畫和多媒體
(一)動畫基礎
WPF 提供了豐富的動畫系統,允許對 UI 元素的屬性進行動態變化,從而創建出各種生動的效果。動畫主要分為線性動畫、關鍵幀動畫和路徑動畫。
- 線性動畫:線性動畫是最簡單的動畫類型,它在指定的時間內從一個值線性變化到另一個值。例如,實現一個按鈕在點擊時逐漸變大的動畫:
<Window.Resources><Storyboard x:Key="GrowButtonAnimation"><DoubleAnimation Storyboard.TargetProperty="Width"From="100" To="150" Duration="0:0:0.5"/><DoubleAnimation Storyboard.TargetProperty="Height"From="50" To="75" Duration="0:0:0.5"/></Storyboard>
</Window.Resources>
<Button Content="Animate Me" Click="Button_Click"><Button.Triggers><EventTrigger RoutedEvent="Button.Click"><BeginStoryboard Storyboard="{StaticResource GrowButtonAnimation}"/></EventTrigger></Button.Triggers>
</Button>
在這個示例中,定義了一個?Storyboard
,其中包含兩個?DoubleAnimation
,分別對按鈕的?Width
?和?Height
?屬性進行動畫處理。當按鈕被點擊時,觸發?EventTrigger
,開始播放動畫。
- 關鍵幀動畫:關鍵幀動畫允許在動畫過程中定義多個關鍵幀,每個關鍵幀指定一個特定的時間點和屬性值。例如,實現一個按鈕在不同時間點改變顏色的動畫:
<Window.Resources><Storyboard x:Key="ColorAnimationStoryboard"><ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)" Duration="0:0:2"><LinearColorKeyFrame Value="Red" KeyTime="0:0:0"/><LinearColorKeyFrame Value="Green" KeyTime="0:0:1"/><LinearColorKeyFrame Value="Blue" KeyTime="0:0:2"/></ColorAnimationUsingKeyFrames></Storyboard>
</Window.Resources>
<Button Content="Color Animate" Click="Button_Click_1"><Button.Triggers><EventTrigger RoutedEvent="Button.Click"><BeginStoryboard Storyboard="{StaticResource ColorAnimationStoryboard}"/></EventTrigger></Button.Triggers>
</Button>
在這個示例中,使用?ColorAnimationUsingKeyFrames
?定義了一個顏色動畫,通過?LinearColorKeyFrame
?指定了不同時間點的顏色值。
- 路徑動畫:路徑動畫允許元素沿著指定的路徑移動。例如,讓一個橢圓沿著一個圓形路徑移動:
<Window.Resources><Storyboard x:Key="PathAnimationStoryboard"><PointAnimationUsingPath Storyboard.TargetProperty="(Canvas.LeftProperty)"Storyboard.TargetName="ellipse"Duration="0:0:5"RepeatBehavior="Forever"><PointAnimationUsingPath.PathGeometry><EllipseGeometry Center="200,200" RadiusX="100" RadiusY="100"/></PointAnimationUsingPath.PathGeometry></PointAnimationUsingPath></Storyboard>
</Window.Resources>
<Canvas><Ellipse x:Name="ellipse" Width="20" Height="20" Fill="Red" Canvas.Left="100" Canvas.Top="100"/><Button Content="Start Animation" Canvas.Left="10" Canvas.Top="10" Click="Button_Click_2"><Button.Triggers><EventTrigger RoutedEvent="Button.Click"><BeginStoryboard Storyboard="{StaticResource PathAnimationStoryboard}"/></EventTrigger></Button.Triggers></Button>
</Canvas>
在這個示例中,使用?PointAnimationUsingPath
?讓橢圓沿著一個圓形路徑移動,路徑由?EllipseGeometry
?定義。
(二)多媒體支持
WPF 提供了對多媒體的支持,包括音頻和視頻的播放。可以使用?MediaElement
?控件來播放多媒體文件。
以下是一個播放視頻的示例:
<MediaElement Source="video.mp4" Width="640" Height="360"LoadedBehavior="Play" UnloadedBehavior="Stop"/>
在這個示例中,MediaElement
?控件的?Source
?屬性指定了要播放的視頻文件的路徑。LoadedBehavior
?屬性設置為?Play
?表示當控件加載完成后自動播放視頻,UnloadedBehavior
?屬性設置為?Stop
?表示當控件卸載時停止播放視頻。
七、命令系統
(一)命令基礎
WPF 的命令系統提供了一種將用戶操作(如按鈕點擊、菜單項選擇等)與業務邏輯分離的機制。命令是一種抽象的操作,它定義了操作的執行邏輯和是否可以執行的判斷邏輯。
(二)實現命令
WPF 中常用的命令實現方式是使用?RelayCommand
?類,它是一個自定義的命令類,實現了?ICommand
?接口。以下是一個?RelayCommand
?類的實現:
public class RelayCommand : ICommand
{private readonly Action<object> _execute;private readonly Predicate<object> _canExecute;public RelayCommand(Action<object> execute, Predicate<object> canExecute = null){_execute = execute;_canExecute = canExecute;}public bool CanExecute(object parameter){return _canExecute == null || _canExecute(parameter);}public event EventHandler CanExecuteChanged{add { CommandManager.RequerySuggested += value; }remove { CommandManager.RequerySuggested -= value; }}public void Execute(object parameter){_execute(parameter);}
}
在 ViewModel 中使用?RelayCommand
:
public class MainViewModel
{public RelayCommand MyCommand { get; set; }public MainViewModel(){MyCommand = new RelayCommand(ExecuteMyCommand, CanExecuteMyCommand);}private void ExecuteMyCommand(object parameter){// 執行命令的邏輯MessageBox.Show("Command executed!");}private bool CanExecuteMyCommand(object parameter){// 判斷命令是否可以執行的邏輯return true;}
}
在 XAML 中綁定命令:
<Button Content="Execute Command" Command="{Binding MyCommand}"/>
(三)系統命令
WPF 還提供了一些內置的系統命令,如?ApplicationCommands.New
、ApplicationCommands.Open
、ApplicationCommands.Save
?等。這些命令可以直接在 XAML 中使用,例如:
<Menu><MenuItem Header="File"><MenuItem Header="New" Command="{x:Static ApplicationCommands.New}"/><MenuItem Header="Open" Command="{x:Static ApplicationCommands.Open}"/><MenuItem Header="Save" Command="{x:Static ApplicationCommands.Save}"/></MenuItem>
</Menu>
系統命令會自動處理一些常見的操作,如快捷鍵綁定、命令狀態更新等。