Ok, we are trying out XAML for our GUI now (and learning as we go)...I have been able to do the data binding without a problem in XAML and C# independent of one another, but now comes the time I need to pass values back and forth and I'm a bit lost. When I compile and try to navigate to the page, it is throwing a XamlParseException: Specified class name doesn't match actual root instance type. Remove Class directive or provide an instance via XamlObjectWriterSettings.RootObjectInstance. Line 5 position 2.
Any help or a gentle shove in the right direction is greatly appreciated :)
Here's where I am:
namespace TheAirline.GraphicsModel.PageModel.PageFinancesModel
{
/// <summary>
/// Interaction logic for PageFinances.xaml
/// </summary>
public partial class PageFinances : Page
{
private Airline Airline;
public PageFinances(Airline airline)
{
InitializeComponent();
this.Language = XmlLanguage.GetLanguage(new CultureInfo(AppSettings.GetInstance().getLanguage().CultureInfo, true).IetfLanguageTag);
this.Airline = airline;
Page page = null;
//loading the XAML
using (FileStream fs = new FileStream("TheAirline\\GraphicsModel\\PageModel \\PageFinancesModel\\PageFinances.xaml", FileMode.Open, FileAccess.Read))
{
page = (Page)XamlReader.Load(fs);
}
//finding XAML element and trying to set the value to a variable
string airlineCash = GameObject.GetInstance().HumanAirline.Money.ToString();
TextBox cashValue = (TextBox)page.FindName("cashValue");
cashValue.DataContext = airlineCash;
}
}
}
And the first few lines of the XAML:
<Page
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:AirlineModel="clr-namespace:TheAirline.Model.AirlineModel"
mc:Ignorable="d"
x:Class="TheAirline.GraphicsModel.PageModel.PageFinancesModel.PageFinances"
xmlns:c="clr-namespace:TheAirline.GraphicsModel.Converters"
...>
</Page>
Bindings in XAML are resolved against the object that is assigned to the DataContext property of any given XAML element. The value of that property (as well as many other properties) Is Inherited in any given Visual Tree from parent elements to child elements.
for instance, given this class:
public namespace MyNamespace
{
public class ViewModel
{
public string Name {get;set;}
public bool IsActive {get;set;}
}
}
and this XAML:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
FontSize="20">
<Window.DataContext>
<local:ViewModel>
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Path=Name}"/>
<CheckBox IsChecked="{Binding Path=IsActive}"/>
<StackPanel>
</Window>
All four objects defined in XAML, the Window, the StackPanel, the TextBox, and the CheckBox, will have a FontSize of 20, and the instance of the ViewModel class assigned to their DataContext property. Therefore all bindings (Except bindings with a specified ElementName, RelativeSource, or Source) will be resolved against that instance.
It would be exactly the same if the property was assigned in code instead of in XAML:
public MyWindow() //Window Constructor
{
InitializeComponent();
this.DataContext = new ViewModel(); //Note that keyword "this" is redundant, I just explicity put it there for clarity.
}
Because of this, there is no need to set the DataContext property to each element explicitly, as the framework is already taking care of that.
Also, notice that in XAML, most built-in Markup Extensions have a default constructor convention that allows you to abbreviate their usage. In the case of the Binding Markup Extension, the default constructor has the Path property, therefore this:
<TextBox Text="{Binding Path=Name}"/>
is exactly the same as this:
<TextBox Text="{Binding Name}"/>
Now, for property changes in the underlying DataContext to be automatically passed from the binding source (ViewModel) to the binding target (XAML-defined objects), the source object must implement the System.ComponentModel.INotifyPropertyChanged interface and raise the PropertyChanged event every time a property changes.
Therefore, in order to support Two-Way Binding, the example class should look like this:
public namespace MyNamespace
{
public class ViewModel: INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
private bool _isActive;
public bool IsActive
{
get
{
return _isActive;
}
set
{
_isActive = value;
NotifyPropertyChanged("IsActive");
}
}
}
public void NotifyPropertyChanged (string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
}
Notice that the ViewModel class has no dependency or direct reference to any of the XAML-defined objects, but still it contains the Values of the properties that will appear in the UI. This allows for a complete decoupling between UI and application logic/data known as the MVVM Pattern. I strongly suggest you research on that topic if you expect to be successful in programming in C# + XAML, because it is a radical mindshift when compared to other, traditional UI paradigms.
For example, something like this is not recommended in XAML-based applications:
if (myWindow.CheckBox1.IsChecked)
//Do Something
because that would mean that you're coupling the application logic and making it dependant on the state of UI elements, which is precisely what you need to avoid.
Notice that all the links and all the concepts referenced in this answer pertain to WPF, but are also applicable to Silverlight and WinRT. Since you did not specify which of the three XAML-based frameworks you're using, I posted the WPF ones, which is what I'm most familiar with.
Related
Why can a dependency property with type XXX get a value of another type ?
Is the type of a dependency property just defined for the default value ?
For example:
Project structure:
User control code (CarControl):
XAML code:
<UserControl x:Class="TypeOfDependencyProperty.Controls.CarControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock Text="{Binding Brand}"/>
</Grid>
</UserControl>
Code behind (C#):
using System.Windows;
using System.Windows.Controls;
namespace TypeOfDependencyProperty.Controls
{
public partial class CarControl : UserControl
{
#region Brand
public static readonly DependencyProperty BrandProperty =
DependencyProperty.Register("Brand", typeof(string), typeof(CarControl),
new FrameworkPropertyMetadata((string)string.Empty));
public string Brand
{
get { return (string)GetValue(BrandProperty); }
set { SetValue(BrandProperty, value); }
}
#endregion
public CarControl()
{
InitializeComponent();
}
}
}
Note that this dependency property Brand is of type string here.
View code (CarView):
XAML code:
<Page x:Class="TypeOfDependencyProperty.Views.CarView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:TypeOfDependencyProperty.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="CarView">
<Grid>
<controls:CarControl Brand="{Binding Brand}"/>
</Grid>
</Page>
Code behind (C#):
using System.Windows.Controls;
using TypeOfDependencyProperty.ViewModels;
namespace TypeOfDependencyProperty.Views
{
public partial class CarView : Page
{
public CarView()
{
InitializeComponent();
this.DataContext = new CarViewModel();
}
}
}
View model code (CarViewModel):
namespace TypeOfDependencyProperty.ViewModels
{
public class CarViewModel
{
public string Brand { get; set; }
public CarViewModel()
{
Brand = "XXXXXXXXXXXXXXXXXXXXXXXXX"; // Any value
}
}
}
Now, if I change the type from string to List<XXX> (or other) as below, it keeps working.
#region Brand
public static readonly DependencyProperty BrandProperty =
DependencyProperty.Register("Brand", typeof(List<double>), typeof(CarControl),
new FrameworkPropertyMetadata((List<double>)null));
public List<double>Brand
{
get { return (List<double>)GetValue(BrandProperty); }
set { SetValue(BrandProperty, value); }
}
#endregion
Of course, I obtain a following error in the output window but that still works ! The value is shown without failure when it's running.
System.Windows.Data Error: 1 : Cannot create default converter to
perform 'one-way' conversions between types 'System.String' and
'System.Collections.Generic.List1[System.Double]'. Consider using
Converter property of Binding. BindingExpression:Path=Brand;
DataItem='CarViewModel' (HashCode=14000148); target element is
'CarControl' (Name=''); target property is 'Brand' (type 'List1')
System.Windows.Data Error: 5 : Value produced by BindingExpression is
not valid for target property.; Value='Car brand'
BindingExpression:Path=Brand; DataItem='CarViewModel'
(HashCode=14000148); target element is 'CarControl' (Name=''); target
property is 'Brand' (type 'List`1')
Can somebody explain me? Is it a bug ?
The Binding in
<TextBlock Text="{Binding Brand}"/>
binds directy to the Brand property in the view model. It does not use the Brand property of the control.
So it would even show the view model property value when you write
<controls:CarControl />
without binding the control's Brand property at all.
The correct usage of the control's property in the control's XAML would be this:
<TextBlock Text="{Binding Brand,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
When you say 'get a value'
in:
Why can a dependency property with type XXX get a value of another type ?
What you are asking about is really how a Binding is resolved.
The WPF framework resolves a binding and gets the actual current value.
When it is done resolving, the framework will try to 'set' the value for your dependency property, and only then will it check if the types are the same.
If they are not and no converter was supplied to the binding, it will throw this exception you added.
DependecyProperty type defines the type of data stored inside it.
You can bind to it only the same type of data. Or you can add a converter to binding. To do it you have to implement the interface IValueConverter.
But I don't understand your problem, want you to show entire list in a string? Or want you to create an item for each element of list?
In the second case I suggest you to use a ItemsControl and insert your custom control inside the ItemTemplate.
Finally you don't have to define default value every time, you can remove PropertyMetadata from DependecyProperty inizialization. And remember if you define it there it will be static and will change from others instances only with base types. If you have a class I suggest you to use null and assign it in the class Costructor.
public static readonly DependencyProperty BrandProperty =
DependencyProperty.Register("Brand", typeof(List<double>), typeof(CarControl));
The scenario is very simple here. I'm trying to bind a textbox to a property of a class at runtime:
tb.displayValue.DataContext = p.GetValue(currentNode, null);
xaml for the textbox:
<TextBox Name="displayValue" Grid.Column="1"
Style="{StaticResource propertyTextBoxStyle}"
Text="{Binding Path=DataContext,
RelativeSource={RelativeSource Self},
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
</TextBox>
When the application starts, the textbox does get populated with the correct information so the source to target binding is working fine. However, when I try and edit a value in the textbox and then switch focus, the changes are not reflected in the actual data structure. The value would stay on the UI, but as soon as I try to reload the UI from the data structure again it defaults back to the original value.
I suspect the binding is not working correctly at first, but after checking the memory address of tb.displayValue.DataContext and comparing it to the actual memory address of the data structure it's an identical match.
INotifyPropertyChanged has been implemented and I have added the OnPropertyChanged call to every setter. After spending two days trying to debug this issue I think I'm really running out of options here so any suggestion would be appreciated.
The simplest two-way binding works this way: you set the DataContext on your Window to a new instance of your MainWindowViewModel class which implements INotifyPropertyChanged, and you set the binding path on your TextBox to the name of the public property on your ViewModel you want to bind to.
I'm trying to show how you need a public property with a get and set to bind to, and how to properly set the DataContext for your window so that all of the controls within it are able to bind to the public properties available on it.
I've never heard of setting the DataContext of a TextBox directly to the return value from a method before, and it just seems wrong, so maybe you are not going about it the right way, and hopefully this helps you see how it can work.
MainWindow.cs
<Window x:Class="DemoWPFApp1.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:vm="clr-namespace:DemoWPFApp1.ViewModels"
Height="300" Width="460" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<TextBox Name="displayValue" Text="{Binding Path=BoundProperty,
Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
</TextBox>
</Window>
MainWindowViewModel.cs
namespace DemoWPFApp1.ViewModels
{
public class MainWindowViewModel : BaseViewModel
{
private string m_boundProperty;
public string BoundProperty
{
get
{
return m_boundProperty;
}
set
{
m_boundProperty = value; OnPropertyChanged();
}
}
public MainWindowViewModel()
{
BoundProperty = "Some value.";
}
}
}
BaseViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace DemoWPFApp1.ViewModels
{
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propName = null)
{
var e = PropertyChanged;
if (e != null && propName != null)
{
e.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
}
}
Hello to everyone who may visit this topic. I am attempting to understand the basic principles of XAML data binding. As you can see, here's a simple Universal Windows Program.
BACKGROUND: The bound element on MainPage.xaml WILL NOT receive data from the DataContext (ClockDispatcher.cs) if code placement (A) desired location is executed within the class.
The bound element on MainPage.xaml WILL eceive data from the DataContext (ClockDispatcher.cs) if code placement (B) testing general binding only is executed within the class.
When Debugging either CODE PLACEMENT OPTION, the locals windows shows that the public property "MyClock.Time" IS being set. But the bound element on MainPage.xaml is only realized when CODE PLACEMENT (B) testing general binding only is executed.
QUESTION: Is there an error in my logic that would prohibit the ability to set a class property as shown AND have that result be delivered to the associated bound element? Please be aware that the class property assignment takes place within the dispatcherTimer_Tick method.
Thank you in advance, for taking the time and effort to help me understand this issue!
Best Regards,
DMMcCollum
MainPage.xaml
<Page
x:Class="TestBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="using:TestBinding.Models"
mc:Ignorable="d">
<Page.DataContext>
<data:ClockDispatcher />
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock x:Name="TimeTextBlock" Text="{Binding MyClock.Time, Mode=OneWay}" />
</StackPanel>
</Grid>
ClockDispatcher.cs
namespace TestBinding.Models
{
public class ClockDispatcher
{
//Bound to "TimeTextBlock" on MainPage.xaml
public Models.Clock MyClock { get; private set; } = new Models.Clock();
public ClockDispatcher()
{
//CODE PLACEMENT (B)
//If executed here - WILL set class public property and WILL BE reflected in UI (but not updated as understood)
//MyClock.Time = string.Format("{0}", DateTime.Now.ToString("t"));
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, object e)
{
//CODE PLACEMENT (A)
//if executed here - WILL set class public property and WILL NOT BE reflected in UI (but updates property on each tick interval as understood)
MyClock.Time = string.Format("{0}", DateTime.Now.ToString("t"));
}
}
}
Clock.cs
namespace TestBinding.Models
{
public class Clock
{
public string Time { get; set; }
}
}
You should implement INotifyPropertyChanged in class Clock so that any change in source or target are in-sync.
Why it is working in Code Placement B - Since, the property value is set in constructor before Initialling the controls so value will always be set at load.
Why it is working in Code Placement A - - It is in different function which is called after the load of window.
Fix
public class Clock: INotifyPropertyChanged
{
private string time;
public string Time
{
get
{
return this.time;
}
set
{
this.time = value;
NotifyPropertyChanged("Time");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You need to notify the View for the change in your View Model, that is done with the help of INotifyPropertyChanged interface (There is a lot of information how to implement it) or using some of the MVVM frameworks like: MVVM Light, Prism, etc ...
INotifyPropertyChanged Interface MSDN The example is given in Win Forms project but essentially it's the same as WPF.
You're able to set the values to the control directly but bindings will not work this way.
For collections of data you could use something like ObservableCollection which notifies the view for changes in it's elements, eg you don't have to call Raise PropertyChanged event when you add an item, the collection will know that there is new item inserted.
I'm having this really simple class, lets call it Customer.
It look like this:
namespace TestValidation
{
class Customer
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
if (String.IsNullOrEmpty(value))
{
throw new Exception("Customer name is mandatory.");
}
}
}
}
}
Now, I've created a basic form, where the user can add customers to the database. The form contain simple TextBox, bounded to the Name property of Customer, and an "Add" button.
The XAML code is:
<Window x:Class="TestValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestValidation"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Margin="119,86,107,194" Name="CustomerName"
Text="{Binding Path=Customer.Name,
ValidatesOnExceptions=True,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged,
NotifyOnValidationError=True}"
/>
<Button Content="Add" HorizontalAlignment="Left" Margin="204,176,0,0" VerticalAlignment="Top" Width="74"/>
</Grid>
</Window>
From the setter of the Name property, you can understand that the name is mandatory for me, so I want an validation event to rise if the Name TextBox left blank. By validation rules of WPF - once the user focus out of the textbox, and there's no value over there - it should change the border color to red. For some reason - this is not happening, and I don't have a clue why. What is wrong in my process?
Now, I've read so many good articles about Validation in WPF (like Enforcing Complex Business Data Rules with WPF, Data validation in WPF and Validation in Windows Presentation Foundation), but none of them helped me solving my problem.
Eventually, I want the form to look like the form in Brian Noyes excellent article over the first link (Don't have 10 credits, so I can't attach a photo... sorry).
I'll be grateful if someone can explain to me how it really works.
Important note - I'm working with .Net framework 4, so I need a solution that suits this version.
I would definitely recommend using IDataErrorInfo for WPF validation since WPF already understands how to use it, and its easy to implement.
To start with, add the interface to the class containing the data you want to validate. The required methods will probably look something like this:
public class Customer : IDataErrorInfo
{
...
#region IDataErrorInfo Members
string IDataErrorInfo.Error
{
get { return null; }
}
string IDataErrorInfo.this[string columnName]
{
get
{
if (columnName == "Name")
{
// Validate property and return a string if there is an error
if (string.IsNullOrEmpty(Name))
return "Name is Required";
}
// If there's no error, null gets returned
return null;
}
}
#endregion
}
Next, you need to set ValidatesOnDataErrors=True in your TextBox binding so it runs the validation whenever the Name property changes:
<TextBox Text="{Binding Path=Customer.Name, ValidatesOnDataErrors=True}" ... />
And finally, create a Validation Template in your XAML to tell WPF how to draw a validation error. Here's the style/template I usually use:
<!-- ValidatingControl Style -->
<Style TargetType="{x:Type FrameworkElement}" x:Key="ValidatingControl">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding
Path=(Validation.Errors)[0].ErrorContent,
RelativeSource={x:Static RelativeSource.Self}}" />
</Trigger>
</Style.Triggers>
</Style>
Also, be sure your Customer class implements INotifyPropertyChanged so it correctly responds to UI updates. I don't see that in your code, but often people leave that out for simplicity :)
You did not specify a validation rule. The validation rule would be invoked before the control is left and then can do whatever you want to validate the inputs.
A simple example - and I guess that's what you want to do - is provided here.
Use IDataErrorInfo for validation. this link will help you.
I think the issue might be that your class isn't implementing INotifyPropertyChanged, so isn't binding as you're expecting.
Implement the INotifyPropertyChanged interface, raise an event when the property changed and it should work.
See http://msdn.microsoft.com/en-us/library/ms743695(v=vs.110).aspx for a walkthrough.
<Binding Path=Name UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
http://msdn.microsoft.com/en-us/library/ms752347%28v=vs.110%29.aspx#what_is_data_binding
Please use this blog : prasadcsharp.blogspot.com
Here is something that worked fine with me. No lag or long coding but I used it on double values only. You may change it as you need.
private void search_box_TextChanged(object sender, TextChangedEventArgs e)
{
// box text and background to normal state if user types numbers
search_box.Foreground = Brushes.Black;
search_box.Background = Brushes.White;
if (search_id.IsSelected == true)
{
try
{
//convert while user is typing
if (string.IsNullOrEmpty(search_box.Text)==false)
Convert.ToDouble(search_box.Text);
search_error.Text = null;
}
//if user types a letter or a space or a symbol ====>
catch (Exception)
{
// user cant type any value other than numbers as exception prevents it and clears the box text value <======
search_box.Text = null;
search_box.Foreground = Brushes.White;
search_box.Background = Brushes.Red;
search_error.Text="id is numberic value";
}
}
}
Hope it helps.
1) when you use exceptions for validation, i reccomand to throw the exception before assigning the value to the property backing field, so you refuse it and your data-object (the Customer object in this case) will contain only valid data:
using System;
namespace TestValidation
{
public class Customer
{
private string _name;
public string Name
{
get => this._name;
set
{
if (String.IsNullOrEmpty(value))
throw new ArgumentException("Customer name is mandatory.", nameof(Name));
_name = value;
}
}
}
}
2) By default, WPF data-binding engine ignores the exceptions that are raised in the setter procedure of the data objetc. You correctly set the ValidatesOnExceptions to true in order to instruct the data binding system to react on Exceptions. But, you set the UpdateSourceTrigger on PropertyChanged, thus the update of the property (Name) of the Source object (Customer) is triggered only when the Target property (Text) of the Target element (TextBox) is changed. If you start with an empty TextBox and just tab into it and than tab again away, the Text property has not been changed, so the updating of the Source property (Name) will no be triggered (this will happen even with LostFocus as the UpdateSourceTrigger mode). You can correct this just initializig the Text property to null or String.Empty in the costructor or in the Loaded event handler. This way, the textbox will appear with a red border as soon as the window is rendered. If you set UpdateSourceTrigger to LostFocus (that is the default for TextBox's Text property), the TextBox will appear initially without error, but if you tab in and out, it will be highlighted with the expected red border.
Note: all this works because the Text property of the TextBox use TwoWay as the default binding mode, data can go from target to source.
using System.Windows;
namespace TestValidation
{
public partial class MainWindow: System.Windows.Window
{
public CustomerTest()
{
InitializeComponent();
}
private void window_Loaded(object sender, RoutedEventArgs e)
{
this.txtCustomerName.Text = null;
}
}
}
<Window x:Class="TestValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ValidationTests"
Title="MainWindow" Height="350" Width="525"
Loaded="window_Loaded">
<Window.Resources>
<local:Customer x:Key="customer" />
</Window.Resources>
<Grid DataContext="{StaticResource customer}">
<TextBox Margin="119,86,107,194"
x:Name="txtCustomerName" x:FieldModifier="protected"
Text="{Binding Path=Name,
ValidatesOnExceptions=True,
UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Add" HorizontalAlignment="Center" Margin="204,176,0,0" VerticalAlignment="Top" Width="74"/>
</Grid>
</Window>
3) In this case, the INotifyPropertyChanged is not required, since you are just interested in changing the value of the Source property (Name) by the interaction of the user in the TextBox, you are not modifying the Name property with other C# code. The INotifyPropertyChanged is implemented to notify the WPF data-binding system about changes in the data objetcs, so that WPF can update the data in user interface (update the Target when the Source is changed due to code procedures).
You didn't implement INotifyPropertyChanged.
Also keep your attention to IDataErrorInfo and INotifyDataErrorInfo.. which are using in case if you want to move validation logic out of setters.
Also need admit that in modern app better move validation logic in separate type. (see fluentValidation)
using System;
namespace TestValidation
{
public class Customer : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => this._name;
set
{
if(_name == value) return;
if (String.IsNullOrEmpty(value))
throw new ArgumentException("Customer name is mandatory.", nameof(Name));
_name = value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
// TODO: Impelemnt interface INotifyPropertyChanged
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
#endregion
}
}
I have trouble to understand how dependency properties can be used between C# and xaml code.
This is a smal code example of my question
XAML code:
<Window x:Class="WpfChangeTextApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Label Name="statusTextLabel" Content="{Binding StatusText}"></Label>
<Button Name="changeStatusTextButton" Click="changeStatusTextButton_Click">Change Text</Button>
</StackPanel>
C# code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string StatusText
{
get { return (string)GetValue(StatusTextProperty); }
set { SetValue(StatusTextProperty, value); }
}
// Using a DependencyProperty as the backing store for StatusText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StatusTextProperty =
DependencyProperty.Register("StatusText", typeof(string), typeof(MainWindow));
private void changeStatusTextButton_Click(object sender, RoutedEventArgs e)
{
StatusText = "Button clicked";
}
}
So, my trouble is that Label statusTextLabel dose not get updated when I click on the button. My trouble is that I don't know in what part of the code that I'm doing something wrong, is it in the xaml or in the C#? In the xaml I might doing something wrong in the Binding? Or have I missed doing something in the C# code?
By default, binding paths are relative to the DataContext property of the current element. You have not set it to anything, so it can't resolve the binding. If you want the StatusText property on your window class, then there are two approaches. One is to use a binding with a RelativeSource of FindAncestor to find the Window in the tree and bind to its properties directly:
<Label Name="statusTextLabel" Content="{Binding StatusText,
RelativeSource={RelativeSource AncestorType=Window}}"></Label>
The other is to set the DataContext of the Window to itself, so it will be inherited by the label. For example, in your constructor:
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
For most applications, you will actually want a separate class to represent the data, and you will set an instance of that class as the DataContext. You can also use ordinary CLR properties instead of dependency properties, although you will need to implement INotifyPropertyChanged if you want to UI to be informed when properties change. Dependency properties are more useful when you are writing a custom control and you want users to be able to set the properties using data binding.