What is the best way to acchieve this, what I am going to describe bellow.
I have two textboxes with twoway bindings on the same object and same property.
Now, when I update text in one textbox I wish other textbox to grab the same value again from object. Is that even possible, or I have to do this manually. For an example, I can use TextChanged event and set this value.
Yes you can bind a single property to two controls
If this class is your DataContext (viewmodel)
public class Bind : INotifyPropertyChanged
{
private string _text1;
public string text1
{
get
{
return _text1;
}
set
{
_text1=value;
NotifyPropertyChanged("text1");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
In XAML
<UserControl x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525"
xmlns:ViewModel="clr-namespace:WpfApplication1">
<UserControl.DataContext>
<ViewModel:Class1/>
</UserControl.DataContext>
<Grid>
<TextBox Width="150" Height="50" Text="{Binding text1, Mode=TwoWay}"/>
<TextBox Text="{Binding text1, Mode=TwoWay}" Margin="0,232,0,0"/>
</Grid>
</UserControl>
Related
I have delved into the magic and mystery of WPF and Binding. It was going OK then I hit a brick wall and need to ask those much cleverer than me for help please.
I cut this back to a simple app removing all the other items in my code. The UI has a text box and a label. When the text in the textbox changes then I want to update the label. Somewhere I am missing a link and I guess it is the binding as I never seem to get into the set. Here is the code
Mainwindow.xaml.cs
using System.ComponentModel;
using System.Windows;
namespace Databinding3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string myBindedVal = "....";
public MainWindow()
{
InitializeComponent();
}
//Create properties for our variable _myBindedVal
public string MyBindedVal
{
get => myBindedVal;
set
{
NotifyPropertyChanged(nameof(MyBindedVal));
myBindedVal = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (propertyName != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Mainwindow.xml
<Window x:Class="Databinding3.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:Databinding3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox x:Name="txtbx_name" Text="Textbox" HorizontalAlignment="Center" Height="57" TextWrapping="Wrap" VerticalAlignment="Center" Width="594"/>
<Label Content="{Binding MyBindedVal, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" HorizontalAlignment="Center" Height="44" Grid.Row="1" VerticalAlignment="Center" Width="594"/>
</Grid>
</Window>
Thanks for your help
You did not bind the Text property of the TextBox. It should look like shown below, where the UpdateSourceTrigger ensures that the source property is updated immediately when you type into the TextBox.
<TextBox Text="{Binding MyBoundVal, UpdateSourceTrigger=PropertyChanged}" .../>
The above Binding does not explicitly specify a source object, and therefore uses the Window's DataContext as source. Set the DataContext like this:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
The Label Binding would then just be
<Label Content="{Binding MyBoundVal}" .../>
Be aware that you would typically use a TextBlock, not a Label, to show text:
<TextBlock Text="{Binding MyBoundVal}" .../>
The execution order in the property setter is also important. Assign the backing field value before firing the PropertyChanged event.
public string MyBoundVal
{
get => myBoundVal;
set
{
myBoundVal = value;
NotifyPropertyChanged(nameof(MyBoundVal));
}
}
Finally, the NotifyPropertyChanged method should look like shown below. Testing the propertyName argument is pointless, but you should test the PropertyChanged event for null, usually by using the null-propagation operator ?.:
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I'm having a bit of trouble with this. I need to add code to move a TabControl to the next page. I'm using System.Windows.Controls.TabControl available in .Net 4.5. I'm not even sure how I can enumerate the TabPages.
An MVVM solution would be ideal, but I can work with a code behind solution. I'd change it a custom behavior or something.
Thanks.
You could implement INotifyPropertyChanged in your ViewModel, and then bind the SelectedIndex to an integer property in your ViewModel which notifies on change, something like this:
ViewModel:
public sealed class MainViewModel : INotifyPropertyChanged
{
private int _tabNumber = 0;
public int TabNumber
{
get { return _tabNumber; }
set
{
if (value == _tabNumber) return;
_tabNumber = value;
OnPropertyChanged("TabNumber");
}
}
private void ChangeTab(int tabNumber)
{
TabNumber = tabNumber;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Window x:Class="WpfApplication1.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" DataContext="{StaticResource MainViewModel}">
<Grid>
<TabControl Height="100" SelectedIndex="{Binding TabNumber}" HorizontalAlignment="Left" Margin="108,108,0,0" Name="tabControl1" VerticalAlignment="Top" Width="200">
<TabItem Header="tabItem1" Name="tabItem1">
<Grid />
</TabItem>
</TabControl>
</Grid>
</Window>
You can do this:
tabControl1.SelectedIndex++;
or can Bind it in XAML
I'm trying to do a simple binding but I'm having some problems. I have a text block and a button. The textblock is binded to a property called "word". When you press the button the value of word changes and I want to automacally update the text block. This is a classic example, please explain me what I'm doing wrong:
namespace WpfApplication5
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _word;
public string word
{
get { return _word; }
set
{
_word= value;
RaisePropertyChanged(word);
}
}
private void change_Click(object sender, RoutedEventArgs e)
{
word= "I've changed!";
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
And my XAML with the binding:
<Window x:Class="WpfApplication5.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>
<TextBlock HorizontalAlignment="Left" Margin="210,152,0,0" TextWrapping="Wrap" Text="{Binding word}" VerticalAlignment="Top"/>
<Button x:Name="change" Content="Change" HorizontalAlignment="Left" Margin="189,235,0,0" VerticalAlignment="Top" Width="75" Click="change_Click"/>
</Grid>
</Window>
You are raising a PropertyChanged event for a property named I've changed!, because you pass the value of the property word to RaisePropertyChanged. You need to pass the name of the property instead:
RaisePropertyChanged("word");
This answer assumes that the data context is set correctly. If not, you need to fix that too:
DataContext = this;
Time for my first question :)
I have the following:
public class BuilderViewModel : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private double _contentScale = 1.0;
public double ContentScale
{
get { return _contentScale; }
set
{
_contentScale = value;
NotifyPropertyChanged("ContentScale");
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#region Commands
bool CanZoomIn() { return true; }
void ZoomInExecute()
{
ContentScale += 1.0;
}
public ICommand ZoomIn { get { return new RelayCommand(ZoomInExecute, CanZoomIn); } }
#endregion
}
And the corresponding view:
<UserControl x:Class="PS_IDE.FormBuilder.View.Builder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PS_IDE.FormBuilder.ViewModel">
<UserControl.DataContext>
<local:BuilderViewModel />
</UserControl.DataContext>
<TextBox Text="{Binding ContentScale}" Width="100" />
</UserControl>
I'm trying to have the ZoomIn command in BuilderViewModel update the text box value in it's view. The command is being fired from another user control, UIBuilder, which includes Builder. If I debug and fire the command from UIBuilder, I can see it updating ContentScale properly.
However, my text box value does not get updated (it only says "1", which is the initial value of ContentScale).
I know I'm missing something and hope someone can point me in the right direction.
EDIT: Added the control that is firing the command
<UserControl x:Class="PS_IDE.FormBuilder.UIBuilder"
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:PS_IDE.FormBuilder"
xmlns:ViewModel="clr-namespace:PS_IDE.FormBuilder.ViewModel"
xmlns:View="clr-namespace:PS_IDE.FormBuilder.View" mc:Ignorable="d">
<UserControl.DataContext>
<ViewModel:BuilderViewModel />
</UserControl.DataContext>
<DockPanel LastChildFill="True">
....
<ToolBarTray DockPanel.Dock="Bottom" HorizontalAlignment="Right">
<ToolBar>
<Button Height="24" Width="24" ToolTip="Zoom In" Command="{Binding ZoomIn}">
<Image Source="Images/ZoomIn.png" Height="16"/>
</Button>
....
</ToolBar>
</ToolBarTray>
<View:Builder x:Name="builder" />
</DockPanel>
</UserControl>
With the setting in both view:
<UserControl.DataContext>
<local:BuilderViewModel />
</UserControl.DataContext>
you are basically creating two viewmodels, one for each view. So when your Command updates the property it does it on one of the viewmodel but your textbox is bound to a different viewmodel.
To resolve it remove the DataContext setting from the Builder.xaml
Additionally you need to pass your DataContext to your Builder control (with this both view will share the same viewmodel).
So modify your UIBuilder.xaml:
<View:Builder x:Name="builder" DataContext="{Binding}" />
Use Mode TwoWay in your binding
Text ="{Binding ElementName=BuilderViewModel,
Path=ContentScale,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Nota : use observable collection in order to send notify
I want to do something like this diagram Data Binding Diagram.
If i update TextBox text Then update TextBlock text and Property and if i change Property Value then also update Textbox and textBlock text. Please tell me how can i do it using WPF ????
Thank`s For Help.
Im not sure if I understand right your question. Are the two Textboxes in the same view or in different?
Here a solution with 2 textboxes in the same view:
View (xaml):
<Window x:Class="Sandbox.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"
Name="mainWindow">
<StackPanel>
<TextBox Name="UpperTextBox" Text="{Binding ElementName=LowerTextBox, Path=Text,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="LowerTextBox" Text="{Binding MyValue, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
View-Codebehind (xaml.cs):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
}
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _myValue;
public string MyValue
{
get { return _myValue; }
set
{
_myValue = value;
OnPropertyChanged("MyValue");
}
}
}