WPF中用户控件的输入和输出

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF中用户控件的输入和输出相关的知识,希望对你有一定的参考价值。

我做了这个简约的项目,目的是通过用户控制来学习输出和输入,并且它按预期工作。我想问一下,这是一个好方法还是有不必要的东西?我也想发表这篇文章,因为有很多关于特定用户案例的文章,但是没有一个简单的例子来学习绑定机制。

主窗口:

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

MainVM:

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>
  1. 我通常不喜欢在xaml中设置数据上下文。相反,我更喜欢将其设置在后面的代码中(大部分来自构造函数)

  2. 而不是在用户控件中创建依赖项属性,并将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,如果您不熟悉的话,它非常漂亮)。

对于Labels,养成使用完成工作所需的最简单控件的习惯,在这种情况下,TextBlocks就可以了,因为您似乎并没有使用任何扩展功能标签提供的内容(例如BorderBrush,Padding,ContentTemplate等)。

另一答案
[假设您只希望全名视图类似于“ Surname,Name”,实际上可以从视图模型中删除FullName属性,而只需使用MultiBinding(顺便说一下,StringFormat属性可以与MultiBindings和常规Bindings,如果您不熟悉的话,它非常漂亮)。

对于Labels,养成使用完成工作所需的最简单控件的习惯,在这种情况下,TextBlocks就可以了,因为您似乎并没有使用任何扩展功能标签提供的内容(例如BorderBrush,Padding,ContentTemplate等)。

以上是关于WPF中用户控件的输入和输出的主要内容,如果未能解决你的问题,请参考以下文章

将 ErrorTemplate 添加到 WPF 用户控件会禁用 TextBox 输入

《深入浅出WPF》学习总结之控件与布局

wpf如何根据输入信息动态生成treeview

WPF 用户控件分享之边上带输入框的圆圈

WPF开发登录窗口之——添加文本输入框用户控件

WPF用户控件嵌套控件-从用户控件切换用户控件