C# WPF 自定义控件 UserControl 绑定

主界面

<Window x:Class="MyWPFSimple2.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:MyWPFSimple2"
        mc:Ignorable="d" WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="460" Width="500">
    
    <Window.DataContext>
        <local:VMMainWindow/>
    </Window.DataContext>
    <Window.Resources>
        <ResourceDictionary Source="PersonInfoDTmpl.xaml"/>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <GroupBox Header="绑定 DataContext">
                <local:PersonInfo/>
            </GroupBox>
            <Button x:Name="btnShow" Click="btnShow_Click" Content="显示信息" Margin="0 5 0 0"/>
            <GroupBox Header="散装">
                <StackPanel HorizontalAlignment="Center" Margin="5">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="姓名:" VerticalAlignment="Center"/>
                        <TextBox BorderBrush="Black" Height="30" Width="150" VerticalContentAlignment="Center" Text="{Binding Name}"/>
                    </StackPanel>
                    <StackPanel Orientation="Horizontal" Margin="0 2 0 0">
                        <TextBlock Text="年龄:" VerticalAlignment="Center"/>
                        <TextBox BorderBrush="Black" Height="30" Width="150" VerticalContentAlignment="Center" Text="{Binding Age}" />
                    </StackPanel>
                </StackPanel>
            </GroupBox>
            <GroupBox Header="数据模板" HorizontalAlignment="Center" Margin="5">
                <ContentControl ContentTemplate="{StaticResource DPersonInfoTmpl}" Content="{Binding}"/>
            </GroupBox>
            <GroupBox Header="绑定自己">
                <local:PersonInfoTest PersonName="{Binding Name}" PersonAge="{Binding Age}"/>
            </GroupBox>
        </StackPanel>
    </Grid>
</Window>

BehindCode:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MyWPFSimple2
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            m_VMMainWindow = DataContext as VMMainWindow;
        }

        private VMMainWindow m_VMMainWindow;
        private void btnShow_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("姓名:" + m_VMMainWindow.Name + "\r\n" + "年龄:" + m_VMMainWindow.Age, "信息");
        }
    }


    public class VMMainWindow : ObservableObject
    {
        public VMMainWindow()
        {
            Name = "令狐冲";
            Age = 25;
        }
        private string m_Name;
        public string Name
        {
            get { return m_Name; }
            set
            {
                m_Name = value;
                RaisePropertyChanged(nameof(Name));
            }
        }

        private int m_Age;
        public int Age
        {
            get { return m_Age; }
            set
            {
                m_Age = value;
                RaisePropertyChanged(nameof(Age));
            }
        }
    }
}

绑定 Window 的 DataContext

PersonInfo.xaml 控件

<UserControl x:Class="MyWPFSimple2.PersonInfo"
             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:MyWPFSimple2" 
             mc:Ignorable="d"  Background="LightBlue"
             d:DesignHeight="70" d:DesignWidth="200"
             DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext}"
             >
    
    <UserControl.Resources>
        <local:PerAgeConvert x:Key="agg_add_one"/>
        <local:PersonNameConvert x:Key="name_with_sub"/>
    </UserControl.Resources>

    <Grid>
        <StackPanel HorizontalAlignment="Center" Margin="5">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="姓名:" VerticalAlignment="Center"/>
                <TextBox BorderBrush="Black" Height="30" Width="150" VerticalContentAlignment="Center"
                         Text="{Binding Name, Converter={StaticResource name_with_sub}}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="0 2 0 0">
                <TextBlock Text="年龄:" VerticalAlignment="Center"/>
                <TextBox BorderBrush="Black" Height="30" Width="150" VerticalContentAlignment="Center" 
                         Text="{Binding Age, Converter={StaticResource agg_add_one}}"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</UserControl>

BehindCode:

这种方式,没有 behindcode。

namespace MyWPFSimple2
{
    /// <summary>
    /// PersonInfo.xaml 的交互逻辑
    /// </summary>
    public partial class PersonInfo : UserControl
    {
        public PersonInfo()
        {
            InitializeComponent();
        }
    }     
}

散装

散装的就不说了。

数据模板 DataTemplate

这个也是直接找的 DataContext 绑定。

PersonInfoDTmpl.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:local="clr-namespace:MyWPFSimple2">

    <local:PerAgeConvert x:Key="agg_add_one"/>
    <local:PersonNameConvert x:Key="name_with_sub"/>

    <DataTemplate x:Key="DPersonInfoTmpl">
        <StackPanel HorizontalAlignment="Center" Margin="5">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="姓名:" VerticalAlignment="Center"/>
                <TextBox BorderBrush="Black" Height="30" Width="150" VerticalContentAlignment="Center"
                         Text="{Binding Name}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="0 2 0 0">
                <TextBlock Text="年龄:" VerticalAlignment="Center"/>
                <TextBox BorderBrush="Black" Height="30" Width="150" VerticalContentAlignment="Center" 
                         Text="{Binding Age}"/>
            </StackPanel>
        </StackPanel>
    </DataTemplate>

</ResourceDictionary>

自定义依赖属性

这种方式,没有测试成功。 ViewModel 更改能通知到它,但它自己更改没有通知到 VM,有时间更熟悉WPF后再测试一下。

主界面绑定的是 clr 属性,而 UserControl 绑定的是 Property 属性, UserControl Property 更改的时候没有通知到 clr 属性,没能实现双向绑定。

PersonInfoTest.xaml

<UserControl x:Class="MyWPFSimple2.PersonInfoTest"
              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:MyWPFSimple2" 
             mc:Ignorable="d"  Background="LightPink"
             d:DesignHeight="70" d:DesignWidth="200"
            >

    <UserControl.Resources>
        <local:PerAgeConvert x:Key="agg_add_one"/>
        <local:PersonNameConvert x:Key="name_with_sub"/>
    </UserControl.Resources>

    <Grid>
        <StackPanel HorizontalAlignment="Center" Margin="5">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="姓名:" VerticalAlignment="Center"/>
                <TextBox BorderBrush="Black" Height="30" Width="150" VerticalContentAlignment="Center"
                       Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=PersonName, Mode=TwoWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="0 2 0 0">
                <TextBlock Text="年龄:" VerticalAlignment="Center"/>
                <TextBox BorderBrush="Black" Height="30" Width="150" VerticalContentAlignment="Center" 
                         Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=PersonAge, Mode=TwoWay}"/>
            </StackPanel>
        </StackPanel>
    </Grid>

</UserControl>

BehindCode:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MyWPFSimple2
{
    /// <summary>
    /// PersonInfoTest.xaml 的交互逻辑
    /// </summary>
    public partial class PersonInfoTest : UserControl, INotifyPropertyChanged
    {
        public PersonInfoTest()
        {
            InitializeComponent();
            //DefaultStyleKeyProperty.OverrideMetadata(typeof(PersonInfoTest), new FrameworkPropertyMetadata(typeof(PersonInfoTest)));
        }

        public string PersonName
        {
            get
            {
                return (string)GetValue(PersonNameProperty);
            }
            set
            {
                SetValue(PersonNameProperty, value);
                RaisePropertyChanged(nameof(PersonName));
            }
        }

        // Using a DependencyProperty as the backing store for PersonName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PersonNameProperty =
            DependencyProperty.Register("PersonName", typeof(string), typeof(PersonInfoTest), new PropertyMetadata("悟空"));

        public int PersonAge
        {
            get
            {
                return (int)GetValue(PerAgeProperty);
            }
            set
            {
                SetValue(PerAgeProperty, value);
                RaisePropertyChanged(nameof(PersonAge));
            }
        }

        // Using a DependencyProperty as the backing store for PerAge.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PerAgeProperty =
            DependencyProperty.Register("PersonAge", typeof(int), typeof(PersonInfoTest), new PropertyMetadata(0));

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void RaisePropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Convert.cs

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace MyWPFSimple2
{
    public sealed class PersonNameConvert : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string name = (string)value;
            return name + " 先生/女生";
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string val = (string)value;
            return val.Split(' ')[0];
        }
    }

    public sealed class PerAgeConvert : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            int val = (int)value;
            return val + 1;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            int val = int.Parse((string)value);
            return val - 1;
        }
    }
}
posted @ 2022-11-20 19:49  double64  阅读(616)  评论(0编辑  收藏  举报