I'm a newbie to WPF and data binding and after hours of binging and searching Stackoverflow I have not been able to find a comprehensive solution. I am trying to display text on a TextBlock control using data binding on my KinectWindow.xaml:
<TextBlock x:Name="InitText"
TextWrapping="Wrap"
Text="{Binding Source=ScanInitTextA,
Path=ScanInitTextA,
UpdateSourceTrigger=PropertyChanged}"
The complimentary KinectWindow.xaml.cs class has the following property:
string ScanInitText = "Preparing for Initial Scan.";
string ScanInitTextA
{ get { return (ScanInitText) ; }
set { ScanInitTextA = value; }
}
I've made numerous attempts, either by binding the property directly from the class or from the xaml. I usually get this error when trying to do anything:
System.Windows.Data Error: 40 : BindingExpression path error: 'ScanInitTextA' property not found on 'object' ''String' (HashCode=1828304777)'.
BindingExpression:Path=ScanInitTextA;
DataItem='String' (HashCode=1828304777);
target element is 'TextBlock' (Name='InitText');
target property is 'Text' (type 'String')
From what I understand, ScanInitTextA cannot be found in the object String?
Finally, I know that when I try a similar approach from a different class (that isn't KinectWindow.xaml.cs, by referencing the class in the xaml and changing the binding source to the name of that class), the data binding does work, but for other reasons I'd prefer to do it through this class.
Thanks in advance. :)
Try this:
<TextBlock x:Name="InitText"
TextWrapping="Wrap"
Text="{Binding Path=ScanInitTextA}"
Error message says that you trying to find property ScanInitTextA on the string object itself. I think the Source for current TextBlock was assigned before (maybe as DataContext).
Have you tried
<TextBlock x:Name="InitText"
TextWrapping="Wrap"
Text="{Binding Path=ScanInitTextA,
UpdateSourceTrigger=PropertyChanged}"
If you have set the DataContext of you view to self then giving Source is wrong. Just update your binding as:
<TextBlock x:Name="InitText"
TextWrapping="Wrap"
Text="{Binding Path=ScanInitTextA,
UpdateSourceTrigger=PropertyChanged}"/>
Update
What is needed is a way to bind to the property of the main window. Here is a way to do that binding
<Window x:Name="KinectWindow"
Title="My Kinect Show"
...>
<TextBlock Text="{Binding ScanInitTextA, ElementName=KinectWindow}" />
Note that you will still need to make ScanInitTextA do a property changed notification if you expect its value will be changed by something and that change needs to be displayed automatically. See #1 and #2 below.
Original Advice to Adhere to MVVM type system and put the VM on the Data Context of the Window
First off whatever class is holding ScanInitTextA needs to implement INotifyPropertyChanged and make ScanInitTextA public.
Secondly a better binding methodology is done by setting the page's data context to the class which has the INotifyPropertyChanged. That is done because it centralizes all of your data to be shown in one location and that a page's data context is inherited by all the controls, so at that point each of the page's controls simply bind to the property name. This is basic MVVM where the VM (view model) has the INotifyPropertyChanged.
Example
We want to show Members, a list of strings, on our page in a list box. Ultimately our binding will be just {Binding Members}.
View Model
public class MainVM : INotifyPropertyChanged
{
private List<string> _Members;
public List<string> Members
{
get { return _Members; }
set { _Members = value; OnPropertyChanged(); }
}
public MainVM()
{
// Simulate Asychronous access, such as to a db.
Task.Run(() =>
{
Members = new List<string>() {"Alpha", "Beta", "Gamma", "Omega"};
MemberCount = Members.Count;
});
}
/// <summary>Event raised when a property changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Raises the PropertyChanged event.</summary>
/// <param name="propertyName">The name of the property that has changed.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Page Codebehind
public partial class MainWindow : Window
{
public MainVM ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
// Set the windows data context so all controls can have it.
DataContext = ViewModel = new MainVM();
}
}
Page Xaml with binding
<ListBox Name="lbData"
ItemsSource="{Binding Members}"
SelectionMode="Multiple"
Margin="10" />
This example is taken from my blog article:
Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding.
Related
My ComboBox does not get populated with data.
Class Employee set to public, has variables such as:
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
Code on UserControl:
public IEnumerable<csEmployee> employeeList;
public ObservableCollection<csEmployee> _employeeSorted { get; set; }
public ucAddClient()
{
InitializeComponent();
//Establish connection
var GetMyData = new DataAccess();
//Get data by procedure
employeeList = GetMyDataPV.ExecuteStoredProc<csEmployee>("procedure", new {KeyDate = Key_to_extract});
employeeList = employeeList.Where(record => record.EmployeeLevelID > 300);
_employeeSorted = new ObservableCollection<csEmployee>(employeeList.Where(record => record != null));
}
And WPF:
<ComboBox x:Name="cbAddManager"
Foreground="#FF4D648B"
FontSize="12"
IsEditable="True"
ItemsSource="{Binding _employeeSorted}"
DisplayMemberPath="FirstName"
PreviewKeyDown="cbAddManager_PreviewKeyDown"
Width="200">
<!--<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width ="50" Text="{Binding LastName}"/>
<TextBlock Text=", "/>
<TextBlock Width ="50" Text="{Binding FirstName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>-->
</ComboBox>
Do you have any idea, why ComboBoxis not populated? When I do this in code (I add it in user control class) it gets data needed.
Im not sure if Im binding it correctly?
That is because you assign a new instance of a collection to your _employeeSorted property after InitializeComponent. At that time, the binding is already set up and does not get notified that you have updated the property from null, because you do not implement INotifyPropertyChanged.
There are multiple ways to solve the issue:
Initialize the collection before InitializeComponent and work on this same collection if you intend to change it, using Clear and Add instead of creating a new instance on changes.
Implement the INotifyPropertyChanged interface and use it to notify changes to your property so that the bindings are updated the the changes are applied in the user interface, e.g.:
public partial class MyUserControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<csEmployee> _employeeSortedField;
public ObservableCollection<csEmployee> _employeeSorted
{
get => _employeeSortedField;
set
{
if (_employeeSortedField == value)
return;
_employeeSortedField = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Expose a depenedency property for the collection instead and bind it to a collection in your view model that is passed as data context of the UserControl, thus moving the data access out it and separating the view from the business logic and data (recommended, see below MVVM).
Another issue might be that you do not set your data context to the UserControl itself in XAML (which is not recommened by the way, although it might solve your issue). In this case, the binding is unable to resolve the property at runtime (a binding error will be shown in the output window).
<UserControl x:Class="YourProject.YourControl"
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
As a note, it seems that you mix your business logic with your UserControl (view). Leverage the MVVM design pattern to create view models and seprate both concerns instead. Furthermore, if you set the data context of your UserControl to itself, you break data context inheritance.
The scenario is very simple here. I'm trying to bind a textbox to a property of a class at runtime:
tb.displayValue.DataContext = p.GetValue(currentNode, null);
xaml for the textbox:
<TextBox Name="displayValue" Grid.Column="1"
Style="{StaticResource propertyTextBoxStyle}"
Text="{Binding Path=DataContext,
RelativeSource={RelativeSource Self},
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
</TextBox>
When the application starts, the textbox does get populated with the correct information so the source to target binding is working fine. However, when I try and edit a value in the textbox and then switch focus, the changes are not reflected in the actual data structure. The value would stay on the UI, but as soon as I try to reload the UI from the data structure again it defaults back to the original value.
I suspect the binding is not working correctly at first, but after checking the memory address of tb.displayValue.DataContext and comparing it to the actual memory address of the data structure it's an identical match.
INotifyPropertyChanged has been implemented and I have added the OnPropertyChanged call to every setter. After spending two days trying to debug this issue I think I'm really running out of options here so any suggestion would be appreciated.
The simplest two-way binding works this way: you set the DataContext on your Window to a new instance of your MainWindowViewModel class which implements INotifyPropertyChanged, and you set the binding path on your TextBox to the name of the public property on your ViewModel you want to bind to.
I'm trying to show how you need a public property with a get and set to bind to, and how to properly set the DataContext for your window so that all of the controls within it are able to bind to the public properties available on it.
I've never heard of setting the DataContext of a TextBox directly to the return value from a method before, and it just seems wrong, so maybe you are not going about it the right way, and hopefully this helps you see how it can work.
MainWindow.cs
<Window x:Class="DemoWPFApp1.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:vm="clr-namespace:DemoWPFApp1.ViewModels"
Height="300" Width="460" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<TextBox Name="displayValue" Text="{Binding Path=BoundProperty,
Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
</TextBox>
</Window>
MainWindowViewModel.cs
namespace DemoWPFApp1.ViewModels
{
public class MainWindowViewModel : BaseViewModel
{
private string m_boundProperty;
public string BoundProperty
{
get
{
return m_boundProperty;
}
set
{
m_boundProperty = value; OnPropertyChanged();
}
}
public MainWindowViewModel()
{
BoundProperty = "Some value.";
}
}
}
BaseViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace DemoWPFApp1.ViewModels
{
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propName = null)
{
var e = PropertyChanged;
if (e != null && propName != null)
{
e.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
}
}
I have three projects in my solution:
My main WPF Application which contains a MainWindow + MainViewModel
UserControl Library with a UserControl (ConfigEditorView)
UIProcess class with the ViewModel for the UserControl (ConfigEditorViewModel)
In my MainWindow I want to use the UserControl with the ViewModel of UIProcess.
First I set the UserControl in my MainWindow:
<TabItem Header="Editor">
<Grid>
<cel:ConfigEditorView DataContext="{Binding ConfEditModel, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</TabItem>
I don't know which of these properties I need here, so I put all together but it still doesn't work.
Then I've set this in my MainViewModel:
public ConfigEditorViewModel ConfEditModel { get; set; }
With simple method that is bound to a Button:
private void doSomething()
{
ConfEditModel = new ConfigEditorViewModel("Hello World");
}
My ConfigEditorViewModel looks basically like this:
public class ConfigEditorViewModel : ViewModelBase
{
private string _Description;
public string Description
{
get
{
return _Description;
}
set
{
_Description = value;
base.RaisePropertyChanged();
}
}
public ConfigEditorViewModel(string t)
{
Description = t;
}
}
The description is bound to a TextBox in my UserControl.
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,10" Text="{Binding Description}"/>
When I start the application and click the Button the TextBox should contain "Hello World" but it's empty.
What I've done wrong?
i gave you a general answer:
within a "real(a usercontrol you wanna use with different viewmodels with different property names)" usercontrol you bind just to your own DependencyProperties and you do that with ElementName or RelativeSource binding and you should never set the DataContext within a UserControl.
<UserControl x:Name="myRealUC" x:class="MyUserControl">
<TextBox Text="{Binding ElementName=myRealUC, Path=MyOwnDPIDeclaredInMyUc, Path=TwoWay}"/>
<UserControl>
if you do that you can easily use this Usercontrol in any view like:
<myControls:MyUserControl MyOwnDPIDeclaredInMyUc="{Binding MyPropertyInMyViewmodel}"/>
and for completeness: the Dependency Property
public readonly static DependencyProperty MyOwnDPIDeclaredInMyUcProperty = DependencyProperty.Register(
"MyOwnDPIDeclaredInMyUc", typeof(string), typeof(MyUserControl), new PropertyMetadata(""));
public bool MyOwnDPIDeclaredInMyUc
{
get { return (string)GetValue(MyOwnDPIDeclaredInMyUcProperty); }
set { SetValue(MyOwnDPIDeclaredInMyUcProperty, value); }
}
Your view models (and, optionally, models) need to implement INotifyPropertyChanged.
Binding's aren't magic. There is no inbuilt mechanism that allows for code to be notified when a plain old property's value changes. You'd have to poll it in order to check to see if a change happened, which would be very bad, performance-wise.
So bindings will look at the objects they are bound against and see if they implement INotifyPropertyChanged and, if so, will subscribe to the PropertyChanged event. That way, when you change a property and fire the event, the binding is notified and updates the UI.
Be warned, you must implement the interface and use it correctly. This example says it's for 2010, but it works fine.
I have this class :
public class property : DependencyObject, INotifyPropertyChanged
{
private string _myproperty;
public string MyProperty
{
get
{
return this._myproperty;
}
set
{
this._myproperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string sproperty)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sproperty));
}
}
}
In the main window I have created an instance of this class myclass xx = new myclass();, where I populate my property with string data and bind it to XAML like so:
<Window.Resources>
<local:property x:Key="prop"></local:property>
</Window.Resources>
In my TextBox i have set the binding :
Text="{Binding Path=MyProperty, Source={StaticResource prop}}" BorderBrush="#FFC7CACC" />
This will not work unless if i use the existing resources:
var property = (local:property)Resources["prop"];
Is there another way to update the TextBox rather than using the resources? I want to use the normal class instantiation.
if you say Text="{Binding Path=MyProperty, Source={StaticResource prop}}" BorderBrush="#FFC7CACC" />
means that your VM is an instance of property class.
Try to surround your textbox with a Grid and set the grid dataContext with an instance of your poperty clas.
I mean
<Grid DataContext="from view or from behind assign your vm= new property()">
<TextBox Text="{Binding Path=MyProperty" ....../>
</Grid>
Try this:
<Window.DataContext>
<local:property/>
<Window.DataContext>
<TextBox Text="{Binding MyProperty}"/>
After setting the data context, just try to build the application, the build will succeed if it can find the property class in the local namespace.
After building your app, if succeeded, you can try to set the binding and also the Intellisense will automatically show MyProperty in Binding Options.
If this doesn't work, try to set the data context and binding using the Properties panel. Maybe visually you can get things right.
Try it, and if it fails, tell me where it went wrong
Objective: Set the visibility of a control based on the selected value of a ComboBox
Issue: The property that is being used to check the visibility is in the VM, however I don't know how to use it as DataContext is already defined to another object, i.e would I need to bind 2 datacontexts?!
Details:
I have a CustomControl that I load in my view associating to it a DataContext (a List of objects that is displayed as a grid:
<GUI:Counterparties_UserInputs x:Name="UserInputs" DockPanel.Dock="Right" DataContext="{Binding Source={StaticResource counterpartiesDataView}}"/>
In that user control I have some StackPanel which visibility should be triggered based on the selection of a ComboBox:
<ComboBox ItemsSource="{Binding Source={StaticResource CounterpartyTypes}}" SelectedValue="{Binding SelectedCounterpartyType}"/>
<StackPanel Visibility="{Binding Path=SelectedCounterpartyType,Converter={StaticResource SelectedValueToVisible}}"/>
The issue I have is that the code behind is never hit as I don't find how to associate an "extra" DataContext to the view.
Here is my code behind:
public partial class Counterparties_UserInputs : UserControl
{
...
public Counterparties_UserInputs()
{
// this.DataContext = _cptyUserInputsVM;
_cptyUserInputsVM = new Counterparties_UserInputs_VM();
InitializeComponent();
}
}
And the ViewModel where the Property "SelectedCounterpartyType" is never hit:
public class Counterparties_UserInputs_VM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _selectedCounterpartyType;
public string SelectedCounterpartyType
{
get
{
return _selectedCounterpartyType;
}
set
{
_selectedCounterpartyType = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedCounterpartyType"));
}
}
}
}
I've seen that answer already but it's not exactly what I am doing... So would really appreciate your help! Thank you!