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}"/>
Related
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.
Okay I've been wracking my brain a lot about this one, I'm missing something, I just can't figure out what. Ultimately I'm trying to set databinding so I can update values to be shown on the fly, but for the life of me, it's not working.
The XAML is:
<TextBox x:Name="textBox" HorizontalAlignment="Left"
Height="37" Margin="85,38,0,0" TextWrapping="Wrap"
Text="{Binding Path=TBBind}" VerticalAlignment="Top"
Width="121" />
Note that I have the {Binding Path=TBBind} set.
The code behind is:
using System.ComponentModel;
using System.Windows;
namespace Databinding_Practice_2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
TBBind = "test";
}
private string _tBBind;
public string TBBind
{
get { return _tBBind; }
set
{
if (value != _tBBind)
{
_tBBind = value;
OnPropertyChanged("TBBind");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
MessageBox.Show("OnPropertyChanged triggered");
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
Help me obi-w.... oh wait, help me anyone!
Assuming that you are trying to use the MVVM pattern (which stands for Model-View-ViewModel):
Your MainWindow is the View.
You should create another class to be the View Model, like this:
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
TBBind = "test";
}
private string _tBBind;
public string TBBind
{
get { return _tBBind; }
set
{
if (value != _tBBind)
{
_tBBind = value;
OnPropertyChanged("TBBind");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
MessageBox.Show("OnPropertyChanged triggered");
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Your MainWindow code behind will become like this after removing all ViewModel related stuff to the MainWindowViewModel class:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
Now, you should link the View with the ViewModel, there are many ways to do this. Here is one of them:
In the XAML of MainWindow, have the following inside the Window element:
<Window.DataContext>
<wpfApplication5:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="37" Margin="85,38,0,0" TextWrapping="Wrap" Text="{Binding TBBind}" VerticalAlignment="Top" Width="121" />
</Grid>
Please note that WpfApplication5 is the name of the namespace in my WPF project. This will probably be different in your case.
Try:
public MainWindow()
{
InitializeComponent();
DataContext = this;
TBBind = "test";
}
The difference here sets the critical DataContext property. This is the cornerstone of the MVVM pattern, which you are implementing here. You should consider separating the View Model responsibility into another class, and then setting the View's DataContext to an instance of that class, but the approach you have taken here works for simple cases.
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.";
}
}
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.
I need to bind a property to a label. i have written the following code:
xaml for the label is
<Label Canvas.Left="807.3" Canvas.Top="148.9" Height="33.567" x:Name="label2"
Width="98" FontFamily="Tw Cen MT" FontSize="24" FontWeight="Bold"
Foreground="#FFFEE3A4"
Content="{Binding Path=UserInformation.AccountBalance,Mode=OneWay}">
<Label.Background>
<ImageBrush />
</Label.Background>
</Label>
The class whcih have the AccountBalance
public class CustomerInformation : INotifyPropertyChanged
{
public CustomerInformation()
{
_Balance = 0.0;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public double AccountBalance
{
get { return _Balance; }
set
{
_wepaBalance = value;
FirePropertyChanged("AccountBalance");
}
}
protected void FirePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
datacontext is set as below
this.LayoutRoot.DataContext = this;
behind the xaml.cs the following code is written to access the UserInfo which is a global object
public CustomerInformation UserInformation
{
get
{
return Globalobjs._Object.UserInfo;
}
}
xamls.cs is derived from Window only.
The problem is PropertyChangedEventHandler of INotifyPropertyChanged is always null when called .
Can any 1 please help me on this issue?
this.LayoutRoot.DataContext = this;
This is the Window, yet you are setting the Window instance as the DataContext. Set the DataContext to the UserInformation.
this.LayoutRoot.DataContext = Globalobjs._Object.UserInfo;
Does the datacontext that you are binding to implement INotifyPropertyChanged?
If this is not an MVVM patterned project, ensure that the class that contains the property that you are binding to implements that interface, and be sure to call the delegate for the event when you change the property (e.g. OnPropertyChanged("MyProperty"))
If it is an MVVM project and you are not using a framework, it is best to derive all of your ViewModels from a ViewModel base that implements INotifyPropertyChanged.
You are binding to the Windows's DataContext. But the Windows DataContext is not the same as the Windows's code behind, where you have UserInformation property defined. To access a property defined in your Window's code behind, you have to set your Window's Name property, then use the following binding instead:
Content="{Binding ElementName=YourWindowName, Path=UserInformation.AccountBalance,Mode=OneWay}"