建立一個ViewModel並自定義屬性取代UserControl中的自定義屬性
ViewModel.cs:
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _labelValue;
public int LabelValue
{
get => _labelValue;
set
{
_labelValue = value;
Console.WriteLine(_labelValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LabelValue"));
}
}
}
INotifyPropertyChanged主要作用是通蜘UI介面對應的屬性值有改變了。
MainWindow中Slider的Value<=>ViewModel的LabelValue<=>UserControl中Label3的Content
在MainWindow要綁定ViewModel屬性方式跟綁定UserControl的屬性一樣,
分為使用code綁定或是在XAML內綁定。
MainWindow使用code綁定ViewModel屬性:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
Slider1.SetBinding(Slider.ValueProperty, new Binding("LabelValue"));
}
MainWindow的XAML內綁定ViewModel屬性:
<Window x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="400" Width="300">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider x:Name="Slider1"
Grid.Column="1"
HorizontalAlignment="Center"
Grid.Row="1"
VerticalAlignment="Center"
Width="250"
ValueChanged="Slider1_ValueChanged"
SmallChange="1"
Maximum="100"
Value="{Binding LabelValue}"/>
<local:UserControl1
x:Name="UserControl_Labels"
HorizontalAlignment="Center"
VerticalAlignment="Center"
LabelValue1="{Binding ElementName=Slider1, Path=Value}"
LabelValue2="{Binding ElementName=Slider1, Path=Value}"/>
</Grid>
</Window>
以上這兩個方式是等效的。
設定DataContext為ViewModel,然後將Slider1的Value與ViewModel的LabelValue綁定。
UserControl用code做綁定:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
Label1.SetBinding(ContentProperty, new Binding(nameof(LabelValue1)) { Source = this });
Label3.SetBinding(ContentProperty, new Binding() {Path = new PropertyPath(nameof(ViewModel.LabelValue))});
//這邊不需設定DataContext,會自動參考到ViewModel的LabelValue
//但如果在這或是XAML內有設定全域性的DataContext,則這裡的綁定會失效。
}
public object LabelValue1
{
get => GetValue(LabelValue1Property);
set => SetValue(LabelValue1Property, value);
}
public static readonly DependencyProperty LabelValue1Property =
DependencyProperty.Register("LabelValue1", typeof(object), typeof(UserControl1));
public object LabelValue2
{
get => (object)GetValue(LabelValue2Property);
set => SetValue(LabelValue2Property, value);
}
public static readonly DependencyProperty LabelValue2Property =
DependencyProperty.Register("LabelValue2", typeof(object), typeof(UserControl1), new PropertyMetadata(3.0));
}
UserControl的XAML內做綁定:
<UserControl x:Class="WpfApp3.UserControl1"
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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="Label1"
Content="Label1"
HorizontalAlignment="Stretch"
Grid.Row="0"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
FontSize="36"
Background="#FFAA2525"
Foreground="#FFFBF8F6"/>
<Label x:Name="Label2"
d:DataContext="{d:DesignInstance Type=local:UserControl1}"
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:UserControl1}}"
Content="{Binding LabelValue2}"
HorizontalAlignment="Stretch"
Grid.Row="1"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
FontSize="36"
Background="#FF60B646"
Foreground="#FFFBF8F6"/>
<Label x:Name="Label3"
d:DataContext="{d:DesignInstance Type=local:ViewModel}"
Content="{Binding LabelValue}"
HorizontalAlignment="Stretch"
Grid.Row="2"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
FontSize="36"
Background="#FF28389C"
Foreground="#FFFBF8F6">
</Label>
</Grid>
</UserControl>
這比較不一樣的是 d:DataContext="{d:DesignInstance Type=local:UserControl1}"
以及d:DataContext="{d:DesignInstance Type=local:ViewModel}",
這兩行的效果是在設計階段引用參考實例,
在UI上顯示綁定後的值,如果是數值就顯示數字、如果是物件就顯示物件名稱。
PS.Label3不需設定DataContext。
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:UserControl1}}"
屬於區域性的DataContext設定,如果設定全域性的DataContext會影響Label3綁定ViewModel的LabelValue。
PS.Label2綁定UserControl的LabelValue2,而Label3綁定的是ViewModel的LabelValue。
簡單說就是Label2是綁定自身屬性,Label3是綁定其他物件的屬性。
如果是參考單一ViewModel屬性或是自身屬性的話,只需設定全域性的DataContext。