2013-05-29 2 views
3

Я новичок в шаблоне MVVM.Bound Button.IsEnabled() Свойство не работает должным образом

Мне было интересно, почему каждый раз мое событие TextChanged() уволено, связанное свойство IsEnabled() не меняет свое состояние. Событие TextChanged() вызывает IsValid() для проверки достоверности данных.

Я имею эту простую ViewModel класс

public class myViewModel : ViewModel 
{ 
    public bool IsOk { get; set; } 
    public RelayCommand OnValidateExecute { get; set; } 

    public myViewModel() 
    { 
     OnValidateExecute = new RelayCommand(p => IsValid(), p => true); 
    } 

    public bool IsValid() 
    { 
     // other codes 
     IsOk = MethodName(); 
     NotifyPropertyChanged("IsOk"); 
    } 
} 

я поставил точку останова на IsValid() и код работает нормально. Мне было интересно, почему свойство IsEnabled не работает должным образом.

Это мой XAML код

<TextBox ...other propeties here....> 
    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="TextChanged"> 
      <i:InvokeCommandAction Command="{Binding OnValidateExecute}"> 
      </i:InvokeCommandAction> 
     </i:EventTrigger> 
    </i:Interaction.Triggers> 
</TextBox> 

<Button x:Name="btnSave" IsEnabled="{Binding IsOk, Mode=TwoWay}" 
      ...other propeties here....> 
    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="Click"> 
      <i:InvokeCommandAction Command="{Binding OnSaveExecute}"> 
      </i:InvokeCommandAction> 
     </i:EventTrigger> 
    </i:Interaction.Triggers> 
</Button> 

То, что я хочу, что когда IsOk свойство ложно, то кнопка должна быть Disabled, в противном случае, Enabled.

Есть ли какие-то ошибки в привязке данных? Если этот вопрос задан раньше, любезно помогите мне перенаправить его.

UPDATE 1

Еще одна проблема, у меня встреча с этим кодом является то, что функция IsValid() срабатывает первый перед установкой значения на текстовом поле. Вот пример. Предполагая, что начальное значение текстового поля 0, когда я изменил его, скажем, на 9, значение, которое будет проверяться, - это предыдущее значение 0 вместо 9. Любая идея, почему это происходит? Есть ли проблема с привязкой?

+1

свойства 'IsEnabled' не работают, как ожидались - То, что вы действительно ожидаете от этой собственности? Если кнопка отключена на пустой текст? – Jawahar

+0

@XAMLLover да. Я попытался отладить код, и значение свойства IsOK изменяется, но кнопка не изменяется. –

ответ

2

Вот ответ и включайте важную часть моей MVVM Framework. Помимо этого, я добавляю дополнительную функцию. Я не могу поместить сюда всю свою библиотеку. Но я уверен, что это поможет.

Если вы используете Commanding, вы должны знать об CanExecuteChanged в интерфейсе ICommand. Вы должны запустить эту команду, когда свойство изменилось. (Я не использую RelayCommand, это 3.party.)

Use my DCommand :) this is the most important part

Он имеет простую реализацию, и имеет FirePropertyChanged метод. Этот метод вызывает CanExecuteChanged ICommand, если он не равен нулю.

Пример команды

Anonymous синтаксис

DCommand commandPost=new DCommand(()=>{ 
     //TODO:Command's real execute method Post() 
     }, 
    ()=> 
     { 
     return this.TextBoxBoundProperty.IsValid; 
     } 
    ) 

Non-анонимным синтаксис

DCommand commandPost=(Post,Validate); 

кроме этого вы должны триггер canexecutechanged следующим способом в CTOR вашей ViewModel в.

this.PropertyChanged += (sender, prop) => 
      { 
     //I preffered canExcuteChange for any property changes for my viewmodel class. You could put your own logic. if(prop.PropertyName.equals("thisone")); 
     //Just works for this class's property changed 
       this.InvokeOnClassPropertyChange(prop.PropertyName,() => 
       { 
        this.commandPost.FirePropertyChanged(); 
       }); 
      } 

InvokeOnClassPropertyChange работает, если свойство является свойством класса ViewModel.

public static void InvokeOnClassPropertyChange(this object instance,string PropertyName,Action action) 
    { 
     Type type = instance.GetType(); 
     var fulllist = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(w => w.DeclaringType == type).ToList(); 
     if (fulllist.Select(p => p.Name).Contains(PropertyName)) 
     { 
      action.Invoke(); 
     } 
    } 

Приведенный выше код показывает InvokeOnClassPropertyChange метод расширения. Ниже показан мой DCommand реализует ICommand.

public class DCommand :ICommand 
    { 
    public void FirePropertyChanged() 
    { 
     if (CanExecuteChanged!=null) 
     CanExecuteChanged(this, EventArgs.Empty);    
    } 

    Func<bool> CanExecuteFunc { get; set; } 
    Action<object> Action { get; set; } 


    public DCommand(Action<object> executeMethod) 
    { 
     this.Action = executeMethod;    
    } 

    public DCommand(Action executeMethod) 
    { 
     this.Action = new Action<object>(
      (prm) => 
      { 
       executeMethod.Invoke(); 
      } 
      ); 
    } 

    public DCommand(Action<object> executeMethod, Func<bool> canExecuteMethod) 
     : this(executeMethod) 
    {    
     this.CanExecuteFunc = canExecuteMethod;    
    } 

    public DCommand(Action executeMethod, Func<bool> canExecuteMethod) 
     : this(executeMethod) 
    { 
     this.CanExecuteFunc = canExecuteMethod; 
    } 

    public bool CanExecute(object parameter=null) 
    { 
     if (CanExecuteFunc == null) 
      return true; 

     return CanExecuteFunc.Invoke(); 
    } 

    public event EventHandler CanExecuteChanged; 

    public void Execute(object parameter=null) 
    { 

     if (CanExecuteFunc == null || CanExecute(parameter)) 
     { 
      Action.Invoke(parameter);     
     } 

    } 
} 

В конце концов, Если вы хотите изменить свойство ViewModel, когда текст TextBox изменяет сразу, вы должны связать этим путем.

Text="{Binding BoundProperty,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"

+0

спасибо за время, позвольте мне попробовать это: ' –

+0

Да, это действительно крадет время с работы (: Я использовал это в течение двух лет. Я ждал хорошего вопроса для обмена. –

+0

Обратите внимание, что' RelayCommand 'support' CanExecute', как 'DCommand' в ответе @ DavutGürbüz.' FirePropertyChanged' называется 'RaiseCanExecuteChanged'. – Tonio

1

Ниже мой код:

<StackPanel> 
    <TextBox x:Name="TextBox1" Margin="5,0,5,0" Width="100"> 
     <i:Interaction.Triggers> 
       <i:EventTrigger EventName="TextChanged"> 
        <i:InvokeCommandAction Command="{Binding OnValidateExecute, Mode=OneWay}" CommandParameter="{Binding Text,ElementName=TextBox1}" /> 
       </i:EventTrigger> 
     </i:Interaction.Triggers> 
    </TextBox> 

    <Button x:Name="btnSave" Width="120" Height="25" Content="click" IsEnabled="{Binding IsOk}"> 
     <i:Interaction.Triggers> 
       <i:EventTrigger EventName="Click"> 
        <i:InvokeCommandAction Command="{Binding OnSaveExecute}"> 
        </i:InvokeCommandAction> 
       </i:EventTrigger> 
     </i:Interaction.Triggers> 
    </Button> 
</StackPanel> 

только добавить параметр командной строки.

public class myViewModel : INotifyPropertyChanged 
{ 
    public bool IsOk { get; set; } 
    public string Message { get; set; } 
    public RelayCommand OnValidateExecute { get; set; } 

    public myViewModel() 
    { 
     OnValidateExecute = new RelayCommand(p => 
      { 
       Message = p as string; 
       IsValid(); 
      }, p => true); 
    } 

    public bool IsValid() 
    { 
     bool valid = !string.IsNullOrEmpty(Message); 
     IsOk = valid; 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs("IsOk")); 
     } 
     return valid; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

Он хорошо работает.

Надеюсь, это поможет.

+0

ой, извините, мой плохой. Это опечатка. Все мое свойство должно быть 'IsOk'. –

+0

Здесь хорошо работает. Просто TextChanged не отражается на свойстве text. Или вы можете опубликовать полный код. –

+0

В любом случае, я дам вам кредит на 'Mode'. :) –

1

Для завершения Davut Gurbuz ответ:

На Button у вас есть Command property, использовать это свойство лучше, чем i:Interaction.Triggers (easyer читать).

Как Давут Гюрбюз говорит RelayCommand имеет CanExecuteFunc parameter. Если вы используете это свойство, состояние вашей кнопки изменяется автоматически.

Xaml код:

<TextBox 
    Text="{Binding BoundProperty,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" 
    ...other propeties here.... /> 
<Button 
    x:Name="btnSave" 
    Command="{Binding OnSaveExecute}" 
    ...other propeties here.... /> 

C#

/// <summary> 
/// Gets the OnSaveExecute. 
/// </summary> 
public RelayCommand OnSaveExecute 
{ 
    get 
    { 
     return _onSaveExecute 
      ?? (_onSaveExecute = new RelayCommand(
            () => 
            { 
               // Save action or call save method 
            }, 
            () => IsOk)); 
    } 
} 
private RelayCommand _onSaveExecute; 

/// <summary> 
/// Sets and gets the IsOk property. 
/// Changes to that property's value raise the PropertyChanged event. 
/// </summary> 
public bool IsOk 
{ 
    get { return _isOk; } 
    set 
    { 
     if (_isOk == value) 
     { 
      return; 
     } 
     _isOk = value; 
     RaisePropertyChanged("IsOk"); 
     OnSaveExecute.RaiseCanExecuteChanged(); 
    } 
} 
private bool _isOk = false; 

/// <summary> 
/// Sets and gets the BoundProerty property. 
/// Changes to that property's value raise the PropertyChanged event. 
/// </summary> 
public string BoundProerty 
{ 
    get { return _boundProerty; } 
    set 
    { 
     if (_boundProerty == value) 
     { 
      return; 
     } 
     _boundProerty = value; 
     RaisePropertyChanged("BoundProerty"); 
     IsValid(); 
    } 
} 
private string _boundProerty = false; 

public myViewModel() 
{ 
} 

public bool IsValid() 
{ 
    // other codes 
    IsOk = MethodName(); 
} 
+0

Благодарим за обмен информацией о RelayCommand. Фактически мы можем удалить EventTriggers по мере того, как я поделился в конце моего сообщения «UpdateSourceTrigger = PropertyChanged», что отражает немедленные изменения в BoundProerty. Когда свойство изменяется, FirePropertyChanged выполняется. Поэтому нам действительно не нужен триггер событий TextChaged. –

+0

Хорошо, я просто редактирую сообщение, чтобы добавить 'BoundProerty' ... Thx! Не стесняйтесь редактировать ответ, если хотите добавить элементы;) – Tonio

Смежные вопросы