How binding of Silverlight UI to .NET Classes works - c#

I am looking at binding a certain silverlight UI to a C# class. In the example code below there are two textboxes in the XAML page. Any changes made to one textbox is reflected in the other the minute it loses focus and vice-versa. While the example I have works as I want it to, I have no clue as to what is goin on under the hood and how it works that way.
Here is the C# code
public class Person : INotifyPropertyChanged
{
public string FirstName
{
get
{return firstname;}
set
{ firstname = value;
FirePropertyChanged("FirstName");
}
}
private string firstname;
void FirePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Grid in the mainpage.xaml
<Grid x:Name="MyLayoutRoot" Background="White" ShowGridLines="True">
<TextBox Text="{Binding FirstName, Mode=TwoWay}" Grid.Column="1"></TextBox>
<TextBox Text="{Binding FirstName, Mode=TwoWay}" Grid.Column="1" Grid.Row="3"></TextBox>
</Grid>
Codebehind for Mainpage.xaml
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Page_Loaded);
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
Person p = new Person()
{
FirstName ="Dee"
};
MyLayoutRoot.DataContext = p;
}
}
My understanding so far which is a bit hazy now is this:
The textbox in the xaml(mainpage.xaml) knows what property to bind to based on its "Binding" tag, from the class (Person) it was setup with, in the xaml codebehind file(mainpage.xaml.cs) by using the datacontext property there.
INotifyPropertyChanged is an interface in the person class, that provides some hook that allows the Xaml UI to know when the Firstname property got changed in the UI.
The minute the Firstname property is set, the FirePropertyChanged method gets called which triggers this event PropertyChangedEventHandler as is implemented in this line
PropertyChanged(this, new PropertyChangedEventArgs(property));
Can anyone elaborate on what goes on behind the scenes here at this moment, when one of the textboxes changes and loses focus; and how does the Binding property on the Silverlight client side UI, maintain contact with the C# class which, correct me if I am wrong is still on the server that the silverlight UI was downloaded from.
Thanks for your time.

If the Person class is in the same Silverlight UI project, then it is actually on the client (not the server). Maybe this makes it easier to understand?

Related

WPF DataGrid not updating - is it one-way binding?

C#, WPF. I am using a Datagrid with binding. My understanding is that with INotifyPropertyChanged implemented, object properties should update in the Datagrid if they are changed.
Currently this is not happening, although I I have implemented INotifyPropertyChanged and I know from testing that the PropertyChanged event is firing. My guess is that binding is not two-way(?) If that is the case I'm not sure how to set it to two-way. The binding is set in XAML, and the ItemsSource is set later in code-behind:
<DataGrid Name="dataGridxyz" ItemsSource="{Binding}">
dataGridxyz.ItemsSource = foo;
Adding two-way binding in XAML using this syntax causes an error:
<DataGrid Name="dataGridxyz" ItemsSource="{Binding, Mode=TwoWay}">
So I was looking for something like this:
dataGridxyz.ItemsSource = foo;
dataGridxyz.Binding.Mode = TwoWay;
It may be that I could set it to two-way binding either in XAML or code-behind... but I can't see how to do either.
EDIT:
The following is minimal functional example to show the problem. It is a much-simplified version of the real thing which is part of a much bigger project.
When the button is clicked, the Name property is changed but it does not update in the PropertyGrid.
<Window x:Class="testBinding.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"
mc:Ignorable="d"
Title="MainWindow">
<Grid>
<StackPanel Orientation="Vertical">
<DataGrid Name="dg" ItemsSource="{Binding}" AutoGenerateColumns="True"/>
<Button Name="btn" Width="100" Height="20" Content="Test" Click="btn_Click"/>
</StackPanel>
</Grid>
namespace testBinding
{
public partial class MainWindow : Window
{
BindingList<foo> bar = new BindingList<foo>() { new foo() };
public MainWindow()
{
InitializeComponent();
dg.ItemsSource = bar;
}
private void btn_Click(object sender, RoutedEventArgs e)
{
bar[0].Name = "Paul";
}
}
class foo : genericClass, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
class genericClass : INotifyPropertyChanged
{
private string _name = "John";
public string EyeColor = "Blue";
public bool Child = false;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Name
{
get { return _name; }
set
{
_name = value;
MessageBox.Show("Name changed!"); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
OnPropertyChanged("Name");
}
}
}
}
I figured out what was happening here through a combination of guesswork and trial and error. Thanks to those who commented.
It was not caused by one-way binding as I had originally surmised.
This problem was caused by the fact that the foo object in the example above inherits from another class (genericClass) and both implement INotifyPropertyChanged. It seems clear that the existence of the PropertyChanged event in the foo class prevents the DataGrid from updating. I had not expected this behavior since I know that the PropertyChanged event in the inherited class does fire and does update the Name property.
If I remove the PropertyChanged event from foo, then the name updates in the PropertyGrid as expected.
class foo : genericClass, INotifyPropertyChanged
{
//public event PropertyChangedEventHandler PropertyChanged;
}
It leaves me with the problem of how to handle property changes at more than one level of inheritance (i.e. both in a class and in one it inherits from, which seems a valid thing to do) ... but that is perhaps a different question.

How to update Sub Window on view-model's properties changed?

I'm learning wpf now, but there a problem when coding . The play data were get from MainWindow,and show player's ID,Name....But i need update player's information. SubWindowViewModel side ,I have update binding properties ,but there is problem, I can't update properties in the view side .I want to update SubWindow when viewModel's properties changed .
public SubWindow(Player player)
{
InitializeComponent();
ISubWindowViewModel subWindowViewModel = new SubWindowViewModel();
#region Get data
subWindowViewModel.ID = player.ID;
subWindowViewModel.Name = player.Name;
subWindowViewModel.Sex = player.Sex;
#endregion
this.DataContext = subWindowViewModel;
}
and view model has implement INotifyPropertyChanged,in xaml.cs:
<TextBox x:Name="Name" Text="{Binding UserName,Mode=TwoWay}"/>
<TextBox x:Name="Sex" Text="{Binding Sex,Mode=TwoWay}" />
<TextBox x:Name="ID" Text="{Binding ID,Mode=TwoWay}"/>
Thank you very much!
I am not pro.. coder too.
What I think is you have to implement something called INotifyPropertyChanged interface in the viewmodel class.
Check out the link. There could be more link out there.
How to: Implement Property Change Notification
INotifyPropertyChanged Interface in WPF with Example
Learn and implement it. Hope it helps. Thank you.
EDIT:
I am assuming your viewModelClass name as PersonViewModel. So your viewmodel class would be sth. like below.
class PersonViewModel:INotifyPropertyChanged
{
private string _username;
public string UserName
{
get { return _username; }
set {
_username= value;
OnPropertyChanged("UserName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Property));
}
}
}
Your xaml is correct. So now I am assuming that you have passed the same viewmodelclass object(in the constructor) that u have used in the MainWindow. So in the code behind you have to set the DataContext of the window as u have done above.
public SubWindow(PlayerViewModel player)
{
InitializeComponent();
this.DataContext=player;
}

XAML Data Binding: Unexpected results when setting class property from within DispatcherTimer's tick event

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.

Simple XAML binding in a textblock text

I have a TextBlock control inside a HubSection in a Windows 8.1 Universal app.
<TextBlock x:Name="api_enabled_label"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Text="{Binding APIinfotext}" />
Now when the page is launched, in the contrustor, there is a method that is run.
public string APIinfotext { get; set; }
public sealed partial class MainPage : Page {
VoipMS voip_service = new VoipMS("shoukatali#hotmail.com", "Kitt0cat");
public string APIinfotext { get; set; }
public MainPage() {
this.InitializeComponent();
// disable sections until API is enabled
mainpagehub.Sections[1].IsEnabled = false;
mainpagehub.Sections[2].IsEnabled = false;
//check for API and enable sections
checkAPI();
}
private async void checkAPI() {
//irrelevant code above
switch (result) {
case "success":
APIinfotext = "Your API is connected";
break;
//irrelevant code below
}
}
So why dosnt this work? I set the DataContext of the Textblock to the current class (which is the MainPage partial class) and the property is a public property.
Note: Today is my first time working with .net 4.5 with XAML after a huge break at the .net 2.0 framework with WinForms.
Your binding doesn't know that APIinfotext property has changed. To let the bindings know that the property has changed you can do one of the following. The first one is the easiest.
1) implement INotifyPropertyChanged interface and raise the PropertyChanged changed event once APIinfotext has changed (PropertyChanged("APIinfotext"));
2) Have an event called APIinfotextChanged with the standard event signature and raise that event after the property has changed.
3) Implement your property as a DependencyProperty (not an ideal solution in this case).
You might be missing the part where you have to RaiseProperyChange NotifyPropertyChage to update the bindings. your Model should implement the INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
then
RaisePropertyChanged("APIinfotext");
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
Looks like you need a very simple example of what the other two are talking about. Let's assume nothing. You need to set the DataContext correctly, plus raise the event. This is as simple as I can put it, when you click on the button it will change the TextBox because I change the Property which raises the event.
XAML
<Page>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBox Text="{Binding APIinfotext}" Height="100" Width="400" HorizontalAlignment="Left"/>
<Button x:Name="myButton" Content="Change Text" Height="200" Width="400" Click="myButton_Click"/>
</StackPanel>
</Grid>
</Page>
C# (Pay attention, to the SET part of the APIinfotext)
using System.ComponentModel; // INotifyPropertyChanged
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private string _apiinfotext = "Default Text";
public string APIinfotext
{
get { return _apiinfotext; }
set
{
_apiinfotext = value;
RaisePropertyChanged("APIinfotext");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
this.APIinfotext = "Don't confuse movement for progress.";
}
}

C# WPF Binding Behavior

I am a reasonably experienced programmer but new to WPF. I have bound a textblock on a form to an object property, but it is not updating the form as I would expect when I set the property. The binding appears to be done correctly--if I troubleshoot with a button that updates the property the form changes, but when I initially set the property in the form's constructor by parsing a local XML file it doesn't update.
I am using C# and VS2010. Could someone guide me for a few steps or refer me to a book or coding tool that gets me over this hump. Also, please note that I chose to structure things way by imitating the paradigm used in the "How Do I: Build My First WPF Application" at windowsclient.net. If you think I'm going about it the wrong way, I would appreciate a pointer to a better tutorial.
Form XAML:
<Window ...
xmlns:vm="clr-namespace:MyProjectWPF.ViewModels">
<Grid>
<Grid.DataContext>
<vm:MyConfigurationViewModel />
</Grid.DataContext>
<TextBlock Name="textBlock4" Text="{Binding Path=Database}" />
</Grid>
MyConfigurationViewModel class definition:
class MyConfigurationViewModel : INotifyPropertyChanged
{
private string _Database;
public string Database
{
get { return _Database; }
set { _Database = value; OnPropertyChanged("Database"); }
}
public void LoadConfiguration()
{
XmlDocument myConfiguration = new XmlDocument();
myConfiguration.Load("myfile.xml");
XmlNode root = myConfiguration.DocumentElement;
Database = root["Database"].InnerText;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string Property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(Property));
}
And the codebehind my XAML form:
public partial class MyForm : Window
{
private ViewModels.myConfigurationViewModel mcvm
= new ViewModels.myConfigurationViewModel();
public MyForm()
{
mcvm.LoadConfiguration();
}
You have two instances of myConfigurationViewModel. One is created inside the XAML and the second one is created inside the form's codebehind. You are calling LoadConfiguration on the one in the code behind, which is never set as the form's DataContext.
Remove this from the XAML:
<Grid.DataContext>
<vm:MyConfigurationViewModel />
</Grid.DataContext>
and change the constructor to this:
public MyForm()
{
mcvm.LoadConfiguration();
DataContext = mcvm;
}
Can you try this XAML:
<Window ...
xmlns:vm="clr-namespace:MyProjectWPF.ViewModels">
<Grid>
<TextBlock Name="textBlock4" Text="{Binding Path=Database}" />
</Grid>
with this code:
public partial class MyForm : Window
{
private ViewModels.myConfigurationViewModel mcvm = new ViewModels.myConfigurationViewModel();
public MyForm()
{
mcvm.LoadConfiguration();
this.DataContext = mcvm;
}
[Update] Was wrong on the explanation, removed it.

Categories