Simple XAML binding in a textblock text - c#

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.";
}
}

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.

Set Datasource of Textbox to Inner Class in xaml

I have a wpf gui page with a textbox that is bound to a property of an innerclass in my window. I have defined the textbox to be bound like so:
XAML:
<TextBox Name="shhh" Text="{Binding Path=derpDerp, Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
CodeBehind:
namespace ...
{
public partial class MainWindow : Window
{
innerclass definition....
public Herp derp;
public MainWindow()
{
...
derp = new Herp();
shhh.DataContext = derp;
...
}
{code that changes derp.derpDerp}
}
}
InnerClass:
public class Herp : INotifyPropertyChanged
{
private secret = "";
public event PropertyChangedEventHandler PropertyChanged;
public Herp(string derp)
{
secret = derp;
}
public string derpDerp
{
get{ return secret; }
set{ secret = value; onPropertyChanged("derpDerp"); }
}
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
What I was wondering is if I can declare the source of the textbox in the xaml. I have seen many examples that say to set the textbox to the datacontext of the parent like the window or a container around the textbox. However i don't find that very intuitive if only 1 control needs the data. It would make sense if I have several textboxes and a stackpanel with a datacontext.
In my implementation I create the object in code and set the datacontext to just the textbox. Is there an equivalent xaml solution?
Something like:
<TextBox Source="something" Path=derpDerp..../>
without setting a datacontext to a container or the window. Also, I didn't know how to set the datacontext of the window to my property correctly because it's an inner class with a namespace of the namespace.the window class or something like that.
What would be the proper way of just giving the textbox a datasource or if not possible how do I reference the innerclass and set the source to the window?
Yes, you can create an instance of a class and set it as DataContext on any control in XAML. The general solution would be like this:
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<Window.Resources>
<local:Herp DerpDerp="This is Derp!!" x:Key="derp"/>
</Window.Resources>
<Grid>
<TextBox Text="{Binding Source={StaticResource derp}, Path=DerpDerp}"/>
</Grid>
Notice that I defined a new xmlns object called local, which points to the namespace in which the class I'm trying to create resides (in this case, it's Herp).Then, in my Window.Resources, I create an instance of Herp, and set a value for the DerpDerp property. Also notice that I gave the class a key, which is necessary in order for the TextBox to find it and bind to it.
Big note: In order for you to be able to create an instace of a class in XAML, the class needs to have a parameter-less constructor! So I changed Herp a little bit:
namespace MyProject
{
public class Herp : INotifyPropertyChanged
{
private string m_derp;
public Herp()
{
}
public string DerpDerp
{
get { return m_derp; }
set { m_derp = value; OnPropertyChanged("DerpDerp"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Finally, in your TextBox, you use the Source element in your binding to bind to the object:
<TextBox Text="{Binding Source={StaticResource derp}, Path=DerpDerp}"/>

Listbox Trouble with Binding to ItemSource using a ObservableCollection

I am having trouble binding to the ItemsSource of a List box control. I would like to be able to add text lines to the List box when the user preforms certain actions.
The SystemControls.xmal Code:
<ListBox Grid.Column="4" Grid.Row="1" Grid.RowSpan="9" ItemsSource="{Binding ListBoxInput}" Height="165" HorizontalAlignment="Left" Name="listBox1" VerticalAlignment="Top" Width="250" ></ListBox>
The SystemControls.xmal.cs code snippet:
public partial class SystemControls : UserControl, ISystemControls
{
IDriver _Driver;
ISystemControls_VM _VM;
public SystemControls(IDriver InDriver, ISystemControls_VM InVM)
{
_VM = InVM;
_Driver = InDriver;
DataContext = new SystemControls_VM(_Driver);
InitializeComponent();
}
The SystemControls_VM.cs This should be where the heart of the problem is. I have gotten it to work in the constructor, when i try to add lines later in the code, for example when a user press a button, it does nothing:
public class SystemControls_VM:ViewModelBase, ISystemControls_VM
{
IDriver _Driver;
public ObservableCollection<string> _ListBoxInput = new ObservableCollection<string>();
public SystemControls_VM(IDriver InDriver)
{
_Driver = InDriver;
ListBoxInput.Add("test");//Works here
}
public ObservableCollection<string> ListBoxInput
{
get
{
return _ListBoxInput;
}
set
{
_ListBoxInput = value;
//OnPropertyChanged("ListBoxInput");
}
}
public void OnButtonClickGetNextError()
{
ListBoxInput.Add("NextErrorClicked");//Does not work here
}
public void OnButtonClickClear()
{
ListBoxInput.Clear();//Or Here
}
Also in case it's needed the OnPropertyChangedEventHandler:
namespace XXX.BaseClasses.BaseViewModels
{
/// <summary>
/// Provides common functionality for ViewModel classes
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate{};
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
1) Your public property is called _ListBoxInput but you're binding to ListBoxInput (no underscore). Make _ListBoxInput private.
2) Because the collection is already observable, you don't need the OnPropertyChanged for your listbox to update.
3) It looks like something might be off with the way you're managing your public vs private ListBoxInput collections. You're calling .Add on your public property (which will immediately raise an event on the observable collection) but then you'll end up adding it to the private collection as well, and then you're calling PropertyChanged on the public property. It's confusing: try my code below and see how it works. (Note in your constructor you add to _ListBoxInput but in your button click event you add to ListBoxInput.)
4) Try adding this.DataContext = this in your constructor
public partial class MainWindow : Window {
public ObservableCollection<string> ListBoxInput { get; private set; }
public MainWindow() {
InitializeComponent();
this.ListBoxInput = new ObservableCollection<string>();
this.DataContext = this;
}
private void AddListBoxEntry_Click(object sender, RoutedEventArgs e) {
this.ListBoxInput.Add("Hello " + DateTime.Now.ToString());
}
}
and in the xaml, take a look at the binding Mode.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding ListBoxInput, Mode=OneWay}"
Height="165" HorizontalAlignment="Left"
Name="listBox1" VerticalAlignment="Top" Width="250" />
<Button Grid.Column="1" Grid.Row="0" Name="AddListBoxEntry"
Margin="0,0,0,158" Click="AddListBoxEntry_Click" >
<TextBlock>Add</TextBlock>
</Button>
</Grid>
5) On a separate note, here's another way you could do your INotifyPropertyChanged (I find this cleaner)
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate{};
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
So got the answer from another source, but figured I would post it here for referance.
So what was happening was that I was setting the data context to one instance of SystemControls_VM while my _VM referance which was handling the button click was going to another instance of SystemControls_VM. That was also why it looked like the button click was working and the List was being populated but no data was getting to the Control itself
I changed the following section of code and it works:
public partial class SystemControls : UserControl, ISystemControls
{
IDriver _Driver;
SystemControls_VM _VM;
public SystemControls(IDriver InDriver, SystemControls_VM InVM)
{
_VM = InVM;
_Driver = InDriver;
DataContext = InVM;//new SystemControls_VM(_Driver);
InitializeComponent();
}

I want to bind a class' properties to controls in my user control

I'm struggling to find a solution to my binding issue.
I have a User Control, which has a button for calling a separate window, in which the user can select an object. Upon selecting this object the window closes and an object in the user control has it's properties updated according to the selection.
The properties of this object are bound to controls in the user control, but when I update the properties in the object, the values in the controls are not updated (I hope that makes sense).
here is a slimmed down code behind:
public partial class DrawingInsertControl : UserControl
{
private MailAttachment Attachment { get; set; }
public DrawingInsertControl(MailAttachment pAttachment)
{
Attachment = pAttachment;
InitializeComponent();
this.DataContext = Attachment;
}
private void btnViewRegister_Click(object sender, RoutedEventArgs e)
{
DocumentRegisterWindow win = new DocumentRegisterWindow();
win.ShowDialog();
if (win.SelectedDrawing != null)
{
Attachment.DwgNo = win.SelectedDrawing.DwgNo;
Attachment.DwgTitle = win.SelectedDrawing.Title;
}
}
}
and the xaml:
<UserControl x:Class="DrawingInsertControl"
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="310" d:DesignWidth="800" >
<Border BorderBrush="Black" BorderThickness="2" Margin="10">
<Grid>
...
<TextBox Grid.Column="1" Name="txtDocNo" Text="{Binding DwgNo}" />
and finally the attached object which is in a separate module:
Public Class MailAttachment
Public Property DwgNo As String
End Class
I've omitted namespaces and other stuff I didn't see as relevant.
Thanks in advance for any help.
Your MailAttachment class should implement INotifyPropertyChanged Interface:
public class MailAttachment: INotifyPropertyChanged
{
private string dwgNo;
public string DwgNo{
get { return dwgNo; }
set
{
dwgNo=value;
// Call NotifyPropertyChanged when the property is updated
NotifyPropertyChanged("DwgNo");
}
}
// Declare the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
// NotifyPropertyChanged will raise the PropertyChanged event passing the
// source property that is being updated.
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This will force your control to observe PropertyChanged event. So your control can be notified about changes.
The code I provided is on C#, but, I hope you can translate it to VB.Net.

Dependency Property vs INotifyPropertyChanged in ViewModel for Windows 8 application

I have created blank C#/XAML Windows 8 application. Add simple XAML code:
<Page
x:Class="Blank.MainPage"
IsTabStop="false"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
Margin="0,150"
HorizontalAlignment="Center">
<TextBlock
x:Name="xTitle"
Text="{Binding Title, Mode=TwoWay}"/>
<Button Content="Click me!" Click="OnClick" />
</StackPanel>
</Grid>
</Page>
And the simple code in C# part:
public sealed partial class MainPage
{
private readonly ViewModel m_viewModel;
public MainPage()
{
InitializeComponent();
m_viewModel = new ViewModel
{
Title = "Test1"
};
DataContext = m_viewModel;
}
private void OnClick(object sender, RoutedEventArgs e)
{
m_viewModel.Title = "Test2";
}
}
Now I want to implement ViewModel. I have two way:
Use Dependency Property
Implement INotifyPropertyChanged
For first approach it is:
public class ViewModel : DependencyObject
{
public string Title
{
get
{
return (string)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
}
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string)
, typeof(ViewModel)
, new PropertyMetadata(string.Empty));
}
For second it is:
public class ViewModel : INotifyPropertyChanged
{
private string m_title;
public string Title
{
get
{
return m_title;
}
set
{
m_title = value;
OnPropertyChanged("Title");
}
}
protected void OnPropertyChanged(string name)
{
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I prefer the first way, because it allows use coerce (Silverlight for web and for WP7 doesn't have coerce functionality.. WinRT too.. but I'm still looking and hope) and looks more natural for me. But unfortunately, it works as OneTime for the first approach.
Could anybody explain to me why MS abandon using Dependency Property for implementing view model?
You should not be using a DependencyProperty in your ViewModel - you should only use them in your controls. You will never want to bind one ViewModel to another, also ViewModels do not need to persist their values nor provide default values, nor provide property metadata.
You should only use INotifyPropertyChanged in your ViewModels.

Categories