UpdateSourceTrigger not working? - c#

I'm trying to validate the text in a textbox when a key is pressed. Here's the shortest code sample that shows what I'm trying to do:
<Window x:Class="WpfApplication1.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">
<Grid>
<TextBox FontSize="15" HorizontalAlignment="Left" Name="txtEmail" VerticalAlignment="Top" Width="135"
Text="{Binding ValidationRules.EmailAddress, ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
"ValidationRules" class:
class ValidationRules
{
string email = "";
public string EmailAddress
{
get
{
return email;
}
set
{
Console.WriteLine("Setting!");
//Only check if there is any text for now...
if (String.IsNullOrWhiteSpace(value))
{
throw new Exception();
}
email = value;
}
}
}
When I start typing in the textbox, I don't get "Setting" as console output, even though I'm using UpdateSourceTrigger=PropertyChanged. I've done my research but all the examples I could find are long winding and confusing. I would also appreciate it if you could point out any other mistakes I have in validation, but try to explain in simple terms if possible because I'm new to WPF.

It must be an issue on where you are setting your DataContext.
This example seems to work fine:
Code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
ValidationRules = new ValidationRules();
}
private ValidationRules _validation;
public ValidationRules ValidationRules
{
get { return _validation; }
set { _validation = value; NotifyPropertyChanged("ValidationRules"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class ValidationRules : INotifyPropertyChanged
{
string email = "";
public string EmailAddress
{
get
{
return email;
}
set
{
Console.WriteLine("Setting!");
//Only check if there is any text for now...
if (String.IsNullOrWhiteSpace(value))
{
throw new Exception();
}
email = value;
NotifyPropertyChanged("EmailAddress");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="125.078" Width="236.441" x:Name="UI" >
<Grid DataContext="{Binding ElementName=UI}">
<TextBox FontSize="15" HorizontalAlignment="Left" Name="txtEmail" VerticalAlignment="Top" Width="135"
Text="{Binding ValidationRules.EmailAddress, ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>

Related

Variable value transfered from Model to ViewModel is not showing on View

I'm learning WPF MVVM and I get stuck with one problem. I'm calculating variable value in one class every 200ms, then I'm sending it to ViewModel where PropertyChanged event is fired, but the value did not appear on View. When I copy this code for calculation of variable value to ViewModel constructor then everything is working. Could you give me some hints what am I doing wrong?
Class where I calculate values:
public class CalculatingSecondsTask
{
public string seconds { get; set; }
public CalculatingSecondsTask()
{
Task.Run(async () =>
{
int i = 0;
while (true)
{
await Task.Delay(200);
seconds = (i++).ToString();
UpdateSecondsInViewModel(seconds);
}
});
}
public void UpdateSecondsInViewModel(string secs)
{
ViewModel test = new ViewModel();
test.Seconds_To_Start = secs;
}
My ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private string mSeconds_To_Start;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (propertyName == null)
throw new ArgumentNullException("propertyExpression");
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public string Seconds_To_Start
{
get
{
return mSeconds_To_Start;
}
set
{
mSeconds_To_Start = value;
OnPropertyChanged(nameof(Seconds_To_Start));
}
}
And my View is like:
<Window x:Class="Program.Views.StartWindow"
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:Program.Views"
xmlns:ViewModel="clr-namespace:Program.ViewModels"
mc:Ignorable="d"
Title="Program" Height="110" Width="400">
<Window.DataContext>
<ViewModel:ViewModel/>
</Window.DataContext>
<Grid>
<!-- Text -->
<TextBlock x:Name="Seconds_To_Start" HorizontalAlignment="Center">
<Run Text="{Binding Seconds_To_Start}" />
<Run Text="seconds" />
</TextBlock>
</Grid>
</Window>

Databinding not working for user control - PropertyChanged always null

I have a problem with data binding. A test application that I have looks as follows:
There's a mainwindow:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:WpfApplication2"
x:Name="main"
Title="MainWindow" >
<StackPanel >
<Button Content="Button" Click="Button_Click"/>
<Controls:UserControl1 />
</StackPanel>
</Window>
And a user control:
<UserControl x:Class="WpfApplication2.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="uc"
>
<Grid >
<TextBox Width="40" Text="{Binding ElementName=main,
Path=Status, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
I want to click the button on the main window to have the value of text box in user control updated:
The code of MainWindow file:
namespace WpfApplication2
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _status;
public string Status
{
get { return _status; }
set
{
if (value != _status)
{
_status = value;
RaisePropertyChanged("Status");
}
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (Status == "one")
Status = "two";
else
Status = "one";
}
}
}
And the code of UserControl:
namespace WpfApplication2
{
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, e: new PropertyChangedEventArgs(propertyName));
}
public UserControl1()
{
InitializeComponent();
}
}
}
I don't understand why doesn't it work, but the PropertyChanged is always null. The example is in the simplest form I can imagine...
You are trying to access the parent window using the ElementName binding, as far as I am aware, that is not possible. You can however use a relative source binding to get the parent window:
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Status}" ... />
Follow up edit:
Your child user control should look like this:
<UserControl
...
x:Name="usr">
<Grid>
<TextBlock Text="{Binding Message, ElementName=usr}"/>
</Grid>
</UserControl>
You will then need to create a dependency property called 'Message' (This is just an example, I'm not sure what you want to call this property).
public partial class YourUserControl: UserControl
{
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
// Using a DependencyProperty as the backing store for Message. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string), typeof(YourUserControl), new PropertyMetadata(""));
public UserControl1()
{
InitializeComponent();
}
}
Then, when you declare this in your parent user control, simply set the binding of the Message property to whatever property you need to bind to in your parent user control:
<YourNamespace:YourUserControl Message="{Binding PropertyName, ElementName=elementName}" />

Unable to bind data to textbox using MVVM architecture?

I am new to MVVM and WPF. I have tried to to bind data to a textbox using DataContext.
Model: MyMessage.cs
public class MyMessage : INotifyPropertyChanged
{
private string testMessage;
public string TestMessage
{
get { return testMessage; }
set
{
testMessage = value;
OnPropertyChanged("TestName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel: MainViewModel.cs
class MainViewModel
{
MyMessage myMessage;
public MainViewModel()
{
myMessage = new MyMessage();
myMessage.TestMessage="Hai";
}
View : MainWindow.xaml
<Window x:Class="DemoApp2.Views.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">
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding TestMessage}" VerticalAlignment="Top" Width="120"/>
</Grid>
You need to turn "myMessage" into property, and bind it as MyMessage.TestMessage in your TextBox, assuming you bind MainViewModel as DataContext in Window.
Try this:
class MainViewModel
{
private MyMessage _messageProperty;
public MyMessage MessageProperty
{
get { return _messageProperty; }
set { _messageProperty = value; }
}
public MainViewModel()
{
_messageProperty = new MyMessage();
_messageProperty.TestMessage="Hai";
}
Also the string in your OnPropertyChanged event must be the same name as the property, like this:
public string TestMessage
{
get { return testMessage; }
set
{
testMessage = value;
OnPropertyChanged("TestMessage");
}
}
In the code-behind file of your MainWindow.xaml.cs, set the data context to your ViewModel:
class MainWindow
{
public MainWindow()
{
this.DataContext = new MainViewModel();
}
And in your MainWindow.xaml file, you have to refer to the nested property of your MessageProperty
<Window x:Class="DemoApp2.Views.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">
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding MessageProperty.TestMessage}" VerticalAlignment="Top" Width="120"/>
</Grid>
Let me know if it worked and if you need more informations ;-)
Also I recommend you to make a Quickstart Tutorial of how MVVM works and how it's implemented e.g. http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial

wpf textblock binding not working

I am new to WPF programming, so forgive me if this is a simple issue.
I have my Mainwindow.xaml set up like so:
<Window x:Class="GNMAwpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GNMAwpf"
Title="Uploader"
Height="353" Width="342" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:GinniNet x:Name="g" />
</Window.DataContext>
Further down I have a textblock:
<TextBlock Text="{Binding Path=StatusText}" Grid.ColumnSpan="2" MinWidth="150"></TextBlock>
In my class i have :
class GinniNet : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _statusMessage;
public string StatusMessage
{
get
{
return _statusMessage;
}
set
{
_statusMessage = value;
OnPropertyChanged("StatusMessage");
}
}
public GinniNet()
{
StatusMessage = "Ready";
}
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
When I change StatusMessage The textblock does not update ever. Where am i making my mistake?
Binding is incorrect -
<TextBlock Text="{Binding Path=StatusText}"/>
It should be -
<TextBlock Text="{Binding Path=StatusMessage}"

Data Binding problem

I want to do something like this diagram Data Binding Diagram.
If i update TextBox text Then update TextBlock text and Property and if i change Property Value then also update Textbox and textBlock text. Please tell me how can i do it using WPF ????
Thank`s For Help.
Im not sure if I understand right your question. Are the two Textboxes in the same view or in different?
Here a solution with 2 textboxes in the same view:
View (xaml):
<Window x:Class="Sandbox.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"
Name="mainWindow">
<StackPanel>
<TextBox Name="UpperTextBox" Text="{Binding ElementName=LowerTextBox, Path=Text,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="LowerTextBox" Text="{Binding MyValue, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
View-Codebehind (xaml.cs):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
}
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _myValue;
public string MyValue
{
get { return _myValue; }
set
{
_myValue = value;
OnPropertyChanged("MyValue");
}
}
}

Categories

Resources