Dynamic Validation In WPF Using MVVM? - wpf

How to do text box input validation in wpf MVVM structure?

You should implement the INotifyDataErrorInfo interface in your view model:
public class ViewModel : INotifyDataErrorInfo
{
private readonly Dictionary<string, string> _validationErrors = new Dictionary<string, string>();
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
//validate:
if (_text?.Length < 3)
_validationErrors[nameof(Text)] = "Too short...";
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Text)));
}
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors => _validationErrors.Count > 0;
public IEnumerable GetErrors(string propertyName) =>
_validationErrors.TryGetValue(propertyName, out string error) ? new string[1] { error } : null;
}
View:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Window>

check this: https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-binding-validation and consider on this: Validation.ErrorTemplate="{StaticResource validationTemplate}"

Related

WPF How to bind object from window to custom user control

I need to create a custom user control and pass it from the main window an object. I need to display the object's attribute inside the user control, how can i do it? Thanks in advance.
EDIT: This is my code, what i'm doing wrong?
My custom control:
public partial class DetailsComponent : UserControl
{
public static readonly DependencyProperty ModelProperty = DependencyProperty.Register("Model", typeof(bool), typeof(DetailsComponent), new PropertyMetadata(null));
public ModelClass Model
{
get { return (ModelClass)GetValue(ModelProperty); }
set
{
SetValue(ModelProperty, value);
}
}
public DetailsComponent()
{
InitializeComponent();
DataContext = this;
}
}
usercontrol.xaml.cs:
<UserControl x:Class="WpfApp3.DetailsComponent"
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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel>
<TextBlock Text="{Binding Name}"></TextBlock></TextBlock>-->
</StackPanel>
</Grid>
MainWindows.xaml:
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3" xmlns:custom="clr-namespace:LoadingSpinnerControl;assembly=LoadingSpinnerControl" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:DetailsComponent Model="{Binding Model}"></local:DetailsComponent>
</Grid>
MainWindows.xaml.cs:
public partial class MainWindow : Window, INotifyPropertyChanged
{
ModelClass Model = null;
public MainWindow()
{
InitializeComponent();
Model = new ModelClass("hi", "hi", "hi");
DataContext = this;
}
}
ModelClass.cs:
public class ModelClass
{
public ModelClass(string name, string description, string city)
{
Name = name;
Description = description;
City = city;
}
public string Name { get; set; }
public string Description { get; set; }
public string City { get; set; }
}
Model must be defined as a public property for you to be able to bind to it:
public partial class MainWindow : Window
{
public ModelClass Model { get; }
public MainWindow()
{
InitializeComponent();
Model = new ModelClass("hi", "hi", "hi");
DataContext = this;
}
}
The DetailsModel should not set its own DataContext property because then you cannot bind to the Model property of its inherited DataContext:
public partial class DetailsComponent : UserControl
{
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(ModelClass), typeof(DetailsComponent), new PropertyMetadata(null));
public ModelClass Model
{
get { return (ModelClass)GetValue(ModelProperty); }
set { SetValue(ModelProperty, value); }
}
public DetailsComponent()
{
InitializeComponent();
}
}
You could bind to the Name property of the Model dependency property using a RelativeSource binding:
<UserControl x:Class="WpfApp3.DetailsComponent"
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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel>
<TextBlock Text="{Binding Model.Name,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
</StackPanel>
</Grid>
</UserControl

why is the main window not enabled when the load event is in the prism framework

xaml code:
<Window x:Class="ZPC.Phone.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:local="clr-namespace:ZPC.Phone"
WindowStyle="None"
x:Name="WindowMain"
xmlns:event="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Background=" #F5F5F5"
Title="MainWindow" Height="780" Width="1280">
<event:Interaction.Triggers>
<event:EventTrigger EventName="Loaded">
<prism:InvokeCommandAction Command="{Binding CommandLoaded}"/>
</event:EventTrigger>
</event:Interaction.Triggers>
</Window>
MainWindowViewModel.cs Code:
public class MainWindowViewModel: BindableBase, INavigationAware, INotifyPropertyChanged
{
public ICommand CommandLoaded { get; set; }
public MainWindowViewModel()
{
CommandLoaded = new CommandBase(new Action<object>((e) =>
{
}));
}
}
questions:
I use event and load "CommandLoaded" in the constructor, and then the main form state will not be enabled,Why?thank you

WPF ContextMenu IsChecked Binding

How to write Binding for "IsChecked" ?
I want to check either of 2 menu.
If I click "Hello1", I want to add check on "Hello1",
and remove Check of "Hello2".
If I click "Hello2", I want to add check on "Hello2",
and remove Check of "Hello1".
I tried for 1 week. but I can not. Please help me "How to write binding"
There is no example for internet.
I recently started WPF programming. I only can do form application.
it is very big different....
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ContextMenu Name="ContextMenuMain" x:Key="menuListBox">
<MenuItem Header="Hello1" IsChecked="{Binding Path=IsCheck1, Mode=OneWay}" Name="ContextMenu_Hello1" Click="ContextMenuClick_Hello1"/>
<MenuItem Header="Hello2" IsChecked="{Binding Path=IsCheck2, Mode=OneWay}" Name="ContextMenu_Hello2" Click="ContextMenuClick_Hello2"/>
</ContextMenu>
</Window.Resources>
</Window>
My most difficult point is below code.
I tried to use name of "ContextMenu_Hello1" in MainWindow.
but C# does not allow me to use it...
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ContextMenu_Hello1.DataContext // error !
}
}
Is this out of scope ? but Why ?
Set the ContextMenu property of the window. You should also set the IsCheckable property to true if you want to be able to check the menu items. Also, you cannot set the binding mode to OneWay:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.ContextMenu>
<ContextMenu Name="ContextMenuMain">
<MenuItem Header="Hello1" IsCheckable="True" IsChecked="{Binding Path=IsCheck1}" Name="ContextMenu_Hello1" />
<MenuItem Header="Hello2" IsCheckable="True" IsChecked="{Binding Path=IsCheck2}" Name="ContextMenu_Hello2" />
</ContextMenu>
</Window.ContextMenu>
</Window>
When you have made these changes, you can then bind to any property of the DataContext:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow ()
{
InitializeComponent();
DataContext = this;
}
private bool _isCheck1;
public bool IsCheck1
{
get { return _isCheck1; }
set
{
_isCheck1 = value;
NotifyPropertyChanged();
_isCheck2 = !_isCheck1;
NotifyPropertyChanged("IsCheck2");
}
}
private bool _isCheck2;
public bool IsCheck2
{
get { return _isCheck2; }
set
{
_isCheck2 = value;
NotifyPropertyChanged();
_isCheck1 = !_isCheck2;
NotifyPropertyChanged("IsCheck1");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

WPF XAML - Property update in nested controls

I have a control that exposes a string property named HeaderText in this way:
public partial class HeaderControl : UserControl
{
public static DependencyProperty HeaderTextProperty;
[Category("Header Properties")]
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
static HeaderControl()
{
HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof(string), typeof(HeaderControl), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
public HeaderControl()
{
this.DataContext = this;
InitializeComponent();
}
}
HeaderControl's Xaml:
<UserControl x:Class="Col.HMI.Controls.HeaderControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Border Background="{Binding Path=HeaderBackground}" >
<TextBlock Text="{Binding Path=HeaderText}" Foreground="White" TextAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontFamily="Segoe UI Light" FontSize="36" Margin="5"/>
</Border>
and I want to use this HeaderControl in another UserControl, in this way:
OtherControl's Xaml:
<controls:HeaderControl Grid.Row="0" HeaderText="DEMO" />
And this works without problems. But if I bind the HeaderText property to a string property in the OtherControl ViewModel, in this way:
<controls:HeaderControl Grid.Row="0" HeaderText="{Binding Path=SummaryTitle}" />
the bind doesn't work.
This is the SummaryTitle property in the OtherControl ViewModel:
public string SummaryTitle
{
get
{
return _summaryTitle;
}
set
{
_summaryTitle = value; OnPropertyChanged("SummaryTitle");
}
}
PS: I have other controls binded to the OtherControl View Model and they work well.
You are setting DataContext of HeaderControl to itself in the constructor by doing this:
this.DataContext = this;
That means, when you apply some binding to any of the properties in HeaderControl, the Binding engine tries to find the bound property (in your case SummaryTitle) in this control, which it wont find and will fail.
So, to fix your problem, do not set the DataContext of HeaderControl to itself in the Constructor and the Binding engine will try find the properties in the correct DataContext.
Update your HeaderControl constructor to the following, and the bindings should start to work:
public HeaderControl()
{
InitializeComponent();
}
UPDATE
What you are trying to do here is, You want to have DependencyProperty named HeaderText in your UserControl, so that it's value can be set via DataBinding, and then update a value of TextBlock in your UserControl with the value of that DependencyProperty.
You can achieve this by two ways:
1) By updating TextBlock Binding to use ElementNme and Path syntax, XAML would look like this:
<UserControl x:Class="WpfApplication1.HeaderControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="_This">
<Grid>
<TextBlock Text="{Binding ElementName=_This, Path=HeaderText}" FontSize="24" />
</Grid>
</UserControl>
With this approach, whenever the property HeaderText is changed either via Binding or explicitly setting the value.
2) By listening to property value changed event for HeaderText property and then updating the TextBlock accordingly.
For this approach your HeaderControl.xaml would look like:
<UserControl x:Class="WpfApplication1.HeaderControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" >
<Grid>
<TextBlock x:Name="TextBlockInUserControl"/>
</Grid>
</UserControl>
and the HeaderControl.xaml.cs
public partial class HeaderControl : UserControl
{
public static readonly DependencyProperty HeaderTextProperty;
[Category("Header Properties")]
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
static HeaderControl()
{
HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof (string), typeof (HeaderControl),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHeaderTextPropertyChanged));
}
private static void OnHeaderTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var headerControl = (HeaderControl) dependencyObject;
headerControl.UpdateTextBlock((string) dependencyPropertyChangedEventArgs.NewValue);
}
void UpdateTextBlock(string text)
{
TextBlockInUserControl.Text = text;
}
public HeaderControl()
{
InitializeComponent();
}
}

binding to a usercontrol not working

i have encountered a databinding probleme,
so i create a usercontrole
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static DependencyProperty TestThisProperty = DependencyProperty.Register
(
"TestThis",
typeof(string),
typeof(UserControl1),
new PropertyMetadata("Some Data",new PropertyChangedCallback(textChangedCallBack))
);
public string TestThis
{
get { return (string)GetValue(TestThisProperty); }
set { SetValue(TestThisProperty, value); }
}
static void textChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
UserControl1 _us = (UserControl1)property;
_us.MyUserControl.TestThis = (string)args.NewValue;
}
}
UserControl1.xaml
<UserControl x:Class="WpfApplication1.UserControl1"
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"
mc:Ignorable="d" x:Name="MyUserControl"
d:DesignHeight="300" d:DesignWidth="300">
</UserControl>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
UserControl1 _uc = new UserControl1();
public MainWindow()
{
InitializeComponent();
DispatcherTimer _dt = new DispatcherTimer();
_dt.Interval = new TimeSpan(0, 0, 1);
_dt.Start();
_dt.Tick += new EventHandler(_dt_Tick);
}
private void _dt_Tick(object s,EventArgs e)
{
_uc.TestThis = DateTime.Now.ToString("hh:mm:ss");
}
and finaly the MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Text="{Binding Path=TestThis,ElementName=MyUserControl, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
but the problem here, whene i debug my code i get this warning
Cannot find source for binding with reference 'ElementName=MyUserControl'. and the ui does not update of course,any idea please?
ElementName only targets names in the same namescope, e.g.
<TextBox Name="tb"/>
<Button Content="{Binding Text, ElementName=tb}"/>
If you do not define the UserControl in the MainWindow XAML and give it a name there or register a name in code behind (and target that) you won't get this ElementName binding to work.
An alternative would be exposing the usercontrol as a property on the MainWindow, e.g.
public UserControl1 UC { get { return _uc; } }
Then you can bind like this:
{Binding UC.TestThis, RelativeSource={RelativeSource AncestorType=Window}}

Resources