I have a registration form with few fields. It´s a PRISM MVVM application.
XAML of one of the field looks like this (RegisterView.xaml):
<TextBlock>Surname</TextBlock>
<TextBox Validation.ErrorTemplate="{StaticResource validationTemplate}" HorizontalAlignment="Left" Margin="0" Name="Surname" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="Surname" UpdateSourceTrigger="LostFocus" >
<Binding.ValidationRules>
<val:Required />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
As you can see from the code above, I use class Required to validate the field. Function Validate() of class Required then returns ValidationResult object. I also defined some triggers to styles for inputs, so I´m able to show validation result to the user in view.
What I don't know is how to detect the state of validation of all the inputs in ViewModel. In the ViewModel, I have the SaveUserCanExecute function which should enable/disable registration form submit button on the basic on a validation state of all the inputs.
So is there any simple way how to achieve this?
I could make some workaround for this, but I think is not the proper way.
Now I made a Submit_Click function in a View code behind fired on a Click event of a Submit Button.
In RegisterView.xaml
<Button Content="Register" HorizontalAlignment="Left" Margin="0" Name="Submit" VerticalAlignment="Top" Command="{x:Static inf:Commands.SaveUser}" Click="Submit_Click" />
I also created new public boolean variable "formIsValid" in code behind. When submit button is pressed, then I check whether all inputs has no validation error (with Validation.GetHasError(InputName) function). If so, I set the formIsValid variable to true, otherwise, I set it to false.
In RegisterView.xaml.cs
private void Submit_Click(object sender, RoutedEventArgs e)
{
if (Validation.GetHasError(Firstname) == false && Validation.GetHasError(Surname) == false)
{
registerFormValid = true;
}
else
{
registerFormValid = false;
}
}
Then in ViewModel SaveUserCanExecute function looks like this:
private bool SaveUserCanExecute(string parameter)
{
if (View.registerFormValid == true)
{
return true;
}
return false;
}
But as I mentioned before, I think it´s not the proper way, and I´m looking for some more clear way.
implement IDataErrorInfo in your ViewModel then you have all information you need in your VM. Your XAML just need the ValidatesOnDataErrors=true
<TextBlock>Surname</TextBlock>
<TextBox Validation.ErrorTemplate="{StaticResource validationTemplate}" HorizontalAlignment="Left" Margin="0" Name="Surname" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="Surname" UpdateSourceTrigger="LostFocus" ValidatesOnDataErrors="True">
</Binding>
</TextBox.Text>
</TextBox>
EDIT: check the use of DelegeCommand and then your command CanExecute can simply check then for string.IsNullOrEmpty(this.Error).
Bind a command (ICommand) to your Submit Button and implement this logic in Its CanExecute method. Here is a classic ICommand implementation.
Related
I have a TextBox with OneWay Mode, so validation does not happen automatically.
<TextBox.Text>
<Binding Path="SelectedValue.Customername"
ElementName="customerListBox"
Mode="OneWay"
>
<Binding.ValidationRules>
<validators:NameValidator ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
I have a button :
<Button Content="Save" Command="{Binding SaveCommand}"/>
Now on ViewModel , I want to validate the Text Input before doing anything else:
SaveCommand = new RelayCommand(
param=>
{
//If validation is true
//Then Execute Res
}
);
Ditch the UI validation rules and have your VM implement IDataErrorInfo and INotifyDataErrorInfo.
Think about it--your save command should not execute unless the data in your VM is valid. That means the validation logic should be in your VM, and not in your UI.
Implementing these interfaces makes it trivial to check whether or not you execute/can execute and fire appropriate events when CanExecute has changed.
I want to validate some Text in a TextBlock
TextBlock xaml:
<TextBlock x:Name="numInput" Validation.ErrorTemplate="{StaticResource errorTemplate}" >
<TextBlock.Text>
<Binding Path="Text" RelativeSource="{RelativeSource self}" NotifyOnValidationError="True">
<Binding.ValidationRules>
<local: NumberValidator />
</Binding.ValidationRules>
</Binding>
</TextBlock.Text>
</TextBlock>
The Text is added in codebehind by some button clicks in the GUI (i.e. a touch screen)
errorTemplate
<ControlTemplate x:Key="errorTemplate">
<StackPanel>
<TextBlock Foreground="Red">error msg</TextBlock>
<AdornedElementPlaceholder/>
</StackPanel>
</ControlTemplate>
NumberValidator
class NumberValidator : ValidationRule {
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) {
Console.WriteLine("validating numbers!!");
int num = -1;
try {
num = Int32.Parse(value.ToString());
}
catch (Exception e) {
return new ValidationResult(false, "input must be numbers!");
}
if (num > 999 || num < 1) {
return new ValidationResult(false, string.Format("must be integers from {0} to {1}", 1, 999));
}
return new ValidationResult(true, null);
}
}
Questions:
No error message is shown. In fact, NumberValidator isn't even called. Why?
How to validate the error only when a Button is clicked?
How to pass valid range (i.e min, max) information to the NumberValidator?
Thanks!
I assume that you want to perform validation in source-to-target direction (provide visual feedback for model errors), therefore my answer only applies if this is the case.
Validation rules are by design only checked in target-to-source direction (the main idea here is to validate user input), so when you change the value on the model, validation rules are not checked. In order to perform validation in source-to-target direction, your model should implement either IDataErrorInfo or INotifyDataErrorInfo (the latter being supported only in .NET 4.5 or newer), and ValidatesOnDataErrors should be set to true on the binding.
The validation occurs whenever binding is updated, so if the button click updates the property on the model (or, more specifically, raises PropertyChanged event), the validation will be performed. Note that if property is changed on some other occasion, the validation will also be performed, so in order to perform the validation only on button click make sure the property is changed (or PropertyChanged is raised) only then.
Despite using ValidationRule derivatives is not appropriate approach in assumed scenario, the answer is to define Max and Min properties on the NumberValidator class, and then use them in XAML like so: <local:NumberValidator Min="0" Max="100"/>.
For more information on bindings see Data Binding Overview.
I have the IsEnabled property of a XAML button configured with the following databinding currently:
<Button Name="ThirdPartyPostoneButton" Content="Postpone"
Click ="postponeThirdPartyUpdatesButton_Click" Margin="5,5,0,0"
Height="25" IsEnabled="{Binding Item3.CanDefer}"/>
I need to also add a check for IsEnabled="{Binding Item3.InstallSourceExists}" (in other words both criteria must be met in order for the button to be enabled). How can I accomplish this?
Two options I can think of:-
Use a MultiBinding plus a custom IMultiValueConverter that checks both values are true.
Expose a new property on your "Item3" model that simply returns true if the other properties are both True. This is a cleaner approach, and means that if the logic changes in the future (e.g. you need to include a third bool property), you don't have to touch your XAML.
Use a multibinding like this:
<Button Name="ThirdPartyPostoneButton" Content="Postpone" Click ="postponeThirdPartyUpdatesButton_Click" Margin="5,5,0,0" Height="25" >
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource MyCustomConvertor}">
<Binding Path="Item3.CanDefer"/>
<Binding Path="Item3.InstallSourceExists"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
How can I send multiple parameters from Button in WPF? I am able to send single parameter which is value of TextBox properly. Here is the code.
XAML
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="133,22,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
<Button Content="Button" Grid.Row="1" Height="23" Command="{Binding Path=CommandClick}" CommandParameter="{Binding Text,ElementName=textBox1}" HorizontalAlignment="Left" Margin="133,62,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
Code behind
public ICommand CommandClick { get; set; }
this.CommandClick = new DelegateCommand<object>(AddAccount);
private void AddAccount(object obj)
{
//custom logic
}
Other than using the approach of defining properties in you class (let's call it your ViewModel) to be binded by your view, there are times (not common) where we don't wan't to do so, an important tool to know in these situations is the MultiBinding, so just for completeness sake , even though you are satisfied with the first option, I'll cover another approach.
so to answer your question:
1. MVVM Approach :
Use the MVVM approach and define properties to binded by your view, and use those properties in your ViewModel's command without the need for CommandParameters.
2. MultiBinding : (Can live happily with MVVM approach)
Passing the Command Parameter as a Multi Binded parameter as seen here:
<Button Content="MultiBindingExample" Command="{Binding MyCommand}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource MyMultiConverter}">
<Binding Path="..." ElementName="MyTextBox"/>
<Binding Path="..." ElementName="MySomethingElse"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
With your Converter Defined using the IMultiValueConverter Interface:
public class MyMultiConverter: IMultiValueConverter
{
public object Convert(object[] values, ...)
{
return values.Clone();
}
}
and for extracting the values:
Simply refer to the parameter in your command as an Object[] and use the parameters in the same order as in the MultiBinding.
How can i send multiple parameters from button in wpf.
You can only send one parameter as the CommandParameter.
A better solution is typically to just bind the TextBox and other controls to multiple properties in your ViewModel. The command would then have access to all of those properties (since it's in the same class), with no need for a command parameter at all.
WPF MVVM in ViewModel I want to access the same data a textbox in the XAML is bound to
The XAML on MainWindow.xaml has a textbox bound to StoredProcs/ProcName
<TextBox Name="txtProcName" Text="{Binding Path=StoredProcs/ProcName}"></TextBox>
And a Grid bound to StoredProcs
Whenever the grid selection changes, the bound text in the textbox changes as it should.
<DataGrid AutoGenerateColumns="False"
Height="300" Width="290"
HorizontalAlignment="Center"
Name="dataGrid1"
VerticalAlignment="Top"
ItemsSource="{Binding StoredProcs}"
IsSynchronizedWithCurrentItem="True"
Margin="-6,0" Grid.RowSpan="2" Grid.Row="0">
<DataGrid.Columns>
<DataGridTextColumn Header="Proc Name" Binding="{Binding ProcName}" >
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
I have a button that executes a procedure in MainWindowViewModel when clicked, that works
<Button Content="Create RDL" Command="{Binding CreateStoredProcedure}" />
In the CreateStoredProcedure code, I need to access the same data that is displayed in the textbox (not using code behind). I would think I need to get the StoredProcs/ProcName but can't figure out how to do that.
I tried adding CommandParameter to the XAML but don't know how to access it in the CreateStoredProcedure instructions as it won't allow me to add paramaters
void CreateStoredProcedureExecute()
{
string procName = "proc";
//procName = { StoredProcs/ProcName };
MessageBoxResult result =
MessageBox.Show(String.Format("Create Stored Procedure {0}", procName));
}
bool CanCreateStoredProcedure()
{
return true;
}
public ICommand CreateStoredProcedure
{
get
{
return new RelayCommand(CreateStoredProcedureExecute,
CanCreateStoredProcedure);
}
}
Unless I am misunderstanding your question, you should be able to get the value of the property that the TextBox is bound to from within CreateStoredProcedure.
One thing though, if you want the TextBox to update the property, make sure you add "Mode=TwoWay" to your binding expression:
<TextBox Name="txtProcName" Text="{Binding Path=StoredProcs/ProcName, Mode=TwoWay}"></TextBox>
Unless I misunderstood I think you want something like this?
<Button
Content="Create RDL"
Command="{Binding CreateStoredProcedure}"
CommandParameter="{Binding ElementName=txtProcName, Path=Text}"/>
However as the other answer states, you should be able to just access the property in the ViewModel that is backing the textbox from the command, but if for some reason you cannot my code should work as well.
(Assuming you are defining RelayCommand as defined by this MSDN article, this should fix your other problem)
public ICommand CreateStoredProcedure
{
get
{
return new RelayCommand<object>(
(object parameter) => CreateStoredProcedureExecute(parameter),
(object parameter) => CanCreateStoredProcedure);
}
}
private void CreateStoredProcedureExecute(object parameter)
{
string ProcName = parameter as string;
}
I will admit my somewhat inexperience with setting up commands like this, but I did find a working example in my code that followed this, so hopefully it helps.
I think KDiTraglia has the right solution. The only thing I would do differently is to bind the CommandParameter to the model, not the UI element.
<Button
Content="Create RDL"
Command="{Binding CreateStoredProcedure}"
CommandParameter="{Binding Path=StoredProcs/ProcName}" />
I'm assuming that StoredProcs/ProcName is a placeholder for a real, valid binding path.
Root around here for more information: http://msdn.microsoft.com/en-us/library/ms752308