DataBinding to a TextBox not working - c#

I am having a difficult time getting my WPF to properly use Databinding. In the XAML I have a the following:
....
<TextBox Name="txt_FirstName" Text="{Binding Path=currentApplication.FirstName, UpdateSourceTrigger=PropertyChanged}" />
....
I have in the following CS code:
namespace WPF1
{
public partial class MainWindow : Window
{
personalApp currentApplication = new personalApp ();
public MainWindow()
{
InitializeComponent();
}
}
}
That references the following two classes:
class personalApp : INotifyPropertyChanged
{
private Person person = new Person();
public string FirstName
{
get { return person.FirstName; }
set
{
person.FirstName = value;
this.OnPropertyChanged("FirstName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
class Person
{
private string firstName = "";
get { return firstName; }
set { FirstName = value; }
}
I pause it in the code and step through to check, but when I update the txt_FirstName in the application, it never seems to set the firstName Object.
Where am I going wrong?

You need to update your XAML binding, and set the DataContext of the Window using the TextBox.
namespace WPF1
{
public partial class MainWindow : Window
{
personalApp currentApplication = new personalApp ();
public MainWindow()
{
InitializeComponent();
this.DataContext = currentApplication;
}
}
}
Updating the XAML:
<TextBox Name="txt_FirstName" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />

I have corrected the code.
For text box:
<TextBox Name="txt_FirstName" Height="30" Background="Beige"
Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />
C# Code
namespace Wpf1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new personalApp();
}
}
internal class personalApp : INotifyPropertyChanged
{
private Person person = new Person();
public string FirstName
{
get { return person.FirstName; }
set
{
person.FirstName = value;
this.OnPropertyChanged("FirstName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
internal class Person
{
public string FirstName { get; set; }
}
}

public MainWindow()
{
DataContext = this;
InitializeComponent();
}
or if you don't want to assign data context to yourself (window), as you might have other datacontext coming into the window, you can add this in xaml:
give your window a name:
<Window .... x:Name="this"...
then
<TextBox Name="txt_FirstName" Text="{Binding ElementName=this,
Path=currentApplication.FirstName/>

What you mean by "update the txt_FirstName in the application" ?
If you set directly the value of the textbox then you should try to set the value of currentApplication instead of the textbox value

Related

WPF DataGrid Binding property not found

I have just started using WPF and I am facing a problem displaying data to a DataGrid.
WPF (Views\ResultsPage.xaml):
<DataGrid x:Name="dgResults"
Margin="2"
Style="{DynamicResource AzureDataGrid}"
ItemsSource="{Binding SampleData}"
AutoGenerateColumns="False"
RenderOptions.ClearTypeHint="Enabled"
TextOptions.TextFormattingMode="Display">
<DataGrid.Columns>
<DataGridTextColumn Header="Well" Binding="{Binding WellID}"/>
..
C# (Data\SampleData.cs):
public class : INotifyPropertyChanged
{
private string _wellID;
public string WellID
{
get
{
return _wellID;
}
set
{
_wellID = value;
NotifyPropertyChanged("WellID");
}
}
...
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I get this error: System.Windows.Data Error: 40 : BindingExpression path error: 'SampleData'
SampleData is supposed to be a property of the DataContext of the DataGrid (or a parent element) that returns an IEnumerable<SampleData>. Try this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//set the DataContext of the window itself or the DataGrid
dgResults.DataContext = new ViewModel();
}
}
public class ViewModel
{
public ViewModel()
{
SampleData = new List<SampleData>();
SampleData.Add(new SampleData() { WellID = "1" });
SampleData.Add(new SampleData() { WellID = "2" });
}
public List<SampleData> SampleData { get; }
}

Create a ViewModel with sub ViewModel

Is there a proper way to create a C#/WPF ViewModel containing subViewModel ?
Objective is:
I have a MainWindow. That window is use to read/create images. There is a button on that windows who switch between 2 UserControl one with IHM used to read image, the other one used to create.
The MainWindow has a MainWindowViewModel with :
command switch
image length
application parameters
I want that both UserControls can acces to MainWindowViewModel field/properties and have they own commands.
Construction will be something like this:
public partial class ReadUserControl : UserControl
{
public ReadUserControl()
{
InitializeComponent();
DataContext = MainViewModel.ReadViewModel;
}
}
public partial class CreateUserControl : UserControl
{
public CreateUserControl()
{
InitializeComponent();
DataContext = MainViewModel.CreateViewModel;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = MainViewModel;
}
}
For example, if a MainViewModel contain a field ImageWidth setting ImageWidth in CreateUserControl change the value for ReadUserControl.
I hope to have been clear, I don't know how design my MainViewModel to achieve this result
EDIT1:
I've created the MainWindowViewModel as a Singleton but i'm still unable to get MainViewModel.CreateViewModel and MainViewModel.ReadViewModel
public class MainWindowViewModel : ViewModelBase
{
private static MainWindowViewModel _instance = null;
public static MainWindowViewModel Instance
{
get
{
if (_instance == null)
_instance = new MainWindowViewModel();
return _instance;
}
}
private MainWindowViewModel()
: base()
{
}
#region CreateViewModel
/* How to create ? */
#endregion
#region ReadViewModel
/* How to create ? */
#endregion
}
Your example will work. At least if you have made your MainViewModel a Singleton.
A more professional approach might be an Constructor-Injection like this.
public partial class ReadUserControl : UserControl
{
public ReadUserControl(MainViewModel vm)
{
InitializeComponent();
DataContext = vm.ReadViewModel;
}
}
With such DependencyInjections you can achieve a higher level of abstraction, since your UserControls can be generalized. (They will all have the same Constructor)
On the other hand, you give every such UserControl the ability, to manipulate the MainViewModel, not aware of side-effects.
In your special case, it would be more safe, to pass only the needed parameters to the UserControl, instead of giving them a bunch of informations, they will never need.
public partial class ReadUserControl : UserControl
{
public ReadUserControl(Icommand command, int imageLength, AppParams appParams)
{
InitializeComponent();
...
// Do with your Constructorparameters what ever you have to
}
}
Edit:
Here a small, dumb implementation of how it could be done:
Code
public class MainViewModel : INotifyPropertyChanged {
private INotifyPropertyChanged _selectedViewModel;
public MainViewModel() {
var cmd = new RelayCommand(x => {
MessageBox.Show("HelloWorld");
}, x => true);
this.RVM = new ReadViewModel(cmd);
this.WVM = new WriteViewModel(cmd);
this.SelectedViewModel = WVM;
}
private ICommand _switchViewModelCommand;
public ICommand SwitchViewModelCommand => this._switchViewModelCommand ?? (this._switchViewModelCommand = new RelayCommand(x => {
if (this.SelectedViewModel == RVM) {
this.SelectedViewModel = WVM;
return;
}
this.SelectedViewModel = RVM;
}));
public INotifyPropertyChanged SelectedViewModel {
get {
return this._selectedViewModel;
}
set {
if (Equals(value, this._selectedViewModel))
return;
this._selectedViewModel = value;
this.OnPropertyChanged();
}
}
public ReadViewModel RVM {
get; set;
}
public WriteViewModel WVM {
get; set;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ReadViewModel : INotifyPropertyChanged {
public ReadViewModel(ICommand sayHelloCommand) {
this.HelloCommand = sayHelloCommand;
}
public ICommand HelloCommand {
get;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class WriteViewModel : INotifyPropertyChanged {
public WriteViewModel(ICommand sayHelloCommand) {
this.HelloCommand = sayHelloCommand;
}
public ICommand HelloCommand {
get;
}
public ICommand HelloMoonCommand => new RelayCommand(x => { MessageBox.Show("Hello Moon"); });
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid Height="200">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ContentControl Content="{Binding SelectedViewModel, UpdateSourceTrigger=PropertyChanged}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:ReadViewModel}">
<StackPanel>
<Button Content="Say Hello world" Command="{Binding HelloCommand}"></Button>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:WriteViewModel}">
<StackPanel>
<Button Content="Say Hello world" Command="{Binding HelloCommand}"></Button>
<Button Content="Say Hello Moon" Command="{Binding HelloMoonCommand}"></Button>
</StackPanel>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
<Button Content="Switch VM" Command="{Binding SwitchViewModelCommand}" Grid.Row="1"/>
</Grid>
You can pass in the MainViewModel as DataContext for your user control and set the data context of elements as Read/Create model
something like
<Grid> <!--using MainWindowViewModel as data context-->
<Grid DataContext="{Binding Path=CreateViewModel}"> <!--using CreateViewModel as data context-->
.....
</Grid>
<Grid>

How to bind in xaml to a textbox from a different class

I can figure out how to bind a property to a textbox in the codebehind, but with my current application I need to bind to a property from a different class. Here's a simplified version of what I have:
<Window x:Class="Project1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox x:Name="Textbox1" Text="{Binding Class1.Class2.TextToBind, Mode=TwoWay}" Height="20" Width="75" Background="#FFE5E5E5"/>
</Grid>
Codebehind:
namespace Project1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
Class1 = new Class1();
}
public Class1 Class1 { get; set; }
}
}
Class1:
namespace Project1
{
public class Class1
{
public Class1()
{
Class2 = new Class2();
}
public Class2 Class2 { get; set; }
}
}
Final class:
namespace Project1
{
public class Class2
{
public Class2()
{
}
private string textToBind;
public string TextToBind { get { return textToBind; } set { SetProperty(ref textToBind, value); } }
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
}
You have to set DataContext for your TextBox or for your Window
namespace Project1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
Class1 = new Class1();
Class1.Class2.TextToBind = "Test";
this.DataContext = this;
}
public Class1 Class1 { get; set; }
}
}
Also you need to inherit Class2 from INotifyPropertyChanged:
public class Class2 : INotifyPropertyChanged
{
public Class2()
{
}
private string textToBind;
public string TextToBind { get { return textToBind; } set { SetProperty(ref textToBind, value); } }
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
If you want to bind your textbox to a property from a class other than the datacontext of the window, you need to set it explicitly
I tried this code and it worked
<Window x:Class="Project1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
<!-- first add namespace of your project-->
xmlns:local="clr-namespace:Project1"
Title="Window1" Height="300" Width="300">
<!--second define your data context class as resource-->
<Window.Resources >
<local:Class2 x:Key="class2"></local:Class2>
</Window.Resources>
<Grid>
<TextBox x:Name="Textbox1" Text="{Binding TextToBind, Mode=TwoWay}" Height="20" Width="75" Background="#FFE5E5E5">
<!--third set the data context of the textbox Explicitly-->
<TextBox.DataContext>
<StaticResourceExtension ResourceKey="class2"/>
</TextBox.DataContext>
</TextBox>
</Grid>
</Window>
Please note : if you are going to set the property TextToBind programmatically and you want your UI to show the result, you have to implement INotifyPropertyChanged.

Notify Property Changed not working

this my xml code
<TextBlock Grid.Column="0" Tag="{Binding id,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Text="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
this is my model
public string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; RaisePropertyChanged("Name"); }
}
when i set value to these two propertie ie. to id and Name
but its not notifying to Name ...
Simple Databinding Example with Updates. You can use this as a reference to get you started :)
public partial class MainWindow : Window, INotifyPropertyChanged
{
// implement the INotify
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _mytext;
public String MyText
{
get { return _mytext; }
set { _mytext = value; NotifyPropertyChanged("MyText"); }
}
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this; // set the datacontext to itself :)
MyText = "Change Me";
}
}
<TextBlock Text="{Binding MyText}" Foreground="White" Background="Black"></TextBlock>

INotifyPropertyChanged doesn't work

I have a simple object (which is globally initiated in App.xaml.cs):
public class now_playing : INotifyPropertyChanged
{
// notify
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string p)
{
Debug.WriteLine(p + ": notify propertychanged");
PropertyChangedEventHandler handler = PropertyChanged;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
// artist
public string artist
{
get
{
return _artist;
}
set
{
_artist = value;
NotifyPropertyChanged("artist");
}
}
private string _artist;
// album
public string album
{
get
{
return _album;
}
set
{
_album = value;
NotifyPropertyChanged("album");
}
}
private string _album;
// track title
public string tracktitle
{
get
{
return _tracktitle;
}
set
{
_tracktitle = value;
NotifyPropertyChanged("tracktitle");
}
}
private string _tracktitle;
}
Whenever I change the values, the class does notify (I see the debug).
So I guess the problems lies in my XAML or the code behind.
Page code:
public sealed partial class nowplaying : Page
{
// artistdata
public string artist { get { return App.nowplaying.artist; } }
// albumdata
public string album { get { return App.nowplaying.album; } }
// trackdata
public string tracktitle { get { return App.nowplaying.tracktitle; } }
public nowplaying()
{
this.InitializeComponent();
this.DataContext = this;
}
}
XAML:
<Grid Margin="50">
<TextBlock Text="{Binding tracktitle}" Foreground="White" FontSize="40"/>
<TextBlock Foreground="#dcdcdc" FontSize="20" Margin="0,50,0,0">
<Run Text="{Binding artist}"/>
<Run Text=" - "/>
<Run Text="{Binding album}"/>
</TextBlock>
</Grid>
Why does the UI not update when I change values?
Stack trace:
Music.exe!Music.App.InitializeComponent.AnonymousMethod__6(object sender = {Music.App}, Windows.UI.Xaml.UnhandledExceptionEventArgs e = {Windows.UI.Xaml.UnhandledExceptionEventArgs}) Line 50 C#
Music.exe!play_music.MessageReceivedFromBackground(object sender = null, Windows.Media.Playback.MediaPlayerDataReceivedEventArgs e = {Windows.Media.Playback.MediaPlayerDataReceivedEventArgs}) Line 57 C#
UPDATE: problem solved! I had to use a dispatcher when calling the propertychanged event:
CoreDispatcher dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
if (PropertyChanged != null)
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
this.PropertyChanged(this, new PropertyChangedEventArgs(p));
});
}
You "loose" the change notification in the properties in the Page as these properties do not have any change notifiaction.
Try using now_playing directly:
public sealed partial class nowplaying : Page
{
public now_playing NowPlaying { get { return App.nowplaying; } }
public nowplaying()
{
this.InitializeComponent();
this.DataContext = this;
}
}
and
<Run Text="{Binding NowPlaying.artist}"/>
Otherwise you need to implement INotifiyPropertyChanged in nowplaying and forward the events from now_playing.
You actually binding to artist, album and tracktitle Of nowplaying class which does implement INotifyPropertyChanged

Categories