Ввод и вывод из пользовательского элемента управления в WPF - PullRequest
0 голосов
/ 09 мая 2020

Я сделал этот минималистичный c проект, чтобы изучить вывод и ввод с пользовательским контролем, и он работает как задумано. Я хочу спросить, это хороший подход или есть что-то, что не нужно? Я также хочу опубликовать это, потому что существует множество сообщений с конкретными c пользовательскими случаями, но не с простым примером для изучения механики привязки.

Главное окно:

<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));
        }
    }
}

Control 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();
        }
    }
}

Ответы [ 2 ]

3 голосов
/ 09 мая 2020

На этот вопрос очень широкие ответы. Для своих приложений могут использовать разные люди с разными подходами.

Но мы всегда следуем одной общей формуле. Каждое представление - будет иметь свою модель представления. (Опять же, при таком подходе кто-то может сказать, что это может быть неправдой все время).

Из вашего кода (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}" />

Моя привязка usercontrol contorl по-прежнему остается такой же, как и имена свойств, и мы перешли на UserControlViewModel.cs

Теперь вы может удалить все свойства зависимостей из кода, стоящего за UserControl.xaml.cs

Примечание: - Как я сказал в начале своего ответа, этот вопрос имеет широкую область для ответов, и их много возможно Возможности ответить на этот вопрос.

Надеюсь, я попытался дать вам некоторые комментарии со своей стороны. Думаю, это должно дать вам представление о том, как развиваться дальше.

Вы можете попробовать внести эти изменения и сообщить мне, если у вас возникнут какие-либо ошибки или проблемы с привязкой.

0 голосов
/ 10 мая 2020

Предполагая, что вы просто хотите, чтобы представление с полным именем было чем-то вроде «Фамилия, Имя», вы можете фактически удалить свойство FullName из своей модели представления и просто использовать MultiBinding (кстати, свойство StringFormat можно использовать как с MultiBindings и обычные привязки, это довольно изящно, если вы не знакомы с ним).

Что касается меток, хорошо иметь привычку использовать простейший элемент управления, необходимый для выполнения работы, и в этом случае , TextBlocks вполне подойдет, поскольку вы, похоже, не используете какие-либо расширенные функции, предлагаемые Label (например, BorderBru sh, Padding, ContentTemplate и т. Д. c.).

Вы обычно не требуется создавать собственные свойства зависимостей в классах, производных от UserControl, поскольку они обычно разрабатываются с учетом конкретной модели представления. Они более полезны, когда представление не зависит от модели просмотра, а свойства зависимости служат в качестве API, через который другие элементы управления / модели просмотра могут взаимодействовать с ним.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...