I made a submit button with the command that changes the text in a textblock based on whether the user entered the correct number in a textbox.
public string txtResults { get; set; }
public string txtInput { get; set; }
// Method to execute when submit command is processed
public void submit()
{
if (txtInput == number.ToString())
txtResults = "Correct!";
else
txtResults = "Wrong!";
}
'txtInput' is the member that is bound to the textbox and includes the user's input. 'txtResults' is supposed to be shown in the textblock. Right now when I click the submit button, in debugging mode, the txtResults value is assigned the "Correct!" string but it doesn't update in the view.
The XAML:
<Window x:Class="WpfMVVP.WindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfMVVP"
Title="Window View" Height="350" Width="525" Background="White">
<Grid>
<Canvas>
<Label Canvas.Left="153" Canvas.Top="89" Content="Guess a number between 1 and 5" Height="28" Name="label1" />
<TextBox Text="{Binding txtInput, UpdateSourceTrigger=PropertyChanged}" Canvas.Left="168" Canvas.Top="142" Height="23" Name="textBox1" Width="38" />
<TextBlock Text="{Binding txtResults}" Canvas.Left="257" Canvas.Top="142" Height="23" Name="textBlock1" />
<Button Command="{Binding Submit}" Canvas.Left="209" Canvas.Top="197" Content="Submit" Height="23" Name="button1" Width="75" />
</Canvas>
</Grid>
Update:
I made this change in my View Model
public class WindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private string _txtResults;
public string txtResults
{
get { return _txtResults; }
set { _txtResults = value; OnPropertyChanged("txtResults"); }
}
And now it's working! Thanks.
Please make sure your txtResults property is inheriting from INotifyPropertyChanged. Your view model should inherit from there also. Have your view model class inherit from INotifyPropertyChanged, and implement the interface. Then replace you TxtResults property with the following:
private string _txtResults = string.Empty;
public string TxtResults
{
get { return this._txtResults; }
set
{
this._txtResults= value;
this.RaisePropertyChangedEvent("TxtResults");
}
}
Related
Problem
I am trying to bind a ComboBox's SelectedItem to a custom class but this does not update when the property is changed.INotifyPropertyChanged is implemented.
The DataContext
The DataContext is a custom class which contains many properties, but an extract of this is below. You can see it implements INotifyPropertyChanged and this called when the two properties are changed.
public class BctsChange : INotifyPropertyChanged
{
#region declarations
private byContact _Engineer;
public byContact Engineer
{
get { return _Engineer; }
set
{
_Engineer = value;
NotifyPropertyChanged("Engineer");
OnEngineerChanged();
}
}
private BctsSvc.DOSets _LeadingSet;
public BctsSvc.DOSets LeadingSet
{
get { return _LeadingSet; }
set { _LeadingSet = value; NotifyPropertyChanged("LeadingSet"); }
}
#endregion
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public BctsChange()
{
Engineer = new byContact(Environment.UserName);
}
private void OnEngineerChanged()
{
if (Engineer != null)
{
BctsSvc.DOSets leadSet = GetLeadingSetFromDeptCode(Engineer.DeptCode);
if (leadSet == null) return;
LeadingSet = leadSet;
}
}
private static BctsSvc.DOSets GetLeadingSetFromDeptCode(string DeptCode)
{
BctsSvc.BctsServiceSoapClient svc = new BctsSvc.BctsServiceSoapClient();
BctsSvc.DOSets setX = svc.GetSetFromDeptCode(DeptCode);
return setX;
}
}
The Window XAML
I have several controls on the window, but to keep the code simple I believe the following extract will suffice.
<Window x:Class="MyNamespace.wdSubmit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:MyNamespace"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Name="ucReqForm"
Title="wdSubmit" >
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
<GroupBox Header="Engineer Details" Name="grpOwnerDetails" >
<StackPanel Orientation="Vertical">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="35"/>
</Grid.ColumnDefinitions>
<Label Content="{Binding Engineer.FullName, FallbackValue='Please select an engineer by clicking →', Mode=OneWay}" Margin="5,0" IsEnabled="True" FontStyle="Italic" />
<Button Content="{StaticResource icoSearch}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="1" Height="23" Name="btnSelectEngineer" Margin="0,0,5,0" HorizontalAlignment="Stretch" ToolTip="Search for an engineer responsible" Click="btnSelectEngineer_Click" />
</Grid>
<ComboBox Height="23" x:Name="ddSet2" Margin="5,0" ItemsSource="{Binding LeadingSets, Mode=OneWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" SelectedItem="{Binding LeadingSet, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,NotifyOnTargetUpdated=True}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SetName}" ToolTip="{Binding HelpInfo}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<my:LabelledDropdown Height="23" x:Name="ddSet" Margin="5,0" ItemsSource="{Binding LeadingSets, Mode=OneWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" SelectedItem="{Binding LeadingSet, Mode=TwoWay,NotifyOnTargetUpdated=True,NotifyOnSourceUpdated=True}" Label="e.g. BodyHardware">
<my:LabelledDropdown.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SetName}" ToolTip="{Binding HelpInfo}"/>
</DataTemplate>
</my:LabelledDropdown.ItemTemplate>
</my:LabelledDropdown>
</StackPanel>
</GroupBox>
</StackPanel>
</Window>
The above extract contains:
A Label that contains a contact's name, and a button to search for a contact, bound to the FullName of the Engineer
A ComboBox that contains departments within the company, bound to an ObservableCollection<DOSets>, which contains a list of departments
Two ComboBoxes, one which is a custom one and the other which is temporary to ensure the bug is not within the control. These are Databound to LeadingSet
Window Code Behind
In the code behind I set the DataContext to CurrentChange. When the user wants to select a different Engineer then this will update the selected department for the engineer in CurrentChange.
When the user changes the engineer, the data binding for the engineer is updated, but the selected department (Leading Set) isn't.
//Usings here
namespace MyNamespace
{
public partial class wdSubmit : Window, INotifyPropertyChanged
{
private BctsSvc.BctsServiceSoapClient svc;
private BctsChange _CurrentChange;
public BctsChange CurrentChange
{
get { return _CurrentChange; }
set { _CurrentChange = value; OnPropertyChanged("CurrentChange"); }
}
private List<BctsSvc.DOSets> _LeadingSets;
public List<BctsSvc.DOSets> LeadingSets
{
get
{
return _LeadingSets;
}
}
public wdSubmit()
{
InitializeComponent();
svc = new BctsSvc.BctsServiceSoapClient();
_LeadingSets = svc.GetLeadSets().ToList();
OnPropertyChanged("LeadingSets");
this._CurrentChange = new BctsChange();
this.DataContext = CurrentChange;
CurrentChange.PropertyChanged += new PropertyChangedEventHandler(CurrentChange_PropertyChanged);
}
void CurrentChange_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("CurrentChange");
OnPropertyChanged(e.PropertyName);
}
private void btnSelectEngineer_Click(object sender, RoutedEventArgs e)
{
byContact newContact = new frmSearchEngineer().ShowSearch();
if (newContact != null)
{
CurrentChange.Engineer = newContact;
PropertyChanged(CurrentChange, new PropertyChangedEventArgs("LeadingSet"));
PropertyChanged(CurrentChange.LeadingSet, new PropertyChangedEventArgs("LeadingSet"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(CurrentChange, new PropertyChangedEventArgs(propertyName));
}
}
}
I've realised the problem may be due to the LeadingSet, returned when the engineer is changed, being a different instance to that in the ObservableCollection.
WPF n00bie here, trying to get his UI to work properly.
So I made this test example. The textblock bound to HeaderText1 changes correctly at the launch of the app, but the textblock bound to HeaderText2 doesn't update after clicking the button.
What am I doing wrong? Thanks in advance!!
<Window x:Class="DataBinding.DataContextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataContextSample" Height="142.596" Width="310">
<StackPanel Margin="15">
<WrapPanel>
<TextBlock Text="Window title: " />
<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
<Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
</WrapPanel>
<TextBlock Text="{Binding Path=DataContext.HeaderText}"></TextBlock>
<TextBlock Text="{Binding Path=DataContext.HeaderText2}"></TextBlock>
</StackPanel>
</Window>
Main window class:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace DataBinding
{
public partial class DataContextSample : Window
{
public string HeaderText { set; get; }
public DataContextSample()
{
HeaderText = "YES";
InitializeComponent();
this.DataContext = this;
}
private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
{
BindingExpression binding = txtWindowTitle.GetBindingExpression(TextBox.TextProperty);
binding.UpdateSource();
Source source = new Source();
source.HeaderText2 = "YES2";
}
}
}
And the INotifyPropertyChanged class
using System.ComponentModel;
namespace DataBinding
{
public class Source : INotifyPropertyChanged
{
public string HeaderText2 { set; get; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
}
First of all you are doing many things wrong.
You should not be using the window as it's own datacontext, you should have a viewmodel that you set.
You should not be using event handlers in the view to manipulate the viewmodel. You should bind the button to a command.
Your source seems to be a "viewmodel", consider renaming it to MainWindowViewModel (for clarity) and then do this.
public class MainWindowViewModel : INotifyPropertyChanged
{
private string headerText;
private string headerText2;
private ICommand updateHeaderText2;
public string HeaderText
{
set
{
return this.headerText;
}
get
{
this.headerText = value;
// Actually raise the event when property changes
this.OnPropertyChanged("HeaderText");
}
}
public string HeaderText2
{
set
{
return this.headerText2;
}
get
{
this.headerText2 = value;
// Actually raise the event when property changes
this.OnPropertyChanged("HeaderText2");
}
}
public ICommand UpdateHeaderText2
{
get
{
// Google some implementation for ICommand and add the MyCommand class to your solution.
return new MyCommand (() => this.HeaderText2 = "YES2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
And set this viewmodel to the datacontext of your window.
this.DataContext = new MainWindowViewModel();
And then in your xaml you should bind to the viewmodel as such
<Window x:Class="DataBinding.DataContextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataContextSample" Height="142.596" Width="310">
<StackPanel Margin="15">
<WrapPanel>
<TextBlock Text="Window title: " />
<!-- Not sure what this binding is? -->
<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
<Button Name="btnUpdateSource" Command="{Binding UpdateHeaderText2}" Margin="5,0" Padding="5,0">*</Button>
</WrapPanel>
<TextBlock Text="{Binding HeaderText}"></TextBlock>
<TextBlock Text="{Binding HeaderText2}"></TextBlock>
</StackPanel>
</Window>
You set the DataContext to this (the window). You don't have a property named HeaderText2 in the DataContext so the second binding won't work.
I'd do this (without changing your code too much, in reality I'd do a proper MVVM approach):
public partial class DataContextSample : Window
{
public Source Source { get; set; }
public string HeaderText { set; get; }
public MainWindow()
{
InitializeComponent();
HeaderText = "YES";
Source = new Source { HeaderText2 = "YES" };
DataContext = this;
}
private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
{
BindingExpression binding = txtWindowTitle.GetBindingExpression(TextBox.TextProperty);
if (binding != null)
{
binding.UpdateSource();
}
Source.HeaderText2 = "YES2";
}
}
I added a new property called Source which is of type Source. Set its initial HeaderText2 to the same "YES" in the constructor and in the button click change that to "YES2".
You have to change your Source class as well, to actually notify about changes:
public class Source : INotifyPropertyChanged
{
private string _headerText2;
public string HeaderText2
{
get { return _headerText2; }
set
{
_headerText2 = value;
OnPropertyChanged("HeaderText2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
And then in your XAML:
<StackPanel Margin="15">
<WrapPanel>
<TextBlock Text="Window title: " />
<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
<Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
</WrapPanel>
<TextBlock Text="{Binding Path=HeaderText}"></TextBlock>
<TextBlock Text="{Binding Path=Source.HeaderText2}"></TextBlock>
</StackPanel>
Well there are a few issues with your code.
First of all, you never assign your "Source" to a datacontext, so there's no way for your second TextBlock to find the value of "HeaderText2".
If however you would assign your "Source" to the textblocks datacontext then we could fetch the value of "HeaderText2". Consider the code below
<Window x:Class="DataBinding.DataContextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataContextSample" Height="142.596" Width="310">
<StackPanel Margin="15">
<WrapPanel>
<TextBlock Text="Window title: " />
<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
<Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
</WrapPanel>
<TextBlock Text="{Binding Path=HeaderText}"></TextBlock>
<TextBlock Name="TextBlock2" Text="{Binding Path=HeaderText2}"></TextBlock>
</StackPanel>
</Window>
We have given your second Textblock a name, "TextBlock2" and also removed the "Datacontext"-part from your binding.
Then we have moved the Creation of your "Source" object from the button event to the windows constructor (there is no need to make a new one everytime we click a button when all we want to do is to update a property)
public partial class DataContextSample : Window
{
public string HeaderText { set; get; }
private Source source { get; set; }
public DataContextSample()
{
...
source = new Source();
TextBlock2.DataContext = source;
...
}
...
}
And then in your buttons click-event we assign your databound property a value of "YES2".
private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
{
...
source.HeaderText2 = "YES2";
}
There is however one more detail. Your class "Source" does implement "INotifyPropertyChanged", but it never "uses" it. By that I mean, that when you assign a value to your property "HeaderText2" you never actually "notify" the UI that something has changed with it, and thus the UI will not fetch the new value. Consider the code below:
public class Source : INotifyPropertyChanged
{
public string HeaderText2 { set
{
headerText2 = value;
OnPropertyChanged("HeaderText2");
}
get
{
return headerText2;
}
}
string headerText2;
...
}
So let's take a look at what we've done with the property "HeaderText2". Everytime the "HeaderText2" gets a value assigned, it will first save the value in a privat property (so that we can read from it later). But in addition to that we also call the "OnPropertyChanged" method with our Propertys name. That method will in turn check if anyone is "listening" to our "PropertyChanged"-event (and since we have a databinding on the current object, someone is listening), and create a new event.
Now we have assigned a datasource to your textblock with a path to "HeaderText2", we are notifying all listeners when we update "HeaderText2" on the datasource and we are updating "HeaderText2" on the buttons click event.
Happy coding!
I am having a major problem binding my data from TextBox to ViewModel To TextBlock. I have set up my following Xaml Code like so:
<Page
x:Class="digiBottle.MainPage"
DataContext="{Binding Source=UserProfile}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:digiBottle"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding Source=UserProfile}">
<TextBlock HorizontalAlignment="Left" Margin="219,72,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="32" Width="232" Text="{Binding userFirstName, Mode=OneWay}"/>
<TextBox HorizontalAlignment="Left" Margin="39,72,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="111" Text="{Binding userFirstName, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
The .cs file I am trying to use as a source is defined as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace digiBottle.Model
{
public class UserProfile : INotifyPropertyChanged
{
public int ID { get; set; }
public string userFirstName;
public string userLastName { get; set; }
public int age { get; set; }
public int weight { get; set; }
public int height { get; set; }
public DateTime signupTime { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public UserProfile()
{
userFirstName = "First Name";
}
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public UserProfile getCopy()
{
UserProfile copy = (UserProfile)this.MemberwiseClone();
return copy;
}
}
}
What am i doing wrong when trying to bind my TextBox and TextBlock to userFirstName in the UserProfile.cs Source. ANy help would be a major help!
Thank you
The first thing I notice here is that your properties (setter) are not raising event change. You need to call RaisePropertyChanged in your properties setter.
I would have written it like
A private field
private String _userFirstName;
Then in constructor
public UserProfile()
{
this._userFirstName = "First Name";
}
With Property raising event
public String UserFirstName
{
get { return this._userFirstName; }
set
{
this._userFirstName = value;
this.RaisePropertyChanged("UserFirstName");
}
}
And then in XAML, bind it with property "UserFirstName" with two way binding
<TextBlock HorizontalAlignment="Left" Margin="219,72,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="32" Width="232" Text="{Binding UserFirstName, Mode=OneWay}"/>
<TextBox HorizontalAlignment="Left" Margin="39,72,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="111" Text="{Binding UserFirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
DataBinding can be hard to understand at first. Please refer to Data binding for Windows Phone 8 to get yourself started.
For your code: Here are the fixes you will need:
Remember you can only bind to a property.
You need to raise the event on the set action.
You may need twoway binding on the textbox depending on the actions you want.
You need to set the DataContext for both textbox and the textblock.
Here are the changes:
CS
public class UserProfile : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string user_first_name;
public String UserFirstName
{
get { return user_first_name; }
set
{
user_first_name = value;
OnPropertyChanged("UserFirstName");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
UserProfile up = new UserProfile();
this.tb1.DataContext = up;
this.tb2.DataContext = up;
}
}
XAML
<TextBlock x:Name="tb2" TextWrapping="Wrap" Text="{Binding UserFirstName}"/>
<TextBox x:Name="tb1" HorizontalAlignment="Left" Height="72" Margin="14,475,0,0" Grid.Row="1" TextWrapping="Wrap" Text="{Binding UserFirstName, Mode=TwoWay}" VerticalAlignment="Top" Width="456" />
I have 2 usercontrols.
Usercontrol 1: The menubar which has buttons like Add, Edit, Delete, Save and Undo.
Usercontrol 2: Is a screen where the user can input text in textboxes and passwordboxes
But when I want to save I'm used to do the following when I only have 1 usercontrol which has the buttons and everything instead of the menubar and the detailscreen seperated:
<Button Style="{DynamicResource SaveButton}" Command="{Binding Path=SaveCommand}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource pwConverter}">
<Binding ElementName="txtPassword" />
<Binding ElementName="txtRepeatPassword" />
</MultiBinding>
</Button.CommandParameter>
</Button>
But now the elementname "txtPassword" and "txtRepeatPassword" don't exist in that scope.
This is my SaveCommand when I click the save button. It receives those 2 parameters so I can check is the 2 passwords are the same and stuff like that.
private void SaveUserExecute(object passwords)
{
try
{
var passwordvalues = (object[])passwords;
PasswordBox passwordBox1 = (PasswordBox)passwordvalues[0];
PasswordBox passwordBox2 = (PasswordBox)passwordvalues[1];
...
Any ideas on how to solve this issue?
Because my 2 usercontrols shared the same DataContext I've made 2 properties which represent my PasswordBoxes. When I initialize that view I did the following:
public InputUserView()
{
InitializeComponent();
this.DataContext = InputUserViewModel.Instance;
InputUserViewModel.Instance.PasswordBox1 = txtPassword;
InputUserViewModel.Instance.PasswordBox2 = txtRepeatPassword;
}
So now my viewmodel has knowledge of those 2 passwordboxes. I think It's not really that good, but it works for me and I can live with it
This is easy if you use the MVVM pattern. You can have one ViewModel which can be the DataContext to each of your user controls, and your main Window. Then just bind to the properties on each of these.
Below is an example of a ViewModel, it has fields exposed by properties which we can bind to:
public class ViewModel : INotifyPropertyChanged
{
private readonly Command _command;
public Command Command
{
get { return _command; }
}
public ViewModel()
{
_command = new Command(this);
}
private string _textBoxOnUserControlOne;
private string _textBoxOnUserControlTwo;
public string TextBoxOnUserControlOne
{
get { return _textBoxOnUserControlOne; }
set
{
if (value == _textBoxOnUserControlOne) return;
_textBoxOnUserControlOne = value;
OnPropertyChanged("TextBoxOnUserControlOne");
}
}
public string TextBoxOnUserControlTwo
{
get { return _textBoxOnUserControlTwo; }
set
{
if (value == _textBoxOnUserControlTwo) return;
_textBoxOnUserControlTwo = value;
OnPropertyChanged("TextBoxOnUserControlTwo");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Here is the command class, where I am going to work with both of these properties:
public class Command : ICommand
{
private readonly ViewModel _viewModel;
public Command(ViewModel viewModel)
{
_viewModel = viewModel;
}
public void Execute(object parameter)
{
var dataOnControlOne = _viewModel.TextBoxOnUserControlOne;
var dataOnControlTwo = _viewModel.TextBoxOnUserControlTwo;
//Use these values
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
Now, here is my first user control 1 which is bound to one of the fields on my ViewModel, notice the DataContext:
<UserControl ... DataContext="{StaticResource ViewModel}">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Text="{Binding TextBoxOnUserControlOne}" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</UserControl>
And here is a second UserControl with the same DataContext, and the textbox is bound to a different property:
<UserControl ... DataContext="{StaticResource ViewModel}">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Text="{Binding TextBoxOnUserControlTwo}" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</UserControl>
Here is my main window, which contains both of these user controls, and a button bound to my command class:
<Window ... DataContext="{StaticResource ViewModel}">
<Grid>
<my:UserControl1 HorizontalAlignment="Left" Margin="160,69,0,0" x:Name="userControl11" VerticalAlignment="Top" Height="47" Width="155" />
<my:UserControl2 HorizontalAlignment="Left" Margin="160,132,0,0" x:Name="userControl12" VerticalAlignment="Top" Height="48" Width="158" />
<Button Content="Button" Command="{Binding Command}" Height="23" HorizontalAlignment="Left" Margin="199,198,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
And finally my App.Xaml class, to glue everything together:
<Application ...>
<Application.Resources>
<wpfApplication4:ViewModel x:Key="ViewModel"/>
</Application.Resources>
</Application>
Here, we have seperate user controls, and the fields are bound to properties on the one view model. This viewmodel passes itself into the command class, which can then access the properties which the textboxes on the seperate usercontrols are bound to, and work with them when the button is pressed. I hope this helps!
I have an App Bar with some buttons like this
<Page.BottomAppBar>
<AppBar x:Name="bottomAppBar" Padding="10,10,10,10" >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource ReadAppBarButtonStyle}" >
</Button>
</StackPanel>
</AppBar>
</Page.BottomAppBar>
I want to bind the button text to a selected item property of a ListView and use an IValueConverter.
I found that the button text is to be set using AutomationProperties.Name
how can I bind this property through XAML or Code.
Thanks
You're right, for some reason the following doesn't work, although the same binding works just fine you use it for e.g. Text property of a TextBox:
<Button Style="{StaticResource SkipBackAppBarButtonStyle}" AutomationProperties.Name="{Binding SelectedItem, ElementName=List}" />
I did manage to get it work by using a property in the view model and binding to it both ListView.SelectedItem and AutomationProperties.Name:
<ListView ItemsSource="{Binding Strings}"
SelectedItem="{Binding SelectedString, Mode=TwoWay}" />
<!-- ... -->
<Button Style="{StaticResource SkipBackAppBarButtonStyle}"
AutomationProperties.Name="{Binding SelectedString}" />
SelectedString should be a property in a view model implementing INotifyPropertyChanged:
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
Strings = new ObservableCollection<string>();
for (int i = 0; i < 50; i++)
{
Strings.Add("Value " + i);
}
}
public ObservableCollection<string> Strings { get; set; }
private string _selectedString;
public string SelectedString
{
get { return _selectedString; }
set
{
if (value == _selectedString) return;
_selectedString = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}