Binding to a property of an object in XAML not working - c#

I am new to WPF and data binding. So I am trying some things but now encountered a problem that defies everything I find in reference material.
I have a test program with a string TestString1 that is bound to the Text property of a TextBox tbTest1, that works.
And I have an object TestString2 from ClassTestString2 that contains one property Str. And I want to bind Str to the Text property of a TextBox tbTest2. So I use Text="{Binding Path=TestString2.Str}". According to all documentation you can drill down to a property of an object with the normal C# syntax. But it simply doesn't bind, it doesn't show when starting the program and also making changes in tbTest2 are not reflected in TestString2.Str.
When I use this.DataContext = TestString2; and Text="{Binding Path=Str}", it works but than TestString1 is not bound anymore.
I have the following simple piece of XAML:
<Window x:Class="WpfBindingStringOnly.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:WpfBindingStringOnly"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBox
x:Name="tbTest1"
Text="{Binding Path=TestString1}"
HorizontalAlignment="Left" Height="41"
Margin="124,47,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="250"/>
<TextBox
x:Name="tbTest2"
Text="{Binding Path=TestString2.Str}"
HorizontalAlignment="Left" Height="45"
Margin="124,126,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="250"/>
</Grid>
</Window>
And C# code behind:
using System;
using System.Windows;
using static WpfBindingStringOnly.MainWindow;
namespace WpfBindingStringOnly
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public string TestString1 { get; set; }
public class ClassTestString2
{
public string Str { get; set; }
public ClassTestString2(string s)
{
Str = s;
}
}
public ClassTestString2 TestString2;
public MainWindow()
{
TestString1 = "Hello1";
TestString2 = new("Hello2");
InitializeComponent();
this.DataContext = this;
}
}
}

Bindings work on properties, not fields.
Change your TestString2 member from
public ClassTestString2 TestString2; // This is a field.
to
public ClassTestString2 TestString2 { get; set; } // This is a property.

Related

Why binded List<string> to ListView textboxes doesn't save changes, despite twoway mode [duplicate]

I'm trying to write a user control that has an ItemsControl, the ItemsTemplate of which contains a TextBox that will allow for TwoWay binding. However, I must be making a mistake somewhere in my code, because the binding only appears to work as if Mode=OneWay. This is a pretty simplified excerpt from my project, but it still contains the problem:
<UserControl x:Class="ItemsControlTest.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=.}"
x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=TwoWay,
UpdateSourceTrigger=LostFocus,
Path=.}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Click="Button_Click"
Content="Click Here To Change Focus From ItemsControl" />
</StackPanel>
</Grid>
</UserControl>
Here's the code behind for the above control:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
namespace ItemsControlTest
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public ObservableCollection<string> MyCollection
{
get { return (ObservableCollection<string>)GetValue(MyCollectionProperty); }
set { SetValue(MyCollectionProperty, value); }
}
// Using a DependencyProperty as the backing store for MyCollection. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyCollectionProperty =
DependencyProperty.Register("MyCollection",
typeof(ObservableCollection<string>),
typeof(UserControl1),
new UIPropertyMetadata(new ObservableCollection<string>()));
public UserControl1()
{
for (int i = 0; i < 6; i++)
MyCollection.Add("String " + i.ToString());
InitializeComponent();
myItemsControl.DataContext = this.MyCollection;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Insert a string after the third element of MyCollection
MyCollection.Insert(3, "Inserted Item");
// Display contents of MyCollection in a MessageBox
string str = "";
foreach (string s in MyCollection)
str += s + Environment.NewLine;
MessageBox.Show(str);
}
}
}
And finally, here's the xaml for the main window:
<Window x:Class="ItemsControlTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:ItemsControlTest"
Title="Window1" Height="300" Width="300">
<Grid>
<src:UserControl1 />
</Grid>
</Window>
Well, that's everything. I'm not sure why editing the TextBox.Text properties in the window does not seem to update the source property for the binding in the code behind, namely MyCollection. Clicking on the button pretty much causes the problem to stare me in the face;) Please help me understand where I'm going wrong.
Thanx!
Andrew
Ok I believe what is causing this problem is that you are binding directly to a String . Strings are immutable in C# and thus when you change the text, it cannot change the underlying string in the ObservableCollection. What you can do to get around this problem is simply create a model class to hold the string data, and then bind the TextBox.Text to a property inside that class. Here is an example:
public partial class BindingToString : Window
{
public BindingToString()
{
MyCollection = new ObservableCollection<TestItem>();
for (int i = 0; i < 6; i++)
MyCollection.Add(new TestItem("String " + i.ToString()));
InitializeComponent();
myItemsControl.DataContext = this.MyCollection;
}
public ObservableCollection<TestItem> MyCollection
{
get;
set;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Display contents of MyCollection in a MessageBox
string str = "";
foreach (TestItem s in MyCollection)
str += s.Name + Environment.NewLine;
MessageBox.Show(str);
}
}
public class TestItem
{
public string Name
{
get;
set;
}
public TestItem(string name)
{
Name = name;
}
}
Notice that I changed your dependency property to a standard property- there is no reason to make the collection a dependency property. Besides that the only difference is the inclusion of the wrapper class TestItem to hold the string data.
<Window x:Class="TestWpfApplication.BindingToString"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BindingToString " Height="300" Width="300">
<Grid>
<StackPanel>
<ItemsControl ItemsSource="{Binding}"
x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Click="Button_Click"
Content="Click Here To Change Focus From ItemsControl" />
</StackPanel>
</Grid>
Now the TextBox is bound to the Name path on TestItem, and this binding works and modifies the collection as expected.

WPF UserControl DependencyProperty use in XAML or by binding

I want a universal UserControl where i can set a property either by setting its value in XAML directly or by binding it to some model property.
Just like TextBlock Text property works.
Right now i just have the bare simple UserControl, it has a single DependencyProperty TxT and a TextBlock Text property bound to it. No other code present.
If i set TxT in XAML on main window it wont work, binding works.
If i add PropertyChangedCallback to that DependencyProperty it works also in XAML.
So the question, is it mandatory to have PropertyChangedCallback for each property if i want to be able to set it directly in XAML?
This is not clear to me, most don't mention about it, but it also forces me to adding internal control names to change their value in PropertyChangedCallback.
The code is below.
Can it be done some other way?
MainWindow
<Window
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:WpfAppDpBare" xmlns:Model="clr-namespace:WpfAppDpBare.Model" x:Class="WpfAppDpBare.MainWindow"
Background="CadetBlue"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<Model:MainModel/>
</Window.DataContext>
<Grid>
<local:UserControlSample TxT="DIRECT TXT" HorizontalAlignment="Center" VerticalAlignment="Center" Height="125" Width="125" Margin="10,34,659,262"/>
<TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Direct" VerticalAlignment="Top" FontSize="14" FontWeight="Bold"/>
<TextBlock HorizontalAlignment="Left" Margin="203,10,0,0" TextWrapping="Wrap" Text="Binding" VerticalAlignment="Top" FontSize="14" FontWeight="Bold"/>
<local:UserControlSample DataContext="{Binding UCData}" HorizontalAlignment="Center" VerticalAlignment="Center" Height="125" Width="125" Margin="203,34,466,262"/>
</Grid>
public partial class MainWindow:Window {
public MainWindow() {
InitializeComponent();
}
}
UserControl
<UserControl x:Class="WpfAppDpBare.UserControlSample"
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:local="clr-namespace:WpfAppDpBare"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Background="White">
<Grid>
<TextBlock TextWrapping="Wrap" Text="{Binding TxT,FallbackValue=...,TargetNullValue=...}" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20" FontWeight="Bold"/>
</Grid>
public partial class UserControlSample:UserControl {
public UserControlSample() {
InitializeComponent();
}
public string TxT {
get { return (string)GetValue(TxTProperty); }
set { SetValue(TxTProperty, value); }
}
// Using a DependencyProperty as the backing store for TxT. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TxTProperty =
DependencyProperty.Register("TxT", typeof(string), typeof(UserControlSample), new PropertyMetadata());
}
Models
public class MainModel:ViewModelBase {
/// <summary>
/// The <see cref="UCData" /> property's name.
/// </summary>
public const string UCDataPropertyName = "UCData";
private UCModel uCModel = null;
/// <summary>
/// Sets and gets the UCData property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public UCModel UCData {
get {
return uCModel;
}
set {
if(uCModel == value) {
return;
}
uCModel = value;
RaisePropertyChanged(UCDataPropertyName);
}
}
public MainModel() {
UCData = new UCModel() { TxT = "BINDING TXT" };
}
}
public class UCModel:ViewModelBase {
/// <summary>
/// The <see cref="TxT" /> property's name.
/// </summary>
public const string TxTPropertyName = "TxT";
private string _TxT = null;
/// <summary>
/// Sets and gets the TxT property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string TxT {
get {
return _TxT;
}
set {
if(_TxT == value) {
return;
}
_TxT = value;
RaisePropertyChanged(TxTPropertyName);
}
}
}
Full bare project https://wetransfer.com/downloads/199f3db5d183e64cf9f20db4225d4c9820180702001102/f4f61b
As u can see in the project binding works, direct property text not.
I want it all contained in the usercontrol, so i either set usercontrol property value in xaml or bind to it, without another addition in the mainwindow xaml or code.
You are not binding the TextBlock's Text property to the TxT property of the UserControl.
Set the Binding's RelativeSource
<TextBlock Text="{Binding TxT,
RelativeSource={RelativeSource AncestorType=UserControl}, ...}" .../>
or assign a x:Name to the UserControl and use an ElementName Binding.
Then, instead of setting the UserControl's DataContext by
<local:UserControlSample DataContext="{Binding UCData}" .../>
bind its TxT property:
<local:UserControlSample TxT="{Binding UCData.TxT}" .../>
EDIT: In order to bind directly to the properties of the object in its DataContext, as intended with
<local:UserControlSample DataContext="{Binding UCData}" .../>
you do not need to declare any properties at all in the UserControl. Remove the TxT dependency property declaration, and bind the elements in the UserControl's XAML directly, as you already did:
<TextBlock Text="{Binding TxT, ...}"/>
Note however that this is not how UserControl usually work. Yours does now depend on a specific view model type, and can't be reused with other view models.

Binding model from a view wpf c#

I'm trying to build a DTO to store the software configuration, but I'm stuck because my view is not sending the data to my ViewModel and also to my DTO.
I need to transfer 2 textbox and 3 combobox to my DTO, but using this code the values are always empty.
My ViewModel:
public class ViewModelProcessamentoArquivo : ViewModelBase
{
private PesquisaConfiguracao pesquisaConfiguracao;
public PesquisaConfiguracao PesquisaConfiguracao
{
get { return pesquisaConfiguracao; }
set
{
pesquisaConfiguracao = value;
base.OnPropertyChanged("PesquisaConfiguracao");
}
}
}
My DTO/Model
public class PesquisaConfiguracao
{
public string ArquivoOrigem { get; set; }
public string ArquivoDestino { get; set; }
public string TipoPesquisa { get; set; }
public string PesquisaVeicular { get; set; }
public string PesquisaCrediticia { get; set; }
}
And my View is like this.
<TextBox Name="txtBuscarArquivoOrigem" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Height="30" Margin="10, 0" Text="{Binding PesquisaConfiguracao.ArquivoOrigem}" />
<TextBox x:Name="txtBuscarArquivoDestino" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" Height="30" Margin="10, 0" Text="{Binding PesquisaConfiguracao.ArquivoDestino}" IsEnabled="false" />
...
Do you guys know why it's happening? I've used something similar in my other project and worked just fine. Also if you have any other possibly way to fix this issue, please comment!
First UpdateSourceTrigger PropertyChanged, that way the target (view) will update your source object on every change:
<TextBox Name="txtBuscarArquivoOrigem" Height="30" Text="{Binding PesquisaConfiguracao.ArquivoOrigem, UpdateSourceTrigger=PropertyChanged}" />
Then implement in your source object the INotifyPropertyChange Interface on it's properties in order to update the view when the value has changed:
private string _arquivoOrigem;
public string ArquivoOrigem
{
get
{
return _arquivoOrigem;
}
set
{
_arquivoOrigem = value;
OnPropertyChanged("ArquivoOrigem");
}
}
Put a BreakPoint in the property setter and it will break there when you change the value in the view TextBox.
If it doesn't work for you probably forgot to set your DataContext to your ViewModel:
<Window x:Class="WpfApplication1.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"
DataContext="{StaticResource MainViewModel}">
Or did not initialize your source object:
public MainViewModel()
{
pesquisaConfiguracao = new PesquisaConfiguracao
{
ArquivoDestino = "aaa",
ArquivoOrigem = "bbb",
PesquisaCrediticia = "ccc",
PesquisaVeicular = "dddd",
TipoPesquisa = "eee"
};
}

Bind Data From Property to Textblock - MVVM Light and WPF

I have a textblock in WPF which is bound to a property in my ViewModel class. On click of a button I wish to modify the property and expect the same to be reflected in my textblock. I want all these to be done purely using MVVM (MVVMLight). I am using MMVM light and VS 2012.
Challenges- On button click the changes are not being reflected. Though the program execution is going inside the property , changes are not being made.
Please Help !!
Program- View:
<Window x:Class="MvvmLight1_Trail.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:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
Height="500"
Width="500"
Title="MVVM Light Application"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid x:Name="LayoutRoot">
<TextBlock FontSize="34"
Text="{Binding Path=MyText,UpdateSourceTrigger=Default, Mode=TwoWay}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" />
<Button Width="100" Height="100" Command="{Binding PressCommand}" Margin="198.985,277.537,193.014,92.462" Content="Press Me"/>
</Grid>
View Model
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using MvvmLight1_Trail.Model;
using System.ComponentModel;
using System.Threading;
namespace MvvmLight1_Trail.ViewModel
{
public class MainViewModel : ViewModelBase
{
public RelayCommand PressCommand { get; private set; }
Thread t;
private string _welcomeTitle = string.Empty;
public string MyText
{
get
{
return _welcomeTitle;
}
set
{
if (_welcomeTitle == value)
{
return;
}
_welcomeTitle = value;
RaisePropertyChanged(MyText);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
PressCommand = new RelayCommand(() => MyFunc());
myfunc();
}
private void MyFunc()
{
this.MyText = "Hi2";
}
private void myfunc()
{
this.MyText = "Hello";
this.MyText = "Hi";
}
}
}
Replace
RaisePropertyChanged(MyText);
to
RaisePropertyChanged("MyText");
PropertyChanged event should be raised on property name and not on property value.
Already answered by #Rohit Vats. You can also call RaisePropertyChanged like, RaisePropertyChanged( () => MyText) to ease renaming later.
Late to the game but:
in new C# 6 you can also use nameof like this:
RaisePropertyChanged(nameof(MyText))

How to bind class member in a collection

I want to bind a class member of an element I added to a collection to DisplayMemberPath. I bound a ObservableCollection to ComboBox.ItemSource and want to show the property name in the combobox's list which is a member of my class AxisBase.
Here is my code:
private ObservableCollection<AxisBase> axis { get; set; }
axis I use to hold elements of the following class
class AxisBase
{
...
public string name { get; set; }
...
}
This is how my xaml looks like
<ComboBox Name="comboBox_AchsenListe" DisplayMemberPath="{Binding ElementName=axis, Path=AxisBase.name}" ItemsSource="{Binding ElementName=_MainWindow, Path=axis}"</ComboBox>
Does anyone know how to bind name to DisplayMemberPath?
change DisplayMemberPath value
DisplayMemberPath="name"
SelectedValuePath="name"
and look at this question
I have created sample application for you
here the xaml
<Window x:Class="ComboBoxSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox
ItemsSource="{Binding Path=AxisBases}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
Height="23" HorizontalAlignment="Left" Margin="200,134,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" />
</Grid>
here is code behind
using System.Collections.ObjectModel;
using System.Windows;
namespace ComboBoxSample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AxisBases = new ObservableCollection<AxisBase>
{
new AxisBase {Name = "Firts"},
new AxisBase {Name = "Second"},
new AxisBase {Name = "Third"}
};
//Set the data context for use binding
DataContext = this;
}
public ObservableCollection<AxisBase> AxisBases { get; set; }
}
public class AxisBase
{
public string Name { get; set; }
}
}
It works OK and binding also in combo box appears 3 items.

Categories

Resources