CommandBinding question. How to enable command button - wpf

My code is here>>
public class Player:INotifyPropertyChanging
{
string addressBar;
public string Url
{
get {
return addressBar;
}
set { addressBar = value; OnPropertyChanged("Url"); }
}
public Regex regVillage = new Regex(#"\?doc=\d+&sys=[a-zA-Z0-9]{2}");
RelayCommand _AddAttackTask;
public ICommand AddAttackTask
{
get {
if (_AddAttackTask == null)
{
_AddAttackTask = new RelayCommand(param =>
{
}, param => this.CanAttack);
}
return _AddAttackTask;
}
}
public Boolean CanAttack
{
get{
if (Url == null) return false;
return regVillage.IsMatch(Url);
}
}
}
On the xaml, i have textbox and button. Textbox binded by url, button binded by AddAttackTask. When i change textbox value,Url changed.Main target is When changing url, button bring to enable or disable. But button always disabled.
I'm getting RelayCommand class from WPF Apps With The Model-View-ViewModel Design Pattern
What is wrong on my code?
Please fix my command binding!

I found it yourself.
Must call CommandManager.InvalidateRequerySuggested(); function after changing property
public string Url
{
get {
return addressBar;
}
set { addressBar = value; OnPropertyChanged("Url");
CommandManager.InvalidateRequerySuggested();
}
}

Related

Why is my Login button always disabled?

I have a popup view with x:Name=This, on it a button delcared as follows:
<Button Content="Log in" Command="{Binding Path=LoginCommand}" CommandParameter="{Binding ElementName=This}" />
This is to gain access to the non-bindable Password property, which is a SecureString type.
In my ctor I initialise the command like so:
public LoginPopupViewModel()
{
LoginCommand = new DelegateCommand<IHavePassword>(
LogUserIn,
p => !string.IsNullOrWhiteSpace(Username));
}
I fully expect that when I type something in the Username, and change focus, the property change notification will help enable the Login button. It doesn't, so I have added the extra code, and the button still remains disabled.
public string Username
{
get { return _username; }
set
{
if (value == _username) return;
_username = value;
OnPropertyChanged();
CommandManager.InvalidateRequerySuggested();
}
}
If I change the CanExecute delegate like below, only then is the button enabled:
public LoginPopupViewModel()
{
LoginCommand = new DelegateCommand<IHavePassword>(
LogUserIn,
p => true);
}
Why does this button remain disabled even when its command can execute?
I have tried a sample program and binding seems to work fine. I don't have your complete source code but you need to use RaiseCanExecuteChanged on the delegate command when you want the command to check if it needs to execute. Have you checked if the binding on the username is correct?
this.loginCommand.RaiseCanExecuteChanged(); is the key to the answer
public LoginPopupViewModel()
{
this.loginCommand = new DelegateCommand(() =>
{
MessageBox.Show("Logged In Click");
}, () =>
{
return !string.IsNullOrEmpty(UserName);
});
}
private DelegateCommand loginCommand;
private string userName;
public ICommand LoginCommand
{
get { return loginCommand; }
}
public string UserName
{
get { return this.userName; }
set
{
if (value == this.userName)
{
return;
}
this.userName = value;
OnPropertyChanged("UserName");
this.loginCommand.RaiseCanExecuteChanged();
}
}
public string Password { get; set; }

Exposing custom properties using UI Automation Framework

Given a very basic WinForms custom/user control, using System.Windows.Automation it is possible to manipulate built in properties for the custom control.
This is done like this:
public object GetPropertyValue(int propertyId)
{
if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
{
return "Hello World!";
}
}
What I would like to do is expose custom properties to ui automation such as ReadyState, LastAccessed, Etc.
Is this possible?
No, you can't extend the list of properties, and this is complicated by the fact you use Winforms that has a poor UI Automation support (it uses IAccessible with bridges etc.).
What you can do though is add some fake objects to the automation tree, for example, here is a sample Winforms UserControl that does it:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
Button button = new Button();
button.Location = new Point(32, 28);
button.Size = new Size(75, 23);
button.Text = "MyButton";
Controls.Add(button);
Label label = new Label();
label.Location = new Point(49, 80);
label.Size = new Size(35, 13);
label.Text = "MyLabel";
Controls.Add(label);
MyCustomProp = "MyCustomValue";
}
public string MyCustomProp { get; set; }
protected override AccessibleObject CreateAccessibilityInstance()
{
return new UserControl1AccessibleObject(this);
}
protected class UserControl1AccessibleObject : ControlAccessibleObject
{
public UserControl1AccessibleObject(UserControl1 ownerControl)
: base(ownerControl)
{
}
public new UserControl1 Owner
{
get
{
return (UserControl1)base.Owner;
}
}
public override int GetChildCount()
{
return 1;
}
public override AccessibleObject GetChild(int index)
{
if (index == 0)
return new ValueAccessibleObject("MyCustomProp", Owner.MyCustomProp);
return base.GetChild(index);
}
}
}
public class ValueAccessibleObject : AccessibleObject
{
private string _name;
private string _value;
public ValueAccessibleObject(string name, string value)
{
_name = name;
_value = value;
}
public override AccessibleRole Role
{
get
{
return AccessibleRole.Text; // activate Value pattern
}
}
// note you need to override with member values, base value cannot always store something
public override string Value { get { return _value; } set { _value = value; } }
public override string Name { get { return _name; } }
}
And this is how it appears in the automation tree (using the inspect.exe tool):
Note this technique also supports writing back to the property because it's based on the ValuePattern.

Data validation on ShowDialog window in WPF

I have a window that I display as ShowDialog
in the window I have some textboxes binding to object that implement INotifyPropertyChannges and IDataErrorInfo.
I want that the OK button will enabled just if all thextboxes validted
and I want that just if the user click on OK buton the next move will occur.
I can bind the button to ICommand and check the textboxes valitation in CanExcute() but then what can I do in the Excute? the object dont know about the window.
I can also check the textboxes valitation and then raise event that all valid and enable the OK button but then there will be dupliacte code because I checked already in the IDataErrorInfo implmention.
So what is the right way?
Thanks in advance
You CanExecute should look like this.
public bool CanExecuteOK
{
get
{
if (DataModelToValidate.Error == null && DataModelToValidate.Errors.Count == 0) return true;
else return false;
}
}
Here Error and Errors properties are nothing but Wrapper over this[string propertyName] (implemented implicitly for IDataErrorInfo).
Here is Sample Model Class:
public class SampleModel: IDataErrorInfo, INotifyPropertyChanged
{
public SampleModel()
{
this.Errors = new System.Collections.ObjectModel.ObservableCollection<string>();
}
private string _SomeProperty = string.Empty;
public string SomeProperty
{
get
{
return _SomeProperty;
}
set
{
if (value != _SomeProperty)
{
_SomeProperty= value;
RaisePropertyChanged("SomeProperty");
}
}
}
....
....
//this keeps track of all errors in current data model object
public System.Collections.ObjectModel.ObservableCollection<string> Errors { get; private set; }
//Implicit for IDataErrorInfo
public string Error
{
get
{
return this[string.Empty];
}
}
public string this[string propertyName]
{
get
{
string result = string.Empty;
propertyName = propertyName ?? string.Empty;
if (propertyName == string.Empty || propertyName == "SomeProperty")
{
if (string.IsNullOrEmpty(this.SomeProperty))
{
result = "SomeProperty cannot be blank";
if (!this.Errors.Contains(result)) this.Errors.Add(result);
}
else
{
if (this.Errors.Contains("SomeProperty cannot be blank")) this.Errors.Remove("SomeProperty cannot be blank");
}
}
......
return result;
}

MVVM property with a linked complex class and CanExecute Relay Command not working

I have a entity class in a C# library class and linked to Silverlight class library
(entities must be in C# class because of legacy compatiblity with other systems)
Example (C# library):
public class TestClass
{
private string _testValue;
public string TestValue
{
get { return _testValue; }
set
{
if (_testValue!= value)
{
_testValue = value;
OnPropertyChanged("TestValue");
}
}
}}
This class is linked to Silverlight class library.
On a MVVM there is a property
private TestClass _testProp = new TestClass();
public TestClass TestProp
{
get
{
return _testProp ;
}
set
{
if (value != _testProp )
{
_testProp = value;
RaisePropertyChanged("TestProp");
PressCommand.CanExecuteChanged();
}
}
}
The property is binded to a control in XAML
<TextBox Text="{Binding TestProp.TestValue, Mode=TwoWay}">
<Button Content="Press" Command="{Binding PressCommand}" />
I want to control the button with RelayCommands CanExecute depended on the TestValue in TestClass...
PressCommand = new RelayCommand(() =>
{
DoSomething();
}, () => TestProp.TestValue != string.empty);
However, if the TestValue in changed (different then empty string), PressCommand CanExecute doen't seem to notice the change and is not enabled, making it unusable...
Is it possible to use the CanExecute with this kind of set-tu
What you need to do is call PressCommand.CanExecuteChanged() when the value changes. To do this nicely listen for the property change of the value in the property
VM
public TestClass TestProp
{
get
{
return _testProp ;
}
set
{
if (value != _testProp )
{
if(_testProp != null)
{
_testProp.PropertyChanged -= TestPropChanged;
}
_testProp = value;
if(_testProp != null)
{
_testProp.PropertyChanged += TestPropChanged;
}
RaisePropertyChanged("TestProp");
PressCommand.CanExecuteChanged();
}
}
}
private void TestPropChanged(object sender, PropertyChangedEventArgs e)
{
//Can check for specific property if required...
PressCommand.CanExecuteChanged();
}

Validation exception fails if field is empty Silverlight

I m trying to do a simple validation on textbox, its a required field and cannot be empty. Initially value will be empty so when user do not enter any value into the field and directly clicks Save button then valitaion is not triggered. It works fine when user types something and then deletes the value from it then it works perfectly and shows the validation error message. Is there anyway to do validation check after user clicks save button.
[Display(Name = "Sometext", Description = "Some text")]
[Required(ErrorMessage = "Required Field")]
public string SomeText
{
get
{
return _someText;
}
set
{
if (_someText== value &&
value != string.Empty)
{
return;
}
Validate(value, "someText");//This calls Validator.ValidateProperty method
_someText= value;
FirePropertyChanged("someText");
}
}
Please suggest!
Thanks in advance
Sai
You could also call on your command execution
Validator.ValidateObject(this, new ValidationContext(this,null,null),true);
This should validate all properties on your viewmodel, assuming you call this from your viewmodel
edit: Response to comment
You could have a property like so (below) in your BaseViewModel (every viewmodel extends BaseViewModel), and then disallow save with a proper message
protected bool HasValidationErrors
{
get
{
try {
Validator.ValidateObject(this, new ValidationContext(this, null, null), true);
return false;
}
catch (ValidationException) { return true; }
}
}
In your command you would call it like so
public void SaveCommandExecuted(object parameter)
{
if (HasValidationErrors)
{
ShowValidationError();
}
}
Another thing, you could try is to bind the View event for validation errors to a listener in you viewmodel
MyProgram.ViewModels.BaseViewModel baseViewModel = page.Resources["DataSource"] as MyProgram.ViewModels.BaseViewModel;
page.BindingValidationError += new EventHandler<ValidationErrorEventArgs>(baseModel.OnValidationError);
then in your BaseViewModel
private ObservableCollection<ValidationError> Errors { get; set; }
public void OnValidationError(object sender, ValidationErrorEventArgs e)
{
switch (e.Action)
{
case ValidationErrorEventAction.Added:
Errors.Add(e.Error);
break;
case ValidationErrorEventAction.Removed:
Errors.Remove(e.Error);
break;
default:
break;
}
}
then modify HasValidationErrors to
protected bool HasValidationErrors
{
get
{
try {
Validator.ValidateObject(this, new ValidationContext(this, null, null), true);
return this.Errors.Count != 0;
}
catch (ValidationException) { return true; }
}
}
Josh Twist has given a work around for this. This works perfectly..
http://www.thejoyofcode.com/Silverlight_Validation_and_MVVM_Part_II.aspx

Resources