Опубликован: 08.07.2011 | Доступ: свободный | Студентов: 1772 / 93 | Оценка: 4.15 / 4.08 | Длительность: 15:28:00
Лекция 3:

Базовые инструменты WPF

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >

В приложениях часто возникают ситуации, когда данные изменяются в коде программы и это изменение желательно увидеть в интерфейсном элементе. Проверим корректность связи от объекта-источника к целевому объекту. Для этого добавим в форму ещё одну кнопку, при нажатии на которую значение свойства Factor будет увеличиваться в 2 раза. При тестировании изменения значения свойства Factor при нажатии кнопки button2 значение свойства Factor (объекта-источника) изменяется, а свойство Text элемента TextBox (целевого объекта) остается прежним ( рис. 3.5).

Проверка корректности программного изменения объекта-источника

Рис. 3.5. Проверка корректности программного изменения объекта-источника

Проверка корректности программного изменения объекта-источника

Это является результатом того, что свойство Text элемента TextBox не уведомляется об изменении свойства Factor, которое не является свойством зависимостей. Для обеспечения уведомления между объектом-источником и целевым объектом можно поступить следующим образом:

  • сделать свойство Factor свойством зависимостей;
  • инициировать событие изменения свойства Factor ;
  • реализовать в классе, содержащем свойство Factor интерфейс INotifyPropertyChanged.

Продемонстрируем первый и третий способ решения задачи обеспечения синхронизации представления данных между объектом-источником и целевым объектом.

Определим в классе Window1 свойство зависимостей Factor. Измененный код принимает следующий вид:

public partial class Window1 : Window
{
    public static readonly DependencyProperty FactorProperty;
    public float Factor
    {
        set{SetValue(FactorProperty, value);}
        get{return (float)GetValue(FactorProperty);}
    }
    static Window1()
    {
        FactorProperty = DependencyProperty.Register("Factor", 
       typeof(float), typeof(Window1), 
       new FrameworkPropertyMetadata (0.0F,
      FrameworkPropertyMetadataOptions.AffectsRender));
    }
    public Window1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show(Factor.ToString(), "Проверка значения параметра");
    }
    private void button2_Click(object sender, RoutedEventArgs e)
    {
        Factor *= 2;
    }
}

Вначале определяется свойство зависимостей:

public static readonly DependencyProperty FactorProperty;

Затем формируют методы доступа к свойству зависимостей:

public float Factor
{
    set{SetValue(FactorProperty, value);}
     get{return (float)GetValue(FactorProperty);}
}

На последнем шаге свойство зависимостей регистрируется в статическом конструкторе:

static Window1()
{
    FactorProperty = DependencyProperty.Register("Factor", 
    typeof(float), typeof(Window1), 
    new FrameworkPropertyMetadata (0.0F,
   FrameworkPropertyMetadataOptions.AffectsRender));
}

Теперь при нажатии кнопки button2 изменяется значение свойства Factor и целевого объекта свойства Text элемента TextBox.

Для реализации интерфейса INotifyPropertyChanged создадим класс FactorClass:

public class FactorClass: INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  public void OnPropertyChanged(PropertyChangedEventArgs e)
  {
       if (PropertyChanged != null)
           PropertyChanged(this, e);
  }
  private float factor;
  public float Factor
  {
    get { return factor; }
    set 
    { 
      factor = value;
      OnPropertyChanged((new PropertyChangedEventArgs("Factor"));
    }
  }
}

В классе объявляется событие PropertyChanged:

event PropertyChangedEventHandler PropertyChanged;

Затем кодируется обработчик события OnPropertyChanged:

public void OnPropertyChanged(PropertyChangedEventArgs e)
  {
       if (PropertyChanged != null)
           PropertyChanged(this, e);
}

Для свойства Factor при его изменении производится генерация события:

public float Factor
  {
    get { return factor; }
    set 
    { 
      factor = value;
      OnPropertyChanged OnPropertyChanged(new
             PropertyChangedEventArgs("Factor"));
    }
  }

С учетом создания класса FactorClass код окна Window1 требует корректировки:

public partial class Window1 : Window
{
    private static FactorClass factor = new FactorClass();
    public static FactorClass Factor
    {
        get { return Window1.factor; }
    }
   
    public Window1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show(Factor.Factor.ToString(), 
                       "Проверка значения параметра");
    }
    private void button2_Click(object sender, RoutedEventArgs e)
    {
        Factor.Factor *= 2;
    }
}

В коде окна Window1 добавляется поле и свойства Factor типа FactorClass:

private static FactorClass factor = new FactorClass();
public static FactorClass Factor
{
    get { return Window1.factor; }
}

В обработчиках кнопок обращение к свойству Factor производят через экземпляр класса FactorClass.

Тестирование внутреннего изменения значение свойства Factor и целевого объекта свойства Text элемента TextBox показывают синхронизацию изменений свойств.

При использовании класса FactorClass выражение привязки можно упростить. В этом случае создается привязка без указания источника, то есть необходимо указать только свойство Factor класса FactorClass:

<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1" 
Text="{Binding Path = Factor}"/>

Элемент Grid спроектированного окна является контейнером для всех визуальных элементов пользовательского интерфейса. Если свойству DataContext элемента Grid задать значение экземпляра класса FactorClass, то выражение привязки данных будет использовать общедоступные свойства класса FactorClass для заполнения себя данными. Установка свойства DataContext элемента Grid может быть сделана в конструкторе окна:

public Window1()
{
    InitializeComponent();
    gridPrimir.DataContext = Factor;
}

Проведем тестирование созданного приложения. Вначале введем значение 0.3 и проверим результат ( рис. 3.6).

Проверка корректности привязки данных

Рис. 3.6. Проверка корректности привязки данных

Далее нажмем кнопку "Увеличить в 2 раза" и посмотрим реакцию приложения ( рис. 3.7). Приложение работает корректно.

Проверка корректности привязки данных при программном изменении объекта-источника

Рис. 3.7. Проверка корректности привязки данных при программном изменении объекта-источника

В рассматриваемом примере поддерживается двусторонняя связь между объектом-источником и целевым объектом. В общем случае направление привязки может быть однонаправленным и двунаправленным. Это определяется свойством Binding.Mode, которое может принимать следующие значения:

  • OneWay определяет, что целевое свойство обновляется при изменении свойства-источника;
  • TwoWay определяет, что и источник и целевое свойство обновляются при изменении какого-либо свойства;
  • OneTime определяет, что целевое свойство устанавливается изначально на основе значения свойства-источника;
  • OneWaySource определяет, что исходное свойство-источника обновляется при изменении целевого свойства, которое никогда не обновляется;
  • Default определяет, что тип привязки зависит от целевого свойства. Так для свойства TextBox.Text по умолчанию будет TwoWay.

Для явного задания направления привязки необходимо сформировать свойство Mode целевого объекта:

<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1" 
Text="{Binding Path = Factor, Mode = TwoWay}" />

При применении привязки нужно принимать во внимание режим обновления данных между источником и целевым свойством. Режим обновления задается свойством Binding.UpdateSourceTrigger, которое может принимать следующие значения:

  • PropertyChanged задает обновление источника немедленно после обновления целевого свойства;
  • LostFocus задает обновление источника, когда целевой элемент теряет фокус;
  • Expicit задает обновление источника только при вызове метода BindingExpression.UpdateSource() ;
  • Default определяет, что обновления определяются метаданными целевого свойства.

Если нам необходимо немедленное обновление целевого свойства и источника, то необходимо добавить изменения с XAML разметку элемента TextBox:

<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1" 
Text="{Binding Path = Factor, Mode = TwoWay, UpdateSourceTrigger = PropertyChanged}" />
< Лекция 2 || Лекция 3: 12345 || Лекция 4 >
Александр Петров
Александр Петров

При загрузке данных из БД возникает исключение InvalidOperationException с сообщением: Элемент коллекции должен быть пустым перед использованием ItemsSource. Знаю, что для заполнения DataGrid можно использовать коллекции Items или ItemsSource, но одновременно их использовать нельзя: если задано значение для свойства ItemsSource и в коде C# добавляется элемент в Items, возникает исключение. 
Вопрос, как отследить и отключить добавление элемента в Items?

Максим Спиридонов
Максим Спиридонов

В пятой лекции на второй странице в компиляторе выскакивает ошибка в строчке :

ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;

Ошибка CS0029

Не удается неявно преобразовать тип "System.Data.Entity.DbSet<WpfApplProject.Employee>" в "System.Data.Entity.Core.Objects.ObjectQuery<WpfApplProject.Employee>".

в using прописал все как положено, здесь похоже именно с преобразованием типов проблемы