2022年3月28日 星期一

[WPF] DataBinding-進階篇(三)UserControl自定義屬性綁定ViewModel

 建立一個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。

2022年3月24日 星期四

[WPF] DataBinding-進階篇(二)UserControl自定義屬性的綁定

 MainWindow中要與UserControl自定義的屬性作綁定使用方法和基礎篇內的一樣,

分為使Code綁定或在XAML內綁定。

在UserControl內,需要將內部原件的屬性再與自定義的屬性作綁定。

MainWindow原件屬性<=>自定義屬性<=>UserControl原件屬性

利用上一篇創建的UserControl中,

Label1使用Code作屬性綁定,

Label2使用XAML作屬性綁定。

由於在WPF中的屬性綁定必須是DependencyProperty,

所以必須將自定義屬性LabelValue註冊為DependencyProperty。

註冊方法(命名原則=>屬性+Property):

    public object LabelValue
    {
        get => GetValue(LabelValueProperty);
        set => SetValue(LabelValueProperty, value);
    }

    public static readonly DependencyProperty LabelValueProperty =
        DependencyProperty.Register("LabelValue", typeof(object), typeof(UserControl1));

UserControl的Label1使用code綁定自定義屬性LabelValue

    public UserControl1()
    {
        InitializeComponent();
        Label1.SetBinding(ContentProperty, new Binding(nameof(LabelValue)) { Source = this });
        //上下為Binding的兩種建構式
        //DataContext = this;
        //Label1.SetBinding(Label.ContentProperty,nameof(LabelValue) );
    }

UserControl的Label2在XAML綁定自定義屬性LabelValue

    <Label x:Name="Label2"

        DataContext="{Binding RelativeSource={RelativeSource AncestorType=local:UserControl1}}"

        Content="{Binding LabelValue}"

        HorizontalAlignment="Stretch"

        Grid.Row="1" 

        VerticalAlignment="Stretch"

        VerticalContentAlignment="Center" 

        HorizontalContentAlignment="Center"

        FontSize="36" 

        Background="#FF60B646"

        Foreground="#FFFBF8F6"/>

執行結果:



2022年3月23日 星期三

[WPF] DataBinding-進階篇(一)創建UserControl

MainWindow:


MainWindow的XAML:

<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">

    <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"/>

        <local:UserControl1 

            HorizontalAlignment="Center" 

            VerticalAlignment="Center"/>

    </Grid>

</Window>

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"

               Content="Label2"

               HorizontalAlignment="Stretch"

               Grid.Row="1" 

               VerticalAlignment="Stretch"

               VerticalContentAlignment="Center" 

               HorizontalContentAlignment="Center"

               FontSize="36" 

               Background="#FF60B646"

               Foreground="#FFFBF8F6"/>

        <Label x:Name="Label3"

               Content="Label3"

               HorizontalAlignment="Stretch"

               Grid.Row="2"

               VerticalAlignment="Stretch"

               VerticalContentAlignment="Center" 

               HorizontalContentAlignment="Center"

               FontSize="36"

               Background="#FF28389C"

               Foreground="#FFFBF8F6"/>

    </Grid>

</UserControl>

[WPF] DataBinding-基礎篇

 建置一個Label,再建置一個Slider當作輸入源。


XAML:

  <Grid>

        <Grid.RowDefinitions>

            <RowDefinition/>

            <RowDefinition/>

        </Grid.RowDefinitions>

        <Label x:Name="Label1" 

               Content="Label1" 

               Grid.Row="0"

               Grid.Column="1"

              HorizontalAlignment="Center"

              VerticalAlignment="Center"

              FontSize="36"/>

        <Slider x:Name="Slider1"

                Grid.Column="1" 

                HorizontalAlignment="Center"

                Grid.Row="1" 

                VerticalAlignment="Center"

                Width="200" 

                ValueChanged="Slider1_ValueChanged" SmallChange="1" Maximum="100"/>

  </Grid>

 WPF的DataBinding分兩種,一種是寫code,一種是寫在XAML,

最大的差異是XAML能即時預覽,使用code的只能在執行後才看得出效果。

使用Code:

      public MainWindow()

        {

            InitializeComponent();

            Label1.SetBinding(ContentProperty, new Binding("Value") { Source = Slider1 });

            //欲綁定的物件.SetBinding(欲綁定的屬性, new Binding(目標的屬性名稱){Source = 目標物件});      

            //PS.欲綁定的屬性必須是DependencyProperty(命名規則=>屬性+Property)

            //Binding物件有兩個建構式,另一個用法如下

            //Label1.SetBinding(ContentProperty, new Binding() { Source = Slider1, Path = new PropertyPath("Value") });      

        }

使用XAML:

 <Label x:Name="Label1"

    Content="{Binding ElementName=Slider1, Path=Value}"

    Grid.Row="0"

    Grid.Column="1"

   HorizontalAlignment="Center"

   VerticalAlignment="Center"

   FontSize="36"/>

   //欲綁定的屬性="{Binding ElementName=現有的物件, Path=物件上的屬性}"

[Windows Form] DataBinding

 建置一個Label,再建置一個TrackBar當作輸入源。


程式碼:

	public Form1()
	{
		InitializeComponent();
		label1.DataBindings.Add(new Binding("Text",trackBar1,"Value"));
		//欲綁定的物件.DataBindings.Add(new Binding(欲綁字的屬性,目標物件,目標屬性));  
	}

 執行結果: