WPF 中用户控件的输入和输出
Input to and Output from User Control in WPF
我制作了这个简约项目来学习用户控制的输出和输入,它按预期工作。我想问一下,这是一个好方法还是有一些不必要的东西?
我也想 post 这个,因为有大量 post 特定的用户案例,但没有一个简单的示例来学习绑定机制。
主要Window:
<Window x:Class="OutputFromUserControl.View.OutputFromUserControlWindow"
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:OutputFromUserControl.View"
xmlns:uc="clr-namespace:OutputFromUserControl.View.Controls"
xmlns:vm="clr-namespace:OutputFromUserControl.ViewModel"
mc:Ignorable="d"
Title="Output From User Control" Height="450" Width="800">
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
<StackPanel HorizontalAlignment="Left">
<Label Content="Form elements:"/>
<Border CornerRadius="5" BorderBrush="Blue" BorderThickness="1">
<Grid HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="Name Input: " Grid.Row="0" Grid.Column="0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<Label Content="Surname Input: " Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<Label Content="Name Output from Control: " Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1"
Text="{Binding FullName}"
Width="200"
/>
</Grid>
</Border>
<Label Content="User Control:" Margin="0,10,0,0"/>
<Border CornerRadius="5" BorderBrush="Red" BorderThickness="1">
<uc:NameConcatControl x:Name="NameUC"
NameInput="{Binding NameInput}"
SurnameInput="{Binding SurnameInput}"
NameOutput="{Binding FullName, Mode=TwoWay}"
/>
</Border>
</StackPanel>
</Window>
主虚拟机:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace OutputFromUserControl.ViewModel
{
public class MainVM : INotifyPropertyChanged
{
private string nameInput;
public string NameInput {
get { return nameInput; }
set
{
nameInput = value;
OnPropertyChanged(nameof(NameInput));
}
}
private string surnameInput;
public string SurnameInput {
get { return surnameInput; }
set {
surnameInput = value;
OnPropertyChanged(nameof(SurnameInput));
}
}
private string fullName;
public string FullName {
get { return fullName; }
set {
fullName = value;
OnPropertyChanged(nameof(FullName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
对照xaml:
<UserControl x:Class="OutputFromUserControl.View.Controls.NameConcatControl"
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:OutputFromUserControl.View.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="Name Input: " Grid.Row="0" Grid.Column="0"/>
<TextBlock Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput}"
x:Name="NameInputTextBlock"
/>
<Label Content="Surname Input: " Grid.Row="1" Grid.Column="0"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput}"
x:Name="SurnameInputTextBlock"
/>
<Label Content="Name Output: " Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1"
Text="{Binding NameOutput}"
x:Name="OutputNameTextBlock"
/>
</Grid>
</UserControl>
用户控件.cs:
using System.Windows;
using System.Windows.Controls;
namespace OutputFromUserControl.View.Controls
{
/// <summary>
/// Interaction logic for NameConcatControl.xaml
/// </summary>
public partial class NameConcatControl : UserControl
{
public string NameInput {
get { return (string)GetValue(NameInputProperty); }
set { SetValue(NameInputProperty, value); }
}
public static string defaultNameInput = "NameInput";
public static readonly DependencyProperty NameInputProperty =
DependencyProperty.Register("NameInput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultNameInput, SetNameOutput));
public string SurnameInput {
get { return (string)GetValue(SurnameInputProperty); }
set { SetValue(SurnameInputProperty, value); }
}
public static string defaultSurnameInput = "Surname Input";
public static readonly DependencyProperty SurnameInputProperty =
DependencyProperty.Register("SurnameInput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultSurnameInput, SetNameOutput));
public string NameOutput {
get { return (string)GetValue(NameOutputProperty); }
set { SetValue(NameOutputProperty, value); }
}
public static string defaultNameOutput = "Name Output";
public static readonly DependencyProperty NameOutputProperty =
DependencyProperty.Register("NameOutput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultNameOutput));
private static void SetNameOutput(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
NameConcatControl control = (NameConcatControl)d;
string nameInput = "";
string surnameInput = "";
if(e.Property.Name == "NameInput")
{
string newValue = (string)e.NewValue;
nameInput = string.IsNullOrEmpty(newValue) ? "" : newValue;
}
else
{
nameInput = string.IsNullOrEmpty(control.NameInputTextBlock.Text)
? ""
: control.NameInputTextBlock.Text;
}
if(e.Property.Name == "SurnameInput")
{
string newValue = (string)e.NewValue;
surnameInput = string.IsNullOrEmpty(newValue) ? "" : newValue;
}
else
{
surnameInput = string.IsNullOrEmpty(control.SurnameInputTextBlock.Text)
? ""
: control.SurnameInputTextBlock.Text;
}
string fullName = $"{nameInput} {surnameInput}";
control.OutputNameTextBlock.Text = fullName;
control.NameOutput = fullName;
}
public NameConcatControl()
{
InitializeComponent();
}
}
}
这个问题的答案很广泛。不同的人有不同的方法可以用于他们的应用程序。
但我们总是遵循一个共同的公式。
每个视图 - 都有自己的视图模型。 (同样在这种方法中,有人可能会说可能并非一直如此)。
根据您的代码(xaml 和代码),以下是我的观察结果。
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
我通常不喜欢在 xaml 中设置数据上下文。相反,我更喜欢将它设置在代码隐藏(主要来自构造函数)
而不是在用户控件中创建依赖属性并将 MainVM 属性绑定到用户控件的依赖属性。
我更喜欢这样做。
我更喜欢创建一个单独的 UserControlViewModel.cs 并向其中添加所需的属性。
public class UserControlViewModel : INotifyPropertyChanged
{
private string nameInput;
public string NameInput {
get { return nameInput; }
set
{
nameInput = value;
OnPropertyChanged(nameof(NameInput));
}
}
private string surnameInput;
public string SurnameInput {
get { return surnameInput; }
set {
surnameInput = value;
OnPropertyChanged(nameof(SurnameInput));
}
}
private string fullName;
public string FullName {
get { return fullName; }
set {
fullName = value;
OnPropertyChanged(nameof(FullName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
然后我更喜欢将其作为 属性 添加到 MainVM.cs
public class MainVM : INotifyPropertyChanged
{
private UserControlViewModel _userControlViewModel;
public UserControlViewModel UserControlViewModel
{
get { return _userControlViewModel; }
set
{
_userControlViewModel = value;
OnPropertyChanged(nameof(UserControlViewModel));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
// Rest of your code
// You don't need existing properties any more here.
// If you want to access these properties from MainVM then use the UserControlViewModel property and access the members of it.
}
然后我更喜欢将我的 UserControl 的数据上下文设置为此 属性,如下面的 MainWindow.xaml
<uc:NameConcatControl x:Name="NameUC" ="{Binding UserControlViewModel}" />
我的用户控件控件绑定仍然保持不变,因为 属性 名称相同,我们移至 UserControlViewModel.cs
现在您可以从 UserControl.xaml.cs
的代码后面删除所有依赖属性
注意:- 正如我在回答开头所说的那样,这个问题的答案范围很广,并且有很多可能性可以回答这个问题。
我希望我已经尽力为您提供了一些意见。我想这应该会给你一些发展休息的想法..
您可以尝试进行这些更改,如果您遇到任何错误或绑定问题,请告诉我。
假设您只希望全名视图类似于 "Surname, Name",您实际上可以从视图模型中删除 FullName 属性,并且只需使用 MultiBinding(顺便说一句,StringFormat 属性 可以与 MultiBindings 和常规 Bindings 一起使用,如果您不熟悉它,它会非常漂亮)。
至于标签,最好养成使用完成工作所需的最简单控件的习惯,在这种情况下,TextBlocks 就可以了,因为您似乎没有使用任何Label 提供的扩展功能(即 BorderBrush、Padding、ContentTemplate 等)。
您通常不需要在派生的 UserControl 中创建自己的依赖属性 类,因为它们通常在设计时考虑了特定的视图模型。当视图独立于视图模型时,它们更有用,并且依赖属性充当 api,其他 controls/viewmodels 可以通过它与之交互。
<Window x:Class="OutputFromUserControl.View.OutputFromUserControlWindow"
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:OutputFromUserControl.View"
xmlns:uc="clr-namespace:OutputFromUserControl.View.Controls"
xmlns:vm="clr-namespace:OutputFromUserControl.ViewModel"
mc:Ignorable="d"
Title="Output From User Control" Height="450" Width="800">
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
<StackPanel HorizontalAlignment="Left">
<Label Content="Form elements:"/>
<Border CornerRadius="5" BorderBrush="Blue" BorderThickness="1">
<Grid HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Name Input:" Grid.Row="0" Grid.Column="0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<TextBlock Text="Surname Input:" Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<TextBlock Text="Name Output from Control:" Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1" Width="200">
<MultiBinding StringFormat="{}{0}, {1}">
<Binding Path="SurnameInput"/>
<Binding Path="NameInput"/>
</MultiBinding>
</TextBlock>
</Grid>
</Border>
<Label Content="User Control:" Margin="0,10,0,0"/>
<Border CornerRadius="5" BorderBrush="Red" BorderThickness="1">
<uc:NameConcatControl x:Name="NameUC"
NameInput="{Binding NameInput}"
SurnameInput="{Binding SurnameInput}"
NameOutput="{Binding FullName}"
/>
</Border>
</StackPanel>
我制作了这个简约项目来学习用户控制的输出和输入,它按预期工作。我想问一下,这是一个好方法还是有一些不必要的东西? 我也想 post 这个,因为有大量 post 特定的用户案例,但没有一个简单的示例来学习绑定机制。
主要Window:
<Window x:Class="OutputFromUserControl.View.OutputFromUserControlWindow"
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:OutputFromUserControl.View"
xmlns:uc="clr-namespace:OutputFromUserControl.View.Controls"
xmlns:vm="clr-namespace:OutputFromUserControl.ViewModel"
mc:Ignorable="d"
Title="Output From User Control" Height="450" Width="800">
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
<StackPanel HorizontalAlignment="Left">
<Label Content="Form elements:"/>
<Border CornerRadius="5" BorderBrush="Blue" BorderThickness="1">
<Grid HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="Name Input: " Grid.Row="0" Grid.Column="0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<Label Content="Surname Input: " Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<Label Content="Name Output from Control: " Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1"
Text="{Binding FullName}"
Width="200"
/>
</Grid>
</Border>
<Label Content="User Control:" Margin="0,10,0,0"/>
<Border CornerRadius="5" BorderBrush="Red" BorderThickness="1">
<uc:NameConcatControl x:Name="NameUC"
NameInput="{Binding NameInput}"
SurnameInput="{Binding SurnameInput}"
NameOutput="{Binding FullName, Mode=TwoWay}"
/>
</Border>
</StackPanel>
</Window>
主虚拟机:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace OutputFromUserControl.ViewModel
{
public class MainVM : INotifyPropertyChanged
{
private string nameInput;
public string NameInput {
get { return nameInput; }
set
{
nameInput = value;
OnPropertyChanged(nameof(NameInput));
}
}
private string surnameInput;
public string SurnameInput {
get { return surnameInput; }
set {
surnameInput = value;
OnPropertyChanged(nameof(SurnameInput));
}
}
private string fullName;
public string FullName {
get { return fullName; }
set {
fullName = value;
OnPropertyChanged(nameof(FullName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
对照xaml:
<UserControl x:Class="OutputFromUserControl.View.Controls.NameConcatControl"
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:OutputFromUserControl.View.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="Name Input: " Grid.Row="0" Grid.Column="0"/>
<TextBlock Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput}"
x:Name="NameInputTextBlock"
/>
<Label Content="Surname Input: " Grid.Row="1" Grid.Column="0"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput}"
x:Name="SurnameInputTextBlock"
/>
<Label Content="Name Output: " Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1"
Text="{Binding NameOutput}"
x:Name="OutputNameTextBlock"
/>
</Grid>
</UserControl>
用户控件.cs:
using System.Windows;
using System.Windows.Controls;
namespace OutputFromUserControl.View.Controls
{
/// <summary>
/// Interaction logic for NameConcatControl.xaml
/// </summary>
public partial class NameConcatControl : UserControl
{
public string NameInput {
get { return (string)GetValue(NameInputProperty); }
set { SetValue(NameInputProperty, value); }
}
public static string defaultNameInput = "NameInput";
public static readonly DependencyProperty NameInputProperty =
DependencyProperty.Register("NameInput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultNameInput, SetNameOutput));
public string SurnameInput {
get { return (string)GetValue(SurnameInputProperty); }
set { SetValue(SurnameInputProperty, value); }
}
public static string defaultSurnameInput = "Surname Input";
public static readonly DependencyProperty SurnameInputProperty =
DependencyProperty.Register("SurnameInput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultSurnameInput, SetNameOutput));
public string NameOutput {
get { return (string)GetValue(NameOutputProperty); }
set { SetValue(NameOutputProperty, value); }
}
public static string defaultNameOutput = "Name Output";
public static readonly DependencyProperty NameOutputProperty =
DependencyProperty.Register("NameOutput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultNameOutput));
private static void SetNameOutput(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
NameConcatControl control = (NameConcatControl)d;
string nameInput = "";
string surnameInput = "";
if(e.Property.Name == "NameInput")
{
string newValue = (string)e.NewValue;
nameInput = string.IsNullOrEmpty(newValue) ? "" : newValue;
}
else
{
nameInput = string.IsNullOrEmpty(control.NameInputTextBlock.Text)
? ""
: control.NameInputTextBlock.Text;
}
if(e.Property.Name == "SurnameInput")
{
string newValue = (string)e.NewValue;
surnameInput = string.IsNullOrEmpty(newValue) ? "" : newValue;
}
else
{
surnameInput = string.IsNullOrEmpty(control.SurnameInputTextBlock.Text)
? ""
: control.SurnameInputTextBlock.Text;
}
string fullName = $"{nameInput} {surnameInput}";
control.OutputNameTextBlock.Text = fullName;
control.NameOutput = fullName;
}
public NameConcatControl()
{
InitializeComponent();
}
}
}
这个问题的答案很广泛。不同的人有不同的方法可以用于他们的应用程序。
但我们总是遵循一个共同的公式。 每个视图 - 都有自己的视图模型。 (同样在这种方法中,有人可能会说可能并非一直如此)。
根据您的代码(xaml 和代码),以下是我的观察结果。
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
我通常不喜欢在 xaml 中设置数据上下文。相反,我更喜欢将它设置在代码隐藏(主要来自构造函数)
而不是在用户控件中创建依赖属性并将 MainVM 属性绑定到用户控件的依赖属性。
我更喜欢这样做。
我更喜欢创建一个单独的 UserControlViewModel.cs 并向其中添加所需的属性。
public class UserControlViewModel : INotifyPropertyChanged
{
private string nameInput;
public string NameInput {
get { return nameInput; }
set
{
nameInput = value;
OnPropertyChanged(nameof(NameInput));
}
}
private string surnameInput;
public string SurnameInput {
get { return surnameInput; }
set {
surnameInput = value;
OnPropertyChanged(nameof(SurnameInput));
}
}
private string fullName;
public string FullName {
get { return fullName; }
set {
fullName = value;
OnPropertyChanged(nameof(FullName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
然后我更喜欢将其作为 属性 添加到 MainVM.cs
public class MainVM : INotifyPropertyChanged
{
private UserControlViewModel _userControlViewModel;
public UserControlViewModel UserControlViewModel
{
get { return _userControlViewModel; }
set
{
_userControlViewModel = value;
OnPropertyChanged(nameof(UserControlViewModel));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
// Rest of your code
// You don't need existing properties any more here.
// If you want to access these properties from MainVM then use the UserControlViewModel property and access the members of it.
}
然后我更喜欢将我的 UserControl 的数据上下文设置为此 属性,如下面的 MainWindow.xaml
<uc:NameConcatControl x:Name="NameUC" ="{Binding UserControlViewModel}" />
我的用户控件控件绑定仍然保持不变,因为 属性 名称相同,我们移至 UserControlViewModel.cs
现在您可以从 UserControl.xaml.cs
的代码后面删除所有依赖属性注意:- 正如我在回答开头所说的那样,这个问题的答案范围很广,并且有很多可能性可以回答这个问题。
我希望我已经尽力为您提供了一些意见。我想这应该会给你一些发展休息的想法..
您可以尝试进行这些更改,如果您遇到任何错误或绑定问题,请告诉我。
假设您只希望全名视图类似于 "Surname, Name",您实际上可以从视图模型中删除 FullName 属性,并且只需使用 MultiBinding(顺便说一句,StringFormat 属性 可以与 MultiBindings 和常规 Bindings 一起使用,如果您不熟悉它,它会非常漂亮)。
至于标签,最好养成使用完成工作所需的最简单控件的习惯,在这种情况下,TextBlocks 就可以了,因为您似乎没有使用任何Label 提供的扩展功能(即 BorderBrush、Padding、ContentTemplate 等)。
您通常不需要在派生的 UserControl 中创建自己的依赖属性 类,因为它们通常在设计时考虑了特定的视图模型。当视图独立于视图模型时,它们更有用,并且依赖属性充当 api,其他 controls/viewmodels 可以通过它与之交互。
<Window x:Class="OutputFromUserControl.View.OutputFromUserControlWindow"
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:OutputFromUserControl.View"
xmlns:uc="clr-namespace:OutputFromUserControl.View.Controls"
xmlns:vm="clr-namespace:OutputFromUserControl.ViewModel"
mc:Ignorable="d"
Title="Output From User Control" Height="450" Width="800">
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
<StackPanel HorizontalAlignment="Left">
<Label Content="Form elements:"/>
<Border CornerRadius="5" BorderBrush="Blue" BorderThickness="1">
<Grid HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Name Input:" Grid.Row="0" Grid.Column="0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<TextBlock Text="Surname Input:" Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<TextBlock Text="Name Output from Control:" Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1" Width="200">
<MultiBinding StringFormat="{}{0}, {1}">
<Binding Path="SurnameInput"/>
<Binding Path="NameInput"/>
</MultiBinding>
</TextBlock>
</Grid>
</Border>
<Label Content="User Control:" Margin="0,10,0,0"/>
<Border CornerRadius="5" BorderBrush="Red" BorderThickness="1">
<uc:NameConcatControl x:Name="NameUC"
NameInput="{Binding NameInput}"
SurnameInput="{Binding SurnameInput}"
NameOutput="{Binding FullName}"
/>
</Border>
</StackPanel>