none
Правильное обновление данных RRS feed

  • Вопрос

  • В моем wpf приложении я пытаюсь обновить данные в БД открывая диалоговое окно, в которое данные попадают из выделенной строки родительского окна. По моей задумке данные в БД и DataGrid должны измениться после нажатия кнопки ОК диалогового окна, но в DataGrid они изменяются сразу после изменения данных в текстовых полях дочернего окна. Подскажите пожалуйста, что я делаю не так.

    Вот мой код:


    BankWindow window;
    private CashRegContext DbContext;
    public ObservableCollection<Bank> Banks { get; private set; }
            
    public MainViewModel()
    {
       DbContext = new CashRegContext();
       Banks = new ObservableCollection<Bank>(DbContext.Banks);
    }
    
    #region Получаем выделенный банк
    private Bank selectBank;
    public Bank SelectBank
    {
      get
      {
         return this.selectBank;
      }
      set
      {
         this.selectBank = value;
         this.RaisePropertyChanged("SelectBank");
      }
    }
    #endregion

    Обновление происходит следующим образом:

    DbContext.Banks.Attach(CurrentBank);
    DbContext.Entry(CurrentBank).State = System.Data.Entity.EntityState.Modified;
    DbContext.SaveChanges();
    CurrentBank = null;
    window.Close();
    XAML дочернего окна выглядет вот так:
    <TextBlock Text="Город:" Grid.Row="0" Grid.Column="0" Margin="5"/>
    <TextBox Text="{Binding Path=SelectBank.city, UpdateSourceTrigger=PropertyChanged}"  Grid.Row="0" Grid.Column="1"  Margin="5" />
    <TextBlock Text="Банк:" Grid.Row="1" Grid.Column="0"  Margin="5"/>
    <TextBox Text="{Binding Path=SelectBank.Name, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="1"  Margin="5"/>
    <TextBlock Text="БИК:" Grid.Row="2" Grid.Column="0"  Margin="5"/>
    <TextBox Text="{Binding Path=SelectBank.bik, UpdateSourceTrigger=PropertyChanged}" Grid.Row="2" Grid.Column="1"  Margin="5" />
    <TextBlock Text="Корр. счет:" Grid.Row="3" Grid.Column="0"  Margin="5"/>
    <TextBox Text="{Binding Path=SelectBank.kor_acc, UpdateSourceTrigger=PropertyChanged}" Grid.Row="3" Grid.Column="1"  Margin="5" />


    • Изменено jaguar_sea 28 сентября 2014 г. 23:52
    28 сентября 2014 г. 23:51

Ответы

  • Добрый день.

    У вас к одному и тому-же свойству через Biding привязаны два элемента управления. Поменяли в одном, отобразилось в другом. А вы еще поставили на каждое изменение свойства прокидывать Binding. Вам нужно заменить на вот такую конструкцию:

    <TextBox Text="{Binding Path=SelectBank.city, UpdateSourceTrigger=Explicit}"

    И принудительно вызывать обновление через Binding вызывая UpdateSource, при нажатии на кнопку Ок.
    29 сентября 2014 г. 7:10
    Отвечающий
  • Создаете класс вида:

    class NotEmptyValidation : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            var result = new ValidationResult(false, "Не допустима пустая строка");
            if (value is string && !string.IsNullOrWhiteSpace(value.ToString()))
            {
                result = new ValidationResult(true, null);
            }
            return result;
        }
    }

    Подключаем пространство имен и настраиваем Binding:

    <Window x:Class="WpfApplication3.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication3"
            Title="MainWindow" Height="350" Width="525">
    <TextBox Grid.Column="2" x:Name="tbLastName">
        <TextBox.Text>
            <Binding Path="Person.LastName" UpdateSourceTrigger="Explicit">
                <Binding.ValidationRules>
                    <local:NotEmptyValidation />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

    На кнопке Принять пишем код вида:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        BindingExpression beLastName = tbLastName.GetBindingExpression(TextBox.TextProperty);
        BindingExpression beFirstName = tbFirstName.GetBindingExpression(TextBox.TextProperty);
        if (beLastName.ValidateWithoutUpdate() && beFirstName.ValidateWithoutUpdate())
        {
            beLastName.UpdateSource();
            beFirstName.UpdateSource();
            // Вот здесь можно закрывать View, не забыв уведомить ViewModel
        }
    }

    Работает вот так (левые и правые TextBox привязаны к одному объекту модели, как видите подсвечена ошибка и не применен Binding):

    Полный пример можно посмотреть здесь.

    • Помечено в качестве ответа jaguar_sea 3 октября 2014 г. 14:54
    3 октября 2014 г. 6:04
    Отвечающий

Все ответы

  • Добрый день.

    У вас к одному и тому-же свойству через Biding привязаны два элемента управления. Поменяли в одном, отобразилось в другом. А вы еще поставили на каждое изменение свойства прокидывать Binding. Вам нужно заменить на вот такую конструкцию:

    <TextBox Text="{Binding Path=SelectBank.city, UpdateSourceTrigger=Explicit}"

    И принудительно вызывать обновление через Binding вызывая UpdateSource, при нажатии на кнопку Ок.
    29 сентября 2014 г. 7:10
    Отвечающий
  • Алексей, спасибо за совет.

    Если я вас правильно понял, при нажатии кнопки ОК нужно сделать для каждого TextBox'а

    var be = tb.GetBindingExpression(TextBox.TextProperty);
    if (be != null)
    {
       be.UpdateSource();
    }

    Если так то я не понимаю как должно происходить добавление нового банка...

    При добавление нового банка у меня проходит проверка на возможность совершения этой операции...

    private bool CanExecuteAddBankCommand()
    {
    if (string.IsNullOrEmpty(SelectBank.city) ||
        string.IsNullOrEmpty(SelectBank.Name) ||
        string.IsNullOrEmpty(SelectBank.bik) ||
        string.IsNullOrEmpty(SelectBank.kor_acc))
            return false;
    return true;
    }

    Соответственно без PropertyChanged это сделать не получится.

    Подскажите, пожалуйста, как быть в этой ситуации?

    30 сентября 2014 г. 23:19
  • Такие проверки вы можете выполнить воспользовавшись ValidationRules.

    1 октября 2014 г. 4:46
    Отвечающий
  • Не могли бы привести пример кода ValidationRules? Ни как не получается найти что то похожее.
    3 октября 2014 г. 1:50
  • Создаете класс вида:

    class NotEmptyValidation : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            var result = new ValidationResult(false, "Не допустима пустая строка");
            if (value is string && !string.IsNullOrWhiteSpace(value.ToString()))
            {
                result = new ValidationResult(true, null);
            }
            return result;
        }
    }

    Подключаем пространство имен и настраиваем Binding:

    <Window x:Class="WpfApplication3.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication3"
            Title="MainWindow" Height="350" Width="525">
    <TextBox Grid.Column="2" x:Name="tbLastName">
        <TextBox.Text>
            <Binding Path="Person.LastName" UpdateSourceTrigger="Explicit">
                <Binding.ValidationRules>
                    <local:NotEmptyValidation />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

    На кнопке Принять пишем код вида:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        BindingExpression beLastName = tbLastName.GetBindingExpression(TextBox.TextProperty);
        BindingExpression beFirstName = tbFirstName.GetBindingExpression(TextBox.TextProperty);
        if (beLastName.ValidateWithoutUpdate() && beFirstName.ValidateWithoutUpdate())
        {
            beLastName.UpdateSource();
            beFirstName.UpdateSource();
            // Вот здесь можно закрывать View, не забыв уведомить ViewModel
        }
    }

    Работает вот так (левые и правые TextBox привязаны к одному объекту модели, как видите подсвечена ошибка и не применен Binding):

    Полный пример можно посмотреть здесь.

    • Помечено в качестве ответа jaguar_sea 3 октября 2014 г. 14:54
    3 октября 2014 г. 6:04
    Отвечающий
  • Спасибо, большое!!!
    3 октября 2014 г. 14:54