Метод получения / установки является функцией обычных свойств C #.Он не уникален для WPF.
Этот односторонний / двусторонний материал говорит о привязке данных WPF, которая не требует создания свойств зависимости - просто для их использования.
Свойства зависимостей встроены в сами элементы управления.Они позволяют напрямую ссылаться на эти свойства при добавлении экземпляров вашего элемента управления в форму.Они позволяют вашему пользовательскому элементу управления чувствовать себя немного более «родным».
Обычно они используются для реализации свойства, которое может использовать привязку данных.В ваших приложениях вы будете просто использовать привязку данных, а не внедрять новые хуки для нее.
... если кто-то нажмет на кнопку добавления или редактирования, всеполя ввода данных станут «включенными» либо с пустыми данными для новой записи, либо с редактированием существующих данных.В то же время кнопки добавления / редактирования станут недоступными, но теперь кнопки «Сохранить / Отменить» станут активными.
Аналогично, после завершения с помощью кнопки «Сохранить / Отменить» будут отключены все поля ввода, сохранение / отмена.и снова включите кнопки Добавить / Редактировать.
Я бы выполнил то, что вы хотите сделать с помощью:
- Модель представления
- Привязка данныхв представлении для этой модели представления
- Предоставление ICommand для этой модели представления (для кнопок)
- INotifyPropertyChanged для модели представления (для всех свойств)
Нет новогосвойства зависимости должны быть созданы для этого сценария.Вы просто будете использовать существующие для привязки данных.
Вот пример кода / учебное пособие по выполнению WPF со связыванием данных и стилю MVVM.
Настройка проекта
Я создал приложение WPF в мастере создания проекта и назвал его MyProject
.
Я настроил имя проекта и пространства имен в соответствии с общепринятой схемой вещей.Вы должны установить эти свойства в обозревателе решений -> проект -> щелчок правой кнопкой мыши -> свойства.
У меня также есть настраиваемая схема папок, которую я люблю использовать для проектов WPF:
Я поместил представление в его собственную папку «Вид» для организационных целей.Это также отражается в пространстве имен, поскольку ваши пространства имен должны соответствовать вашим папкам (namespace MyCompany.MyProject.View
).
Я также отредактировал AssemblyInfo.cs и очистил ссылки на сборку и конфигурацию приложения, но это всего лишь некоторая утомительная вещьчто я оставлю в качестве упражнения для читателя:)
Создание представления
Начните с дизайнера, и все будет выглядеть красиво.Не добавляйте какой-либо код или еще какую-либо работу.Просто поиграйте в дизайнере, пока все не будет выглядеть правильно (особенно при изменении размера).Вот что я закончил:
View / EntryView.xaml:
<Window x:Class="MyCompany.MyProject.View.EntryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Entry View" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Text="Test 1" Grid.Row="0" />
<TextBox Text="Test 2" Grid.Row="1" Margin="0,6,0,0" />
<TextBox Text="Test 3" Grid.Row="2" Margin="0,6,0,0" />
<TextBox Text="Test 4" Grid.Row="3" Margin="0,6,0,0" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="Edit" IsEnabled="True" Grid.Column="0"
HorizontalAlignment="Left" Width="75" />
<Button Content="Save" IsEnabled="False" Grid.Column="1"
Width="75" />
<Button Content="Cancel" IsEnabled="False" Grid.Column="2"
Width="75" Margin="6,0,0,0" />
</Grid>
</Grid>
</Window>
View / EntryView.xaml.cs:
using System.Windows;
namespace MyCompany.MyProject.View
{
public partial class EntryView : Window
{
public EntryView()
{
InitializeComponent();
}
}
}
Я не создал Name
свойств для этих элементов управления.Это специально.Я собираюсь использовать MVVM, и не буду использовать какой-либо код позади.Я позволю дизайнеру делать то, что он хочет, но я не буду касаться этого кода.
Создание модели представления
Далее я сделаюмодель моего взгляда.Это должно быть разработано таким образом, чтобы оно обслуживало представление, но в идеале могло бы быть независимым от представления.Я не буду сильно беспокоиться об этом, но дело в том, что у вас нет , чтобы иметь контроль четности 1-в-1 и просматривать объекты модели.
Я пытаюсьчтобы мои представления / модели представления имели смысл в более широком контексте приложения, поэтому я начну рассматривать модель представления здесь.Мы сделаем эту «редактируемую форму» записью rolodex.
Мы создадим вспомогательный класс, который нам нужен в первую очередь ...
ViewModel / DelegateCommand.cs:
using System;
using System.Windows.Input;
namespace MyCompany.MyProject.ViewModel
{
public class DelegateCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public DelegateCommand(Action execute)
: this(execute, CanAlwaysExecute)
{
}
public DelegateCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
if (canExecute == null)
throw new ArgumentNullException("canExecute");
_execute = o => execute();
_canExecute = o => canExecute();
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
private static bool CanAlwaysExecute()
{
return true;
}
}
}
ViewModel / EntryViewModel.cs:
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace MyCompany.MyProject.ViewModel
{
public class EntryViewModel : INotifyPropertyChanged
{
private readonly string _initialName;
private readonly string _initialEmail;
private readonly string _initialPhoneNumber;
private readonly string _initialRelationship;
private string _name;
private string _email;
private string _phoneNumber;
private string _relationship;
private bool _isInEditMode;
private readonly DelegateCommand _makeEditableOrRevertCommand;
private readonly DelegateCommand _saveCommand;
private readonly DelegateCommand _cancelCommand;
public EntryViewModel(string initialNamename, string email,
string phoneNumber, string relationship)
{
_isInEditMode = false;
_name = _initialName = initialNamename;
_email = _initialEmail = email;
_phoneNumber = _initialPhoneNumber = phoneNumber;
_relationship = _initialRelationship = relationship;
MakeEditableOrRevertCommand = _makeEditableOrRevertCommand =
new DelegateCommand(MakeEditableOrRevert, CanEditOrRevert);
SaveCommand = _saveCommand =
new DelegateCommand(Save, CanSave);
CancelCommand = _cancelCommand =
new DelegateCommand(Cancel, CanCancel);
}
public string Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
public string Email
{
get { return _email; }
set
{
_email = value;
RaisePropertyChanged("Email");
}
}
public string PhoneNumber
{
get { return _phoneNumber; }
set
{
_phoneNumber = value;
RaisePropertyChanged("PhoneNumber");
}
}
public string Relationship
{
get { return _relationship; }
set
{
_relationship = value;
RaisePropertyChanged("Relationship");
}
}
public bool IsInEditMode
{
get { return _isInEditMode; }
private set
{
_isInEditMode = value;
RaisePropertyChanged("IsInEditMode");
RaisePropertyChanged("CurrentEditModeName");
_makeEditableOrRevertCommand.RaiseCanExecuteChanged();
_saveCommand.RaiseCanExecuteChanged();
_cancelCommand.RaiseCanExecuteChanged();
}
}
public string CurrentEditModeName
{
get { return IsInEditMode ? "Revert" : "Edit"; }
}
public ICommand MakeEditableOrRevertCommand { get; private set; }
public ICommand SaveCommand { get; private set; }
public ICommand CancelCommand { get; private set; }
private void MakeEditableOrRevert()
{
if (IsInEditMode)
{
// Revert
Name = _initialName;
Email = _initialEmail;
PhoneNumber = _initialPhoneNumber;
Relationship = _initialRelationship;
}
IsInEditMode = !IsInEditMode; // Toggle the setting
}
private bool CanEditOrRevert()
{
return true;
}
private void Save()
{
AssertEditMode(isInEditMode: true);
IsInEditMode = false;
// Todo: Save to file here, and trigger close...
}
private bool CanSave()
{
return IsInEditMode;
}
private void Cancel()
{
AssertEditMode(isInEditMode: true);
IsInEditMode = false;
// Todo: Trigger close form...
}
private bool CanCancel()
{
return IsInEditMode;
}
private void AssertEditMode(bool isInEditMode)
{
if (isInEditMode != IsInEditMode)
throw new InvalidOperationException();
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged Members
}
}
Как обычно для этого типа рабочего процесса, есть некоторые требования, которые я пропустил при первоначальном создании представления. Например, я понял, что имеет смысл иметь функцию «отменить», которая отменяет изменения, но сохраняет диалог открытым. Я также понял, что мог бы повторно использовать кнопку редактирования для этой цели. Поэтому я создал свойство, которое буду читать, чтобы получить имя кнопки редактирования.
Модель представления содержит большое количество кода для выполнения чего-то простого, но большая часть - это шаблон для подключения свойств. Этот шаблон дает вам некоторую мощность, хотя. Это помогает изолировать вас от вашего представления, поэтому ваше представление может резко измениться без изменений или только незначительных изменений в модели представления.
Если модель представления становится слишком большой, вы можете начать вставлять ее в дополнительные модели подвидов. Создавайте их там, где это наиболее удобно, и возвращайте их в качестве свойств в этой модели представления. Механизм связывания данных WPF поддерживает связывание контекста данных. Об этом контексте данных вы узнаете чуть позже, когда мы все выясним.
Подключение модели к нашему виду
Чтобы подключить вид к модели вида, необходимо установить свойство DataContext
для вида, чтобы указывать на модель вида.
Некоторым людям нравится создавать экземпляры и указывать модель представления в коде XAML. Хотя это может сработать, мне нравится держать представление и модель представления независимыми друг от друга, поэтому я использую третий класс, чтобы соединить их.
Обычно я использовал бы контейнер внедрения зависимостей для подключения всего моего кода, что требует много работы, но сохраняет все части независимыми. Но для такого простого приложения мне нравится использовать класс App
, чтобы связать все вместе. Пойдем отредактируем это:
App.xaml:
<Application x:Class="MyCompany.MyProject.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="ApplicationStartup">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs:
using System.Windows;
namespace MyCompany.MyProject
{
public partial class App : Application
{
private void ApplicationStartup(object sender, StartupEventArgs e)
{
// Todo: Somehow load initial data...
var viewModel = new ViewModel.EntryViewModel(
"some name", "some email", "some phone number",
"some relationship"
);
var view = new View.EntryView()
{
DataContext = viewModel
};
view.Show();
}
}
}
Теперь вы можете запустить свой проект, хотя построенная нами логика ничего не даст. Это потому, что наше начальное представление создано, но на самом деле оно не выполняет привязку данных.
Настройка привязки данных
Давайте вернемся и отредактируем представление, чтобы закончить все это.
Редактирование View / EntryView.xaml:
<Window x:Class="MyCompany.MyProject.View.EntryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Rolodex Entry"
Height="350" Width="525"
MinWidth="300" MinHeight="200">
<Grid Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Name:" Grid.Column="0" Grid.Row="0" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1"
Grid.Row="0" Margin="6,0,0,0" />
<TextBlock Text="E-mail:" Grid.Column="0" Grid.Row="1"
Margin="0,6,0,0" />
<TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1"
Grid.Row="1" Margin="6,6,0,0" />
<TextBlock Text="Phone Number:" Grid.Column="0" Grid.Row="2"
Margin="0,6,0,0" />
<TextBox Text="{Binding PhoneNumber, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1" Grid.Row="2"
Margin="6,6,0,0" />
<TextBlock Text="Relationship:" Grid.Column="0" Grid.Row="3"
Margin="0,6,0,0" />
<TextBox Text="{Binding Relationship, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1" Grid.Row="3"
Margin="6,6,0,0" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="{Binding CurrentEditModeName}"
Command="{Binding MakeEditableOrRevertCommand}"
Grid.Column="0" HorizontalAlignment="Left"
Width="75" />
<Button Content="Save" Command="{Binding SaveCommand}"
Grid.Column="1" Width="75" />
<Button Content="Cancel" Command="{Binding CancelCommand}"
Grid.Column="2" Width="75" Margin="6,0,0,0" />
</Grid>
</Grid>
</Window>
Я проделал большую работу здесь. Во-первых, статичные вещи:
- Я изменил название формы в соответствии с идеей Rolodex
- Я добавил метки для полей, так как теперь я знаю, что они применяются к
- Я изменил минимальную ширину / высоту, так как заметил, что элементы управления обрезаются
Следующая привязка данных:
- Я связал все текстовые поля с соответствующими свойствами в модели представления
- Я сделал текстовые поля обновлять модель вида при каждом нажатии (
UpdateSourceTrigger=PropertyChanged
). Это не обязательно для этого приложения, но может быть полезно в будущем. Я добавил его, чтобы избавить вас от необходимости искать его, когда вам это нужно:)
- Я связал поле
IsEnabled
каждого текстового поля со свойством IsInEditMode
- Я привязал кнопки к соответствующим командам
- Я связал имя кнопки редактирования (свойство
Content
) с соответствующим свойством в модели вида
Вот результат
Теперь работает вся логика пользовательского интерфейса, кроме тех, которые мы оставили в комментарии Todo
. Я оставил их невыполненными, потому что они имеют отношение к конкретной архитектуре приложения, и я не хотел вдаваться в подробности для этой демонстрации.
Кроме того, в vanilla WPF нет очень чистого MVVM-способа закрыть форму, о которой я знаю. Вы можете использовать выделенный код, чтобы сделать это, или вы можете использовать одну из десятков библиотек надстроек WPF, которые обеспечивают их собственный более чистый способ сделать это.
Свойства зависимостей
Возможно, вы заметили, что я не создал ни одного пользовательского свойства зависимости в своем коде. Все свойства зависимостей, которые я использовал, относились к существующим элементам управления (например, Text
, Content
и Command
). Вот как это обычно работает в WPF, потому что привязка данных и стиль (в который я не попал) дает вам много вариантов. Он позволяет полностью настраивать внешний вид, поведение и действия встроенных элементов управления.
В предыдущих платформах Windows GUI вам часто приходилось создавать подклассы существующих элементов управления или создавать собственные элементы управления, чтобы получить индивидуальный внешний вид. Единственными причинами создания пользовательских элементов управления в WPF является комбинирование шаблонов нескольких элементов управления с возможностью повторного использования или создание совершенно нового элемента управления с нуля.
например. если вы делали текстовое поле для автозаполнения, которое связано с всплывающим элементом управления, чтобы отобразить значения, из которых оно автоматически заполняется. В таком случае вы можете захотеть создать пользовательский элемент управления с пользовательскими свойствами зависимостей (например, источником автозаполнения). Таким образом, вы можете повторно использовать элемент управления в приложении и других приложениях.
Если вы не создаете пользовательские элементы управления или не создаете специальные классы, не связанные с пользовательским интерфейсом, которые могут непосредственно создавать экземпляры и использовать их в XAML и привязке данных, вам, вероятно, не потребуется создавать свойства зависимостей.