Suppose I have a textbox in a WPF Page.
This textbox is bound to a static property which is declared in MainWindowViewModel. Also this static property implements INotifyPropertyChanged.
Here is the code for MainWindowViewModel :
namespace WpfApplication1.ViewModels
{
class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
cPerson = new Person();
SaveChangesCommand = new RelayCommand(SaveChanges);
MainWindowViewModel.StaticPropertyChanged += MainWindowViewModel_StaticPropertyChanged;
}
void MainWindowViewModel_StaticPropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
private static Person _cPerson;
public static Person cPerson
{
get
{
return _cPerson;
}
set
{
_cPerson = value;
OnStaticPropertyChanged("cPerson");
}
}
public ICommand SaveChangesCommand { get; set; }
private void SaveChanges(object obj)
{
//code for saving <-- Gives me values back. i.e. It works fine
using (SampleEntities db = new SampleEntities())
{
db.People.Add(cPerson);
db.SaveChanges();
}
//clear all the fields <-- Here it calls OnStaticPropertyChanged But Fields are not cleared
cPerson = new Person();
}
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
protected static void OnStaticPropertyChanged(string PropertyName)
{
EventHandler<PropertyChangedEventArgs> handler = StaticPropertyChanged;
if (handler != null)
{
handler(typeof(MainWindowViewModel), new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string PropertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
}
Here is the code for Page1ViewModel:
namespace WpfApplication1.ViewModels
{
class Page1ViewModel : INotifyPropertyChanged
{
public Page1ViewModel()
{
}
public Person CurrentPerson
{
get
{
return MainWindowViewModel.cPerson;
}
set
{
MainWindowViewModel.cPerson = value;
OnPropertyChanged("CurrentPerson");
}
}
protected virtual void OnPropertyChanged(string PropertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
I have mentioned my problem in the comments in above code. Still I would like to mention it here:
After I save changes to the database, I want to clear all the textboxes so that user can again fill in the new values.
When I say cPerson = new Person(); Event OnStaticPropertyChanged is raised and all looks fine. When I debug I get cPerson.Name = null and cPerson.Age = null. But when I see the textboxes the old values are not replaced with null.
I have set TargetNullValue = '' in my xaml.
If you like to see the xaml, then here it is:
<Page x:Class="WpfApplication1.Pages.Page1"
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"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1">
<Page.DataContext>
<vm:Page1ViewModel />
</Page.DataContext>
<Canvas DataContext="{Binding DataContext.CurrentPerson, RelativeSource={RelativeSource AncestorType={x:Type Page}}}">
<TextBlock Canvas.Left="10" TextWrapping="Wrap" Text=" Name :" Canvas.Top="44"/>
<TextBox Height="23" Canvas.Left="96" TextWrapping="Wrap"
Text="{Binding Name, UpdateSourceTrigger=LostFocus, TargetNullValue=''}" Canvas.Top="41" Width="120"/>
<TextBlock Canvas.Left="10" TextWrapping="Wrap" Text=" Age :" Canvas.Top="82"/>
<TextBox Height="23" Canvas.Left="96" TextWrapping="Wrap" Text="{Binding Age, UpdateSourceTrigger=LostFocus, TargetNullValue=''}" Width="120" Canvas.Top="80"/>
</Canvas>
</Page>
Try setting target null value as below.
TargetNullValue={x:Static System:String.Empty}
Also add this namespace to your XAML
xmlns:System=”clr-namespace:System;assembly=mscorlib”
Related
i would like to know how to bind a textbox to the property of another class. I can bind a property defined in the same class than the DataContext. But not to a property defined in a subclass. When i click on the button, only the label1 is updated. I do not know how to inform the system that Label2 is linked to the property Class1.Label2
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _Label1;
public string Label1
{
get { return _Label1; }
set
{
_Label1 = value;
OnPropertyChanged("Label1");
}
}
private Class1 objClass1;
public MainWindow()
{
InitializeComponent();
objClass1 = new Class1();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Label1 = "foo";
objClass1.Label2 = "bar";
}
}
The sub class object:
class Class1 : INotifyPropertyChanged
{
private string _Label2;
public string Label2
{
get { return _Label2; }
set
{
_Label2 = value;
OnPropertyChanged("Label2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
The xaml file :
<Window x:Class="WpfAppBinding2.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:local="clr-namespace:WpfAppBinding2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Label Content="{Binding Path=Label1}" HorizontalAlignment="Left" Margin="240,117,0,0" VerticalAlignment="Top" Width="71"/>
<Label Content="{Binding Path=Class1.Label2}" HorizontalAlignment="Left" Margin="240,158,0,0" VerticalAlignment="Top" Width="71"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="276,49,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
Thank you for your help.
I have a contentcontrol that is bound to an object (data property of DataContext in the example). Whenever the object referenced by data changes, I want to re-select the datatemplate. How can I do that?
<ContentControl Name="rootData" Content="{Binding data}"
ContentTemplateSelector="{StaticResource myTemplateSelector}"/>
In Case you are changing whole data type then your view should be as below, Only difference is removing key and using DataType for DataTemplate, this is called implicit data template
<Window x:Class="TextBindingFormatting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TextBindingFormatting"
xmlns:viewModels="clr-namespace:TextBindingFormatting.ViewModels"
Title="MainWindow" Height="350" Width="555">
<Window.Resources>
<local:MyTemplateSelector x:Key="MyTemplateSelector"></local:MyTemplateSelector>
<DataTemplate DataType="{x:Type viewModels:Student}">
<StackPanel Orientation="Horizontal">
<Label>Id</Label>
<TextBlock Text="{Binding Id}"></TextBlock>
<Label>Name</Label>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Parent}">
<StackPanel Orientation="Vertical">
<Label>Name</Label>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<ContentControl Content="{Binding Data}" >
</ContentControl>
<Button Content="Change DataTemplate" Click="ButtonBase_OnClick"></Button>
</StackPanel>
</Grid>
code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel() {Data = new Student{Id = 1, Name = "Student"}};
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as MainWindowViewModel;
vm.Data = new Parent() {Name = "This is parent"};
}
}
I have two classes as below
public class Student : INotifyPropertyChanged
{
private string _name;
private int _id;
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public string Name {
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
}
And another
public class Parent : INotifyPropertyChanged
{
private string _name;
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
}
If you have same data type and different values for Data then you can use DataTemplate Selector as below, Otherwise just use DataType attribute of DataTemplate and you don't even need datatemplate selector.
Below is sample code to select template every time you change the data.
MainWindow.xaml
<Window x:Class="TextBindingFormatting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TextBindingFormatting"
Title="MainWindow" Height="350" Width="555">
<Window.Resources>
<local:MyTemplateSelector x:Key="MyTemplateSelector"></local:MyTemplateSelector>
<DataTemplate x:Key="Template1">
<StackPanel Orientation="Horizontal">
<Label>Label 1</Label>
<Label>Label 2</Label>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Template2">
<StackPanel Orientation="Vertical">
<Label>Label 1</Label>
<Label>Label 2</Label>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<ContentControl Content="{Binding Data}" ContentTemplateSelector="{StaticResource MyTemplateSelector}"></ContentControl>
<Button Content="Change DataTemplate" Click="ButtonBase_OnClick"></Button>
</StackPanel>
</Grid>
Below is the Code Behind, Ideally button click should be handled using command, but for quick example I have implemented in code behind just to trigger change of data.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel() {Data = "1"};
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as MainWindowViewModel;
vm.Data = "2";
}
}
Below is the ViewModel for MainWindow
namespace TextBindingFormatting.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private string _data;
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public string Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged("Data");
}
}
}
}
DataTemplate Selector
namespace TextBindingFormatting
{
public class MyTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var element = container as FrameworkElement;
if (element == null || item == null)
return base.SelectTemplate(item, container);
if (item.ToString() == "1")
return element.FindResource("Template1") as DataTemplate;
if (item.ToString() == "2")
return element.FindResource("Template2") as DataTemplate;
return base.SelectTemplate(item, container);
}
}
}
Found a possible solution here from Rachel:
Change Data template dynamically
DataTemplateSelector wont trigger on PropertyChange but setting triggers does the job for me!
I am trying to bind a label to an Object.Object.Property and I don't get it run.
Here is my code:
XAML
<Window x:Class="MyApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyWindow" Height="1120" Width="800">
<Grid Name="MyGrid">
<StackPanel Orientation="Horizontal">
<Label FontWeight="Bold" FontSize="40" Content="{Binding MyDataObject/AnotherSubObject/MyProperty}"/>
</StackPanel>
</Grid>
</Window>
And the Code:
public partial class MyWindow : Window
{
public MySubObject MyDataObject { get; set; }
public MyWindow(MySubObject object)
{
InitializeComponent();
this.MyDataObject = object; // Contains MyDataObject.AnotherObject.MyProperty
DataContext = this;
}
}
And the code for MySubObject object looks like this:
public class MySubObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
AnotherObject _AnotherObject;
public MySubObject()
{
this._AnotherObject = new AnotherObject();
this._AnotherObject.Property = "Some Value";
}
public AnotherObject AnotherObject
{
get { return _AnotherObject; }
set { _AnotherObject = value; OnPropertyChanged("AnotherObject"); }
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I would be glad to get dome support for this case.
Use Dot(.) as binding property path separator not Forward slash(/)
<Label FontWeight="Bold" FontSize="40"
Content="{Binding MyDataObject.AnotherSubObject.MyProperty}"/>
I have the following class which implements INotifyPropertyChanged
updateNotify.cs
public class updateNotify : INotifyPropertyChanged
{
private DateTime? previousUpdate;
private DateTime? nextUpdate;
public event PropertyChangedEventHandler PropertyChanged;
public updateNotify()
{
}
public updateNotify(DateTime? value)
{
this.previousUpdate = value;
}
public DateTime? GASpreviousUpdate
{
get { return previousUpdate; }
set
{
previousUpdate = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("GASpreviousUpdate");
}
}
public DateTime? GASnextUpdate
{
get { return nextUpdate; }
set
{
nextUpdate = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("GASnextUpdate");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
MainWindow.xaml.cs
public partial class MainWindow
{
private updateNotify properties = new updateNotify();
'''
public void start_dispatcherTimer()
{
dispatcherTimer.Stop();
DateTime timeNow = DateTime.Now;
properties.GASpreviousUpdate = timeNow;
properties.GASnextUpdate = timeNow.AddMinutes((double)Properties.Settings.Default.GASInterval);
dispatcherTimer.Start();
}
}
then in XAML
<UserControl x:Class="ProjectXYZ.Content.MainData"
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:DesignWidth="300" Height="400" >
....
<StackPanel>
<Label Content="Previous Refresh" Target="{Binding ElementName=PreviousRefresh}"/>
<TextBox x:Name="PreviousRefresh" Width="140" Text="{Binding Path=GASpreviousUpdate, Mode=OneWay}"/>
</StackPanel>
<StackPanel>
<Label Content="Next Refresh" Target="{Binding ElementName=NextRefresh}"/>
<TextBox x:Name="NextRefresh" Width="140" Text="{Binding Path=GASnextUpdate, Mode=OneWay}"/>
</StackPanel>
</UserControl>
but the textboxes are not updating in the UI for Text="{Binding Path=GASpreviousUpdate, Mode=OneWay}" and Text="{Binding Path=GASnextUpdate, Mode=OneWay}"
I think I need to set the datacontext but not sure how to do this in the usercontrol XAML. Please can someone point me in the right direction?
try:
public partial class MainWindow
{
public MainWindow()
{
this.DataContext = this.properties; // <----
}
}
I wouldn't call the field properties
I'm trying to create a simple WPF Application using data binding.
The code seems fine, but my view is not updating when I'm updating my property.
Here's my XAML:
<Window x:Class="Calculator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:calculator="clr-namespace:Calculator"
Title="MainWindow" Height="350" Width="525"
Name="MainWindowName">
<Grid>
<Label Name="MyLabel" Background="LightGray" FontSize="17pt" HorizontalContentAlignment="Right" Margin="10,10,10,0" VerticalAlignment="Top" Height="40"
Content="{Binding Path=CalculatorOutput, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
Here's my code-behind:
namespace Calculator
{
public partial class MainWindow
{
public MainWindow()
{
DataContext = new CalculatorViewModel();
InitializeComponent();
}
}
}
Here's my view-model
namespace Calculator
{
public class CalculatorViewModel : INotifyPropertyChanged
{
private String _calculatorOutput;
private String CalculatorOutput
{
set
{
_calculatorOutput = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I'm can't see what I am missing here?? o.O
CalculatorOutput has no getter. How should the View get the value? The Property has to be public as well.
public String CalculatorOutput
{
get { return _calculatorOutput; }
set
{
_calculatorOutput = value;
NotifyPropertyChanged();
}
}