Databinding textbox read from property - c#

I want to fill the text property from a textbox using binding. (My first try with binding).
I have this:
public string TestProperty { get; set; }
private void Window_Loaded(object sender, RoutedEventArgs e)
{
TestProperty = 'Test';
}
in xaml:
<TextBox x:Name="TextBox_Test" HorizontalAlignment="Left" Height="23" Margin="49,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="288" Text="{Binding ElementName=TextBox_Test, Path=TestProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The property is filled when the form loads. The textbox keeps empty. How can i fill the textbox?

You've got to fix a couple of things before this will work.
First, you're binding expression isn't quite right. You specified that the binding source is the TextBox by using the ElementName. That's not correct. Your source should actually be the Window since that's where your property exists. So, give your Window a name, and change ElementName to that Windowname. For example ..
<TextBox x:Name="TextBox_Test" HorizontalAlignment="Left" Height="23" Margin="49,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="288"
Text="{Binding ElementName=Window_Test, Path=TestProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Second, your Windowneeds to implement INotifyPropertyChangedfor changes on the source to be reflected on the target.
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _testProperty;
public string TestProperty
{
get { return _testProperty; }
set
{
_testProperty = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TestProperty"));
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
TestProperty = "Test";
}
public MainWindow()
{
InitializeComponent();
}
}
Note that I modified the class to implement the interface, and I raise the event in the property setter.
With these changes, your binding will work. I should note that this type of binding is a bit unusual. In cases like this, it's more common for the Window to use a DependencyProperty, or for you to bind to a non-UI class (A view model, for example). You may want to look into both as you learn about binding.
Dependency Properties
MVVM Pattern

Related

WPF PropertyChanged event not firing/updating textbox

I'm still relatively new to Data Binding in wpf, but despite plunging through all the articles and posts here and elsewhere about what could be wrong, I still have not found a solution. The code below is the prevalent information parsed out of my larger files.
I have made sure everything else is working, including adding a property to retrieve the protected parameter options to ensure options.FullPath is in fact getting set/changed on the Browse button's Click event. I attempted to subscribe to the PropertyChanged event in the main window with the line test.PropertyChanged += ShowMessage;, ShowMessage being a method that triggers a MessageBox with text in it. I tried multiple variations on the OnPropertyChanged method from hardcoding it within the calling method to what is displayed here. I even tried setting options to a default value of "" just in case it was being weird about that. No luck on anything, and I have no way to acquire C#6 at the moment, so it may very well be that what I have works with the right language updates, but I just can't tell since it doesn't trigger.
Any help or insight would be greatly appreciated!
EDIT: All of the below code is house within the same namespace.
Object Class:
public class EEOptionSet: INotifyPropertyChanged
{
public EEOptionSet()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private string _fullPath;
public string FullPath
{
get { return _fullPath; }
set
{
if (value != _fullPath)
{
_fullPath = value;
OnPropertyChanged();
}
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
Main window's code behind:
public partial class window : Window
{
protected EEOptionSet options = new EEOptionSet();
private void BrowseFiles(object sender, RoutedEventArgs e)
{
options.FullPath = "Test";
}
}
Textbox and Button instances in the xaml of my main window (extraneous properties like Grid placement, Alignment, etc removed for brevity):
<TextBox x:Name="FullPathText" Text="{Binding (options.FullPath), Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" Focusable="False"/>
<uc:ButtonExt x:Name="Browse" Content="..." Click="BrowseFiles"/>
NOTE: I have also tried:
Text="{Binding options.FullPath, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Path=options.FullPath, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Path=(_currentOptionSet.FullPath), Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
As well as without the IsReadOnly and Focusable properties.
You cannot bind to a protected field.
Set the DataContext of the window to your field:
public partial class window : Window
{
protected OptionSet options = new OptionSet();
public window()
{
InitializeComponent();
DataContext = options;
}
private void BrowseFiles(object sender, RoutedEventArgs e)
{
options.FullPath = "Test";
}
}
...and remove "options" from the binding path(s) in the XAML markup:
Text="{Binding FullPath, UpdateSourceTrigger=PropertyChanged}"
Alternatively, make options a public property of the window and set the DataContext the the window itself:
public partial class window : Window
{
public OptionSet options { get; private set; }
public window()
{
InitializeComponent();
options = = new OptionSet();
DataContext = this;
}
private void BrowseFiles(object sender, RoutedEventArgs e)
{
options.FullPath = "Test";
}
}
Then you should keep the binding path as-is:
Text="{Binding options.FullPath, UpdateSourceTrigger=PropertyChanged}"
PropertyPath (this is the type of the Binding.Path property) can only be set by the path expression to the public property of the source. And your variable options is a protected field.
If the source is not explicitly specified in the Binding (there are three ways of setting it: Source, ElementName and RelativeSource), then the Data Context of the element in which the binding is set is used for the source. You did not specify the source in any of these four ways.
An example of setting the Data Context and its use.
Written from assumption:
the EEOptionSet and the OptionSet classes - are one and the same, and you just made a mistake when copying the code;
the EEOptionSet class is declared in the same namespace as your window.
<Window.DataContext>
<local:EEOptionSet/>
<Window.DataContext>
protected readonly EEOptionSet options;
public window()
{
InitializeComponent();
options = (EEOptionSet) DataContext;
}
<TextBox Text="{Binding FullPath, Mode=OneWay}"
IsReadOnly="True"
Focusable="False"/>

WPF DependencyProperty OnPropertyChanged returns default value as NewValue if using DataTemplate on UserControl

I have an application in which I set the content of a contentpresenter, dependent on the datatype by a datatemplate (see MainWindow). The Datatemplate is a usercontrol, which is actually datatype specific. (The small example below is only for demonstration, but in my "real" application the user shall be able to switch between different data.)
The usercontrol (UserControl1) has a DependencyProperty which I assign a value (in my application this is actually a binding to a VM, just set it to a string in example for simplicity).
Setting the value is still working fine. However In my UserControl I need to react to changes of the DependencyProperty to change the view of my UserControl (or later on CustomControl). So I implemented a OnPropertyChangend method.
When application starts OnPropertyChanged works as I expect it and I get the "correct" newvalue of my DependencyProperty. However, if I change my VM (i.e. my datatemplate changes) during runtime by clicking on a button, OnPropertyChanged returns the DependencyProperty's defaultvalue.
In my small example application, I can see that the value is set correctly, as the Textblock content changes to the correct value.
It only seems that OnPropertyChanged gets fired before my DependencyProperty's value gets the new value. So, it's not possible for me to react on the new value.
It is not really clear why this happens. Seems to have something to do with the order in which WPF resolves internal stuff?
Does anyone have a clue, how I can fix this behavior and get access to the current/last value when changing my VM and don't miss an update? As stated out before, I need to react on that value.
Maybe I am doing something totally stupid here. Is the approach I decided to use here a bad one? Are DataTemplates the wrong approach to switch between two pairs? What would be a better approach then? However, I guess it won't be possible to avoid the DependencyProperty and the UserControl in my application.
MainWindow.xaml
<!--MainWindow.xaml -->
<Grid>
<StackPanel>
<Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<ContentPresenter Content="{Binding ActiveVM}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type local:VM1}">
<local:UserControl1 MyProperty="Test1"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:VM2}">
<local:UserControl1 MyProperty="Test2"/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</StackPanel>
</Grid>
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
vmParent = new VMParent();
DataContext = vmParent;
var vm1 = new VM1();
var vm2 = new VM2();
}
VMParent vmParent;
private void Button_Click(object sender, RoutedEventArgs e)
{
vmParent.ChangeActiveVM();
}
}
UserControl1.xaml
<!--UserControl1.xaml -->
<TextBlock Text="{Binding MyProperty, RelativeSource={RelativeSource AncestorType={x:Type local:UserControl1}}}"/>
UserControl1.cs
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(UserControl1), new PropertyMetadata("DefaultString", OnMyPropertyChangend));
private static void OnMyPropertyChangend(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == "DefaultString")
{
;
//xxxxxx
//unexpectedly i get stuck here
//Would expect/need NewValue to be Text1/Text2 to react to it
//xxxxxx
}
}
}
VMParent
class VMParent : INotifyPropertyChanged
{
public VMParent()
{
vm1 = new VM1();
vm2 = new VM2();
ActiveVM = vm1;
}
public event PropertyChangedEventHandler PropertyChanged;
VM1 vm1;
VM2 vm2;
public object ActiveVM
{
get => m_activeVM;
set { m_activeVM = value; OnPropertyChanged("ActiveVM"); }
}
private object m_activeVM;
protected internal void OnPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
public void ChangeActiveVM()
{
if (ActiveVM is VM1)
ActiveVM = vm2;
else
ActiveVM = vm1;
}
}
VMs are only used to apply Datatemplate
class VM1
{
}
class VM2
{
}

WPF control binding to a single variable

I can't seem to bind a control's value to an object. I want to bind a TextBox to a string object, the idea is that when textbox's text changes, it should automatically change the object as well. couldn't figure out what I'm doing wrong. Here is what I have tried:
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
string str;
public MainWindow()
{
InitializeComponent();
this.DataContext = str;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
and MainWindow.xaml:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="150" Width="150">
<Grid Margin="0,0,642,319">
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="{Binding str}" VerticalAlignment="Top" Width="120" Margin="0,0,-120,-46" />
<Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Click="Button_Click" Height="23" Margin="0,28,-75,-51" RenderTransformOrigin="0.423,2.257" />
</Grid>
</Window>
So, when I enter something to the textbox and click the button, I should see the text in str while debugging but it is always null
Change the str to a auto property:
public string str { get; set; }
Change the DataContext to:
DataContext = this;
The DataContext is the class which will hold your binding properties/commands/events.
The properties/commands/events need to be public in order to be accessible by your view.
For the two-way binding to work, you have to notify to the UI binding that the property has been changed and for that you need to implement the INotifyPropertyChanged interface for the class which holds the properties which have been bound in the UI. You will need a private property and you cannot notify from a auto-property.
Simple Example:
public class Sample : INotifyPropertyChanged
{
private string _str;
public string Str
{
get { return _str; }
set
{
_str = value;
NotifyPropertyChanged(nameof(Str));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
First, data bindings in WPF only work with public properties. So you must explicitely declare one in your code behind (instead of string str;)
public string str { get; set; }
Second, the DataContext property of a view defines the object / class in which the property will be searched for the bindings. The line this.DataContext = str; in your example means that you want your bindings in the view to be looked for inside the str object (which is a string). You should replace this line by
this.DataContext = this;
so that the bindings will be searched inside the code behind of this view itself.
Remark
You could also stay with the line this.DataContext = str; if str is a public property and bind using an expression such as
<TextBox Text="{Binding .}" />
which will bind to the value of the DataContext property.
Maybe you can use MVVM light to do the binding.

Binding model with multiple properties in UserControl using one DependencyProperty [duplicate]

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
Closed 4 years ago.
I would like to be able to bind complex model (many properties) to UserControl through DependencyProperty, and if model would be edited in UserControl I would like to see this edited information inside my binded model.
Example application: Model, UserControl (xaml + cs), MainWindow (xaml + cs). I have no ViewModel to simplify idea.
Model:
public class MyModel : INotifyPropertyChanged
{
private string _surname;
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
}
}
public string Surname
{
get => _surname;
set
{
_surname = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MyModelEditor.xaml (inside Grid):
<DockPanel>
<TextBox Text="{Binding MyModel.Name}"/>
<TextBox Text="{Binding MyModel.Surname}"/>
</DockPanel>
Also contains this line in UserControl root element:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
MyModelEditor.xaml.cs:
public partial class MyModelEditor : UserControl
{
public MyModel MyModel
{
get => (MyModel)GetValue(MyModelProperty);
set => SetValue(MyModelProperty, value);
}
public static readonly DependencyProperty MyModelProperty =
DependencyProperty.Register("MyModel", typeof(MyModel), typeof(MyModelEditor), new FrameworkPropertyMetadata(null));
public MyModelEditor()
{
InitializeComponent();
}
}
MainWindow.xaml (inside Grid):
<DockPanel>
<Button DockPanel.Dock="Bottom" Content="Press Me!" Click="ButtonBase_OnClick"/>
<controls:MyModelEditor MyModel="{Binding MyModel}"/>
</DockPanel>
MainWindow.xaml.cs:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private MyModel _myModel;
public MyModel MyModel
{
get => _myModel;
set
{
_myModel = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show(MyModel?.Name);
}
}
My test scenario: type text in textbox, press button.
Current behavior: Message after pressing button is empty.
Expected behavior: Message after pressing button is same like in textbox.
I wold not like to bind to all properties separately, because in future I will have much more then two properties.
Why current approach does not work?
How can I achieve my goal?
You are apparently not using the UserControl instance as Binding source in your UserControl's XAML. One way to do this would be to set the Binding's RelativeSource:
<TextBox Text="{Binding MyModel.Name,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
However, you don't need a new dependency property at all for this purpose. Just bind the UserControl's DataContext to a MyModel instance, like
<controls:MyModelEditor DataContext="{Binding MyModel}"/>
The Bindings in the UserControl's XAML would automatically work with the MyModel object, like this:
<DockPanel>
<TextBox Text="{Binding Name}"/>
<TextBox Text="{Binding Surname}"/>
</DockPanel>
For both of your TextBox controls, you should define their Binding with a TwoWay mode (ms docs on binding modes). Which, basically, would assure that the data flow is working in both direction (i.e. from the view model into the view and the other way around):
<DockPanel>
<TextBox Text="{Binding MyModel.Name, Mode=TwoWay}"/>
<TextBox Text="{Binding MyModel.Surname, Mode=TwoWay}"/>
</DockPanel>
As a good practice, you should always explicitly define what is the mode of the the Binding (NOTE: by default it's OneWay TwoWay - how to know which is the default?).
Another tip would be to go ahead and use MvvmHelpers nuget (github project), which could spare you the time of implementing INotifyPropertyChanged. Besides, you shouldn't re-invent the wheel
EDIT: Fixes are in your GitHub repo
Two things to note here
You have not instantiated your ViewModel (i.e. MyModel), so it was always null
You don't need to create DependencyPropery every time you want to pass some information to your UserControl. You could simply bind the DataContext itself

C# MVVM: Binding a RadioButton to a boolean Property

I am quiet new to programming and am currently learning C# and the MVVM pattern.
I need to code a database tool for ChiliPlants for university.
There you should be able to add a new object to an ObservableCollection.
To add a new Item to this ObservableCollection a new Window opens. It looks like this:
Window Add
I now want the two RadioBoxes to be bound to a property called "HybridSeed". Which is defined in the ViewModel:
//Public Property HybridSeed
public bool HybridSeed
{
get { return ChiliModel.HybridSeed; }
set
{
if (ChiliModel.HybridSeed == value)
return;
ChiliModel.HybridSeed = value;
OnPropertyChanged("HybridSeed");
}
}
The RadioBox part of my View looks like this:
<RadioButton Grid.Row="5" Content="Ja" Grid.Column="1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<RadioButton Grid.Row="5" Content="Nein" Grid.Column="1" HorizontalAlignment="Left" Margin="89,10,0,0" VerticalAlignment="Top"/>
But how to bind the outcome of a user clicking on these RadioButtons to this HybridSeed Property? Important is that the outcome is a bool.
I looked up almost every entry similar to this topic, but I did not find a simple solution. Or a solution which I was able to understand with my bad coding skills :( ...
I would be very happy if you guys could help me. Please keep it simple for this newbie :)
If there is a simpler solution using a CheckBox or a ComboBox it would also be perfect. The most important thing is to have a nice user interface. Right now it only works with a TextBox where the user always has to write "True" or "False".
Solution:
I added the IsClicked Property in the "Yes" RadioButton to be bound to my boulean property with: IsClicked="{Binding HybridSeed}". Thanks to naslund for his fast answer :)
Just bind HybridSeed to the Yes-radiobutton. It will then either be true if the user has selected that or false if No-radiobutton has been selected (or if nothing has been selected). Binding to both buttons in this case is a bit redundant since the mechanism of radiobuttons takes care of it.
WPF:
<RadioButton Content="Yes" IsChecked="{Binding HybridSeed}" />
<RadioButton Content="No" />
<Label Content="{Binding HybridSeed}" ContentStringFormat="Value is: {0}" />
Logic:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
private bool hybridSeed;
public bool HybridSeed
{
get { return hybridSeed; }
set
{
hybridSeed = value;
OnPropertyChanged(nameof(HybridSeed));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Categories