Manually Triggering Validation in WPF while using Caliburn Micro - wpf

I have a view model that implements IDataErrorInfo (and derived from Caliburn Micro's Screen class) like this
public class MyViewModel : Screen, IDataErrorInfo
{
...
public BindableCollection<MyEntity> Entities { get; set; }
public MyEntity SelectedEntity
{
get { return _entity; }
set
{
_entity = value;
OnSelectedEntityChanged();
}
}
private void OnSelectedEntityChanged()
{
// implementation
}
public virtual string Error
{
get { // implementation }
}
public virtual string this[string columnName]
{
get { // implementation }
}
public void Populating(PopulatingEventArgs e)
{
// implementation
}
}
It is bound to the following XAML using Caliburn Micro (only relevant portion shown)
<tk:AutoCompleteBox
x:Name="Entities"
cal:Message.Attach="[Event Populating] = [Populating($eventArgs)]"
SelectedItem="{Binding Path=SelectedEntity, Mode=TwoWay}"
HorizontalAlignment="Stretch"
FilterMode="None"
IsTextCompletionEnabled="True"
Text="{Binding SearchText}"
ValueMemberPath="Name">
<tk:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</tk:AutoCompleteBox.ItemTemplate>
</tk:AutoCompleteBox>
The problem I am running into is that when I update the SelectedEntity property programmatically, it does not cause validation to be triggered. I've tried out many different possible solution such as trying to get the binding expression and calling ValidateWithoutUpdate() on it, adding triggers in the XAML that should cause validation to be triggered etc, but none has worked so far.
How can I trigger validation that would eventually call IDataErrorInfo.Error?
Thanks!

/*
Place the IDataErrorInfo implementation in your Model Class ..
not your view model.
The data error is in the Model not in the ViewModel.
The ViewModel references the model,
IDataErrorInfo communicates with the binding via the
xaml ValidatesOnDataErrors=True in the binding
*/
/* model */
public class OldBoy : Screen, IOldBoy, IDataErrorInfo
{
private Guid oldBoyId;
public Guid OldBoyId
{
get
{
return this.oldBoyId;
}
set
{
this.oldBoyId = value;
NotifyOfPropertyChange(() => OldBoyId);
}
}
private string firstName;
public string Firstname
{
get
{
return this.firstName;
}
set
{
this.firstName = value;
NotifyOfPropertyChange(() => Firstname);
}
}
private string surName;
public string Surname
{
get
{
return this.surName;
}
set
{
this.surName = value;
NotifyOfPropertyChange(() => Surname);
}
}
/* include a Property e.g. CanSave in your Model that will be bound
to the IsEnabled DependencyProperty ofIsEnabled on your Submit Button
Viz.
*/
public string this[string name]
{
get
{
string result = null;
if (name == "Firstname")
{
// force the issue
this.CanSave = true;
if (string.IsNullOrEmpty(this.Firstname))
{
result = "Model says that Firstname must be entered";
}
}
if (name == "Surname")
{
// force the issue
this.CanSave = true;
if (string.IsNullOrEmpty(this.Surname))
{
result = "Model says that Surname must be entered";
}
}
return result;
}
}
public string Error
{
get
{
return null;
}
}
private bool canSave;
public bool CanSave
{
get
{
return this.canSave;
}
set
{
if (string.IsNullOrEmpty(this.Firstname) || string.IsNullOrEmpty(this.surName))
{
this.canSave = false;
}
else
{
this.canSave = true;
}
NotifyOfPropertyChange(() => CanSave);
}
}
}
/* in the ViewModel the underlying class for the edit window */
public class DetailsViewModel : Screen, IDetailsViewModel
{
private IOldBoy editingOldBoyItem;
public IOldBoy EditingOldBoyItem
{
get
{
return this.editingOldBoyItem;
}
set
{
this.editingOldBoyItem = value;
NotifyOfPropertyChange(() => EditingOldBoyItem);
}
}
// elided
}
xaml textboxes involved in validation
<Label Grid.Row="00"
Grid.Column="00"
Content="First Name" />
<TextBox Text="{Binding Path=EditingOldBoyItem.Firstname, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource TextBoxCellValidationStyle}"
Grid.Row="00"
Grid.Column="01">
<i:Interaction.Behaviors>
<local:BindableFocusBehavior HasFocus="{Binding SetFocusToFirstName, Mode=TwoWay, UpdateSourceTrigger=Default}" />
</i:Interaction.Behaviors>
</TextBox>
<Label Grid.Row="01"
Grid.Column="00"
Content="Surname" />
<TextBox Text="{Binding Path=EditingOldBoyItem.Surname, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
Grid.Row="01"
Grid.Column="01">
<!-- save button that is disabled when CanSave is false -->
<Button Content="Save Edit"
IsEnabled="{Binding Path=EditingOldBoyItem.CanSave}"
x:Name="Save"
Margin="4"
Template="{StaticResource RedGlassButton}" />
app.xaml
<ControlTemplate x:Key="ValidationTemplate">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Bottom"
FontSize="12"
FontWeight="Bold"
Foreground="Red">
<TextBlock.ToolTip>
<ToolTip Placement="Center"
Style="{StaticResource ErrorToolTip}">
<TextBlock Foreground="White">
<TextBlock.Text>
<Binding Path="/ErrorContent" />
</TextBlock.Text>
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
<Border BorderBrush="Red"
BorderThickness="1">
<AdornedElementPlaceholder Name="adornerPlaceholder" />
</Border>
</DockPanel>
</ControlTemplate>
<Style x:Key="TextBoxCellValidationStyle"
BasedOn="{StaticResource {x:Type TextBox}}"
TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Background" Value="LightCyan" />
<Setter Property="Foreground" Value="Red" />
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}" />
</Trigger>
</Style.Triggers>
</Style>

Related

Binding Dependency Property of UserControl to MainWindow ViewModel in WPF

Allow me to simplify the problem to its basic blocks.
I have a UserControl1 in my project. It has a TextBox like this:
<TextBox Text="{Binding TextProperty}"/>
In the code-behind, I have a dependency property like this:
public string TextProperty
{
get { return (string)GetValue(TextPropertyProperty); }
set { SetValue(TextPropertyProperty, value); }
}
public static readonly DependencyProperty TextPropertyProperty = DependencyProperty.Register("TextProperty", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
And the constructor of the UserControl is simply
public UserControl1()
{
InitializeComponent();
DataContext = this;
}
In the MainWindow, I have this:
<userctrl:UserControl1 TextProperty="{Binding ABC, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Grid.Row="1" Text="{Binding PQR, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
Now, in the viewModel of the MainWindow, I have:
private string _abc;
public string ABC
{
get { return _abc; }
set
{ _abc = "20";
OnPropertyChanged();
}
}
private string _pqr;
public string PQR
{
get { return _pqr; }
set
{
if(value != _pqr)
{
_pqr = value;
ABC = PQR;
OnPropertyChanged();
}
}
}
Now, even the UserControl is supposed to replicate whatever I write in the textbox, right?
But it doesn't. I have set breakpoints at setter of ABC. It gets updated, but that update does not reach the UserControl. My gut says that the binding is failing silently, and it's because I have set the DataContext to this in the constructor of UserControl.
How am I supposed to solve it?
The Actual Scenario:
Here is the XAML for the UserControl:
<UserControl x:Class="MyDiskTools.UserControls.NodeGrid.NodeGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyDiskTools.UserControls.NodeGrid"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Padding" Value="5"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Command" Value="{Binding InputCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderThickness" Value="5"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="Times New Roman"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<UniformGrid Grid.Row="0" Rows="1">
<Button Content="A" />
<Button Content="B" />
<Button Content="C" />
<Button Content="D" />
<Button Content="E" />
<Button Content="F" />
</UniformGrid>
<UniformGrid Grid.Row="1" Rows="1">
<Button Content="G" />
<Button Content="H" />
<Button Content="I" />
<Button Content="J" />
<Button Content="K" />
<Button Content="L" />
<Button Content="M" />
</UniformGrid>
<UniformGrid Grid.Row="2" Rows="1">
<Button Content="N" />
<Button Content="O" />
<Button Content="P" />
<Button Content="Q" />
<Button Content="R" />
<Button Content="S" />
<Button Content="T" />
</UniformGrid>
<UniformGrid Grid.Row="3" Rows="1">
<Button Content="U" />
<Button Content="V" />
<Button Content="W" />
<Button Content="X" />
<Button Content="Y" />
<Button Content="Z" />
</UniformGrid>
<TextBox Name="InputMessage" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsEnabled="False" Background="Beige" Grid.Row="4" Text="{Binding PasswordDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
Here is the code-behind:
public partial class NodeGrid : UserControl
{
public NodeGrid()
{
InitializeComponent();
InputCommand = new InputCharacterCommand(this);
DataContext = this;
}
public string PasswordDisplay
{
get { return (string)GetValue(PasswordDisplayProperty); }
set { SetValue(PasswordDisplayProperty, value); }
}
public static readonly DependencyProperty PasswordDisplayProperty =
DependencyProperty.Register("PasswordDisplay", typeof(string), typeof(NodeGrid), new PropertyMetadata(""));
private ICommand _inputCommand;
public ICommand InputCommand
{
get
{
return _inputCommand;
}
set
{
_inputCommand = value;
}
}
public void AddCharacter(string input)
{
if (input != null)
{
PasswordDisplay = string.Concat(PasswordDisplay, input);
}
}
public bool InputAllowed()
{
if (PasswordDisplay == null)
{
return true;
}
if (PasswordDisplay.Length < 50)
{
return true;
}
return false;
}
private void OnPropertyChange([CallerMemberName] string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
public event PropertyChangedEventHandler PropertyChanged;
}
class InputCharacterCommand : ICommand
{
private NodeGrid _vmodel;
public InputCharacterCommand(NodeGrid vm)
{
_vmodel = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return (_vmodel.InputAllowed());
}
public void Execute(object parameter)
{
_vmodel.AddCharacter(parameter as string);
}
}
And here is how I use it in my MainWindow:
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<WrapPanel HorizontalAlignment="Center">
<Label Content="Enter PIN"/>
<TextBox CommandManager.PreviewCanExecute="HandleCanExecute" Foreground="Transparent" MinWidth="100" Padding="10,0" Text="{Binding PIN, UpdateSourceTrigger=PropertyChanged}"/>
</WrapPanel>
<customlock:NodeGrid MinHeight="250" MinWidth="500" PasswordDisplay="{Binding NodeGridDisplay}"/>
<Button VerticalAlignment="Center" HorizontalAlignment="Center" Content="Unlock!"/>
</StackPanel>
The Code-behind:
private void HandleCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (e.Command == ApplicationCommands.Cut ||
e.Command == ApplicationCommands.Copy ||
e.Command == ApplicationCommands.Paste)
{
e.CanExecute = false;
e.Handled = true;
}
}
And now, the ViewModel:
private string _PIN;
public string PIN
{
get
{
return _PIN;
}
set
{
if(value != _PIN)
{
_PIN = value;
OnPropertyChanged();
NodeGridDisplay = HashIt(_PIN);
}
}
}
private string _nodeGridDisplay;
public string NodeGridDisplay
{
get
{
return _nodeGridDisplay;
}
set
{
if (value != _nodeGridDisplay)
{
_nodeGridDisplay = value;
OnPropertyChanged();
}
}
}
private string HashIt(string input)
{
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
{
return System.Text.Encoding.Default.GetString(md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(input))).GetHashCode().ToString();
}
}
What is the intended function?
Let me illustrate it.
The Lock
One can enter the PIN, the hash of which will be displayed in the NodeGrid. Then the user will click the letters which will be concatenated with the hash. Then the user can click unlock.
You should use ElementName in your UserControl or use RaltiveResource to find it, and you should not use DataContext = this. (see this answer and this link)
Give your userControl a name and write:
<UserControl x:Class= ......
Name="userControl">
<TextBox Text="{Binding Text, ElementName = userControl}"/>
</UserControl>
Please note that you should use Text instead of TextProperty and make sure to change _abc = "20" to _abc = value.

Checkbox is not checked in DevExpress GridControl using wpf

I am trying to work on DevExpress GridControl Checkbox column but problem is that when I binding the checkbox value in XAML code dynamically its not work perfectlly
below I provide you my demo project code :-
XAML Code:-
<dxg:GridControl AutoPopulateColumns="True" HorizontalAlignment="Left" Margin="0,40,0,0" Name="gridControl1" VerticalAlignment="Top" Height="318" Width="503">
<dxg:GridControl.View>
<dxg:TableView Name="tableView1" ShowTotalSummary="True" />
</dxg:GridControl.View>
<dxg:GridControl.Columns>
<dxg:GridColumn DisplayMemberBinding="{Binding Path=EvenOdd}" Header="Even/Odd" />
<dxg:GridColumn Name="PickColumn" Header="Pick" Width="30"
AllowColumnFiltering="False" AllowSorting="False">
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsValid}"
HorizontalAlignment="Center" VerticalAlignment="Center" >
</CheckBox>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
</dxg:GridColumn>
</dxg:GridControl.Columns>
</dxg:GridControl>
my cs file code:-
public class ss
{
public bool IsValid { get; set; }
public string EvenOdd { get; set; }
}
Code Behind:
public List<ss> sList = new List<ss>();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0)
{
sList.Add(new ss { IsValid = true, EvenOdd = "Even" });
}
else
{
sList.Add(new ss { IsValid = false, EvenOdd = "Odd" });
}
}
gridControl1.ItemsSource = sList;
}
Adding on to HighCore's answer. If you would like to edit the data in your grid.
See ColumnBase.CellTemplate Property:
To enable data editing, use an editor shipped with the DevExpress Data Editors Library for WPF. The editor's Name must be set to
'PART_Editor'.
Standard controls can be used in CellTemplate only for display purposes. Data editing is not allowed.
Then,
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
<dxg:GridColumn Name="PickColumn"
Header="Pick"
Width="30"
AllowColumnFiltering="False"
AllowSorting="False">
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<dxe:CheckEdit x:Name="PART_Editor"
EditValue="{Binding Path=Data.IsValid, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
</dxg:GridColumn>
You still need to use HighCore's implementation of INotifyPropertyChanged.
First of all you need to correct the binding inside the CellTemplate:
<CheckBox IsChecked="{Binding Path=RowData.Row.IsValid}"/>
Second, your data items should implement INotifyPropertyChanged:
public class ss:INotifyPropertyChanged
{
private bool _isValid;
private string _evenOdd;
public bool IsValid
{
get { return _isValid; }
set
{
_isValid = value;
OnPropertyChanged("IsValid");
}
}
public string EvenOdd
{
get { return _evenOdd; }
set
{
_evenOdd = value;
OnPropertyChanged("EvenOdd");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
xmlns:dxgcore="http://schemas.devexpress.com/winfx/2008/xaml/grid"
<dxgcore:GridColumn Width="20"
AllowEditing="True"
Binding="{Binding Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Header="R"
Visible="{Binding CheckboxSelection}"
VisibleIndex="6">
<dxgcore:GridColumn.CellTemplate>
<DataTemplate>
<dxe:CheckEdit HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding
Path=View.DataContext.IsCheckedCommand}"
CommandParameter="{Binding RowData.Row}"
IsChecked="{Binding RowData.Row.IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=View.DataContext.IsCheckBoxEnabled, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</dxgcore:GridColumn.CellTemplate>
</dxgcore:GridColumn>
And Notifychanged property
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
RaisePropertyChanged("IsChecked");
}
}

Databinding to Command in Silverlight Templated Button control

I am trying to create a templated button control with databinding for the Visibility, tooltip, and Command. The Visibility binding works, as does the tooltip, but the Command does not. Another process is responsible for injecting the viewmodel and associating it with the View, and the other data bindings are working so I am pretty confident that is working properly.
In the resource dictionary:
<Converters:BoolToVisibilityConverter x:Key="boolVisibilityConverter" />
<Style TargetType="local:ImageButton">
<Setter Property="Visibility" Value="{Binding FallbackValue=Visible, Path=ToolIsAvailable, Converter={StaticResource boolVisibilityConverter} }"/>
<Setter Property="Command" Value="{Binding ButtonCommand}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageButton">
<Grid>
<Image Source="{TemplateBinding Image}"
ToolTipService.ToolTip="{Binding ToolName}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
the templated control
public class MyButton: ImageButton
{
public MyButton(MyCommandViewModel viewmodel)
{
this.DefaultStyleKey = typeof(ImageButton);
this.Image = new BitmapImage(new Uri("/MyProject;component/Themes/myimage.png", UriKind.Relative));
this.DataContext = viewmodel;
}
}
and in the view model
public MyCommandViewModel()
: base("My Tool", true)
{
}
public class CommandViewModel
{
public CommandViewModel(string toolName, bool isAvailable)
{
ToolIsAvailable = isAvailable;
ToolName = toolName;
_buttoncommand = new DelegateCommand(() =>
{
ExecuteCommand();
},
() => { return CanExecute; });
}
private bool _canExecute = true;
public bool CanExecute
{
get { return _canExecute; }
set
{
_canExecute = value;
OnPropertyChanged("CanExecute");
if (_command != null) _command.RaiseCanExecuteChanged();
}
}
private DelegateCommand _buttoncommand;
public ICommand ButtonCommand
{
get { return _buttoncommand; }
}
protected virtual void ExecuteCommand()
{
}
public bool ToolIsAvailable
{
get { return _toolIsReady; }
set { _toolIsReady = value; OnPropertyChanged("ToolIsAvailable"); }
}
public string ToolName
{
get { return _toolName; }
set { _toolName = value; OnPropertyChanged("ToolName"); }
}
}
Why are the other databindings functioning properly but not the Command data binding. I found this similar post
Overriding a templated Button's Command in WPF
Do I need to template a grid control instead and use RoutedCommands? I am not sure I understand why Silverlight treats the Command binding different than the others so I suspect I just have a bug in the code.
Does specifically looking for the datacontext work?
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ButtonCommand}"
This was my solution. Using the same commandviewmodel as above and same MyCommandViewModel
<Style TargetType="local:ImageButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageButton">
<Grid>
<Image Source="{TemplateBinding Image}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The databinding is now done in a user control
<UserControl x:Class="SilverlightApplication11.Test"
...
>
<UserControl.Resources>
<Converters:BoolToVisibilityConverter x:Key="boolVisibilityConverter" />
</UserControl.Resources>
<Grid>
<local:ImageButton Image="/SilverlightApplication11;component/Themes/hand.png" Command="{Binding ButtonCommand}" Visibility="{Binding FallbackValue=Visible, Path=ToolIsAvailable, Converter={StaticResource boolVisibilityConverter} }"/>
</Grid>
</UserControl>
and the code behind
public Test(TestCommandViewModel vm)
{
InitializeComponent();
this.Loaded += (o, e) => this.DataContext = vm;
}

WPF Validation Control

I am new to WPF and trying to implement validation control on submit form.
Can anyone help me. My code doen't show up any error message even if I enter invalid data infect it does nothing.
Here is my code,
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class UserName : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
private string username;
public string _UserName
{
get { return username; }
set
{
username = value;
OnPropertyChanged(new PropertyChangedEventArgs("_UserName"));
}
}
public string this[string propertyName]
{
get
{
if (propertyName == "_UserName")
{
bool valid = true;
foreach (char c in _UserName)
{
if (!Char.IsLetterOrDigit(c))
{
valid = false;
break;
}
}
if (!valid)
return "The Username can only contain letters and numbers.";
}
return null;
}
}
public string Error
{
get { return null; }
}
}
}
My XAML code is,
<Grid>
<Label Content="User Name" Height="28" HorizontalAlignment="Left" Margin="27,37,0,0" Name="UserNameLB" VerticalAlignment="Top" Width="96" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="135,37,0,0" Name="UserNameTB" VerticalAlignment="Top" Width="189">
<TextBox.Text>
<Binding Path="_UserName">
<Binding.ValidationRules>
<DataErrorValidationRule></DataErrorValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
All you need is to put all logic about your validation in a separate class that inherits from class ValidationRule and override method Validate. Then you can set the message you want to see about the reason of validation failure, like:
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
try
{
if (!Char.IsLetterOrDigit(c))
{
return new ValidationResult(false,
"The Username can only contain letters and numbers.");
}
else
{
return new ValidationResult(true, null);
}
}
catch (Exception e)
{
return new ValidationResult(false, "Illegal characters or " + e.Message);
}
}
Try this:
EDIT: (here's a style I define which shows errors for all TextBox controls)
(put it in Window.Resources)
This style will then show the error message in a ToolTip
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<Grid>
<Label Content="User Name" Height="28" HorizontalAlignment="Left" Margin="27,37,0,0" Name="UserNameLB" VerticalAlignment="Top" Width="96" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="135,37,0,0"
Name="UserNameTB" VerticalAlignment="Top" Width="189"
Text={Binding Path=_UserName, UpdateSourceTrigger=LostFocus,
ValidatesOnDataErrors=true, NotifyOnValidationError=true} />
</Grid>
Source

Changing the User Control of Window dynamically in WPF (MVVM)

I am working on wpf mvvm pattern.
I have a user control in which i am loading a list of checkboxes in DataGridCheckBoxColumn and binding it to IsSelected property from my viewmodel.
Tha xaml code is like this:
<DataGrid Width="150" Grid.Row="0" Background="LightGray" CanUserAddRows="False" AutoGenerateColumns="False" HorizontalAlignment="Left" Name="dataGridCustomers" ItemsSource="{Binding Path=UsecaseListItems}" CanUserResizeColumns="False" CanUserResizeRows="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding Path=IsSelected,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay,IsAsync=True}" Width="50">
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate x:Name="dtAllChkBx">
<CheckBox Name="cbxAll" FontWeight="Bold" Content="All" IsChecked="{Binding Path=DataContext.AllSelected,RelativeSource={RelativeSource AncestorType=UserControl },Mode=TwoWay}"/>
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
</DataGridCheckBoxColumn>
<DataGridTextColumn Width="85" Binding="{Binding Path=UsecaseName}" Header="UsecaseName" IsReadOnly="True" >
<DataGridColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Black"></Setter>
</Style>
</DataGridColumn.HeaderStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
My HomeViewModel is like this:
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
private Control _someUserControl;
public Control CCS01
{
get { return _someUserControl; }
set { _someUserControl = value; }
}
private UserControl _content;
internal void SetNewContent(UserControl _content)
{
ContentWindow = _content;
}
public UserControl ContentWindow
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged("ContentWindow");
}
}
For my 1st checkbox (CCS01), I have created a view(CCS01.xaml) which contains a few labels and textboxes in grid format.And its corresponding ViewModel is below:
public class CCS01ViewModel: BaseNotifyPropertyChanged
{
HomeViewModel _homeViewModel;
public ICommand OpenUsersCommand { get; private set; }
public CCS01ViewModel(HomeViewModel mainModel)
{
this._homeViewModel = mainModel;
//this._model = model;
OpenUsersCommand = new RelayCommand(OpenUsers, CanOpenUsers);
}
private void OpenUsers(object _param)
{
//UsersPanelViewModel upmodel = new UsersPanelViewModel(_mainModel, _model);
//UsersPanel up = new UsersPanel();
//up.DataContext = upmodel;
//_mainModel.SetNewContent(up);
}
private bool CanOpenUsers(object _param)
{
return true;
}
}
I want to load the selected checkbox view in the main UserControl(ExecutionDetails.xaml) . Currently, I am loading it with the first Checkbox view in it by default like this:
<StackPanel>
<Grid Name="HostGrid">
<ContentControl Content="{Binding ContentWindow}"/>
</Grid>
</StackPanel>
Please suggest me how to bind the view with respective checkboxes user controls dynamically based on the checkbox selection.
The code behind of this(ExecutionDetails.xaml.cs) is like this:
public partial class ExecutionDetails : UserControl
{
public ExecutionDetails()
{
InitializeComponent();
HomeViewModel viewmodel = new HomeViewModel();
CCS01ViewModel ccViewModel = new CCS01ViewModel(viewmodel);
CCS01 obj = new CCS01();
obj.DataContext = ccViewModel;
viewmodel.ContentWindow = obj;
this.DataContext = viewmodel;
}
}

Resources