WPF: Binding to a custom class "Cannot find source for binding" - wpf

Did this:
public class myClass : INotifyPropertyChanged
{
public bool? myFlag = false;
public bool? MyFlag
{
get { return myFlag; }
set
{
myFlag = value;
OnPropertyChanged("MyFlag");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
Declared a test variable myClass in the Window1 class:
public partial class Window1 : Window
{
myClass test;
public Window1()
{
InitializeComponent();
test = new myClass();
}
}
Here's an example XAML file:
<Window x:Class="WpfApplication5.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" IsEnabled="{Binding ElementName=test, Path=MyFlag}">
<Grid>
<Button>You shouldn't be clicking me</Button>
</Grid>
</Window>
The window isn't disabled, and the debugger is showing me that message.
What am I missing?

The ElementName property of the Binding is meant to target other elements in xaml, not properties/fields of the object. The common way to do what you're trying to accomplish is to assign an instance of myClass to the Window's DataContext property:
public partial class Window1 : Window
{
//myClass test;
public Window1()
{
InitializeComponent();
this.DataContext = new myClass(); //test = new myClass();
}
}
And then your binding will look like this: IsEnabled="{Binding Path=MyFlag}".
If you actually wanted to bind to a property on the Window itself, you would use a binding like this:
IsEnabled="{Binding RelativeSource={RelativeSource Self}, Path=test.MyFlag}"

Related

PropertyChanged remains null even if the property has been changed

I am struggling with this for a while and I cannot figure it out. I have a button and a textBox. The textBox is linked to a property named: MessageDisplay. I want to be able to access this property and update the textBox in several places. Sadly, the PropertyChanged is null. The weird thing is that if I copy/paste the MessageDisplayModel class into the *MessageViewModel * class, it works ...
here is my code :
XAMLfile :
<Grid>
<Button Command="{Binding DisplayTextCommand}" Name="DisplayTextCommand" Margin="53,72,544.6,286" Width="Auto">Push</Button>
<TextBox Name="MessageDisplay" Text="{Binding MessageDisplay, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</Grid>
MessageDisplayModel file
public class MessageDisplayModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _message;
public string MessageDisplay
{
get { return _message; }
set
{
this._message = value;
this.OnPropertyChanged("MessageDisplay");
}
}
public void UpdateTextBox(string output)
{
MessageDisplay = output;
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}//class
MessageViewModel file:
public class MessageViewModel
{
private ICommand _testCommand;
public MessageDisplayModel MessageDisplaySmt = new MessageDisplayModel();
public ICommand DisplayTextCommand
{
get
{
return new DelegateCommand(DisplayMessage);
}
set
{
if (_testCommand == value) return;
_testCommand = value;
}
}
public void DisplayMessage()
{
MessageDisplaySmt.UpdateTextBox("Successfuly downloaded");
}
}//class
MainWindow file
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MessageDisplay.DataContext = new MessageDisplayModel();
DisplayTextCommand.DataContext = new MessageViewModel();
}
}//class
I update the MessageDisplay property by using the method UpdateTextBox(string). I call this method on the click of the button. When debugging the property gets updated but when time comes to notify the UI that the property has changed, PropertyChangedEventHandler PropertyChanged has its value null ... But if I write something in the textBox, the PropertyChangedEventHandler PropertyChanged gets changed and isn't null anymore. All I want is to be able to change the textBox's property whenever I want and from anywhere I want to.
Thank you
You are using two different instances of MessageDisplayModel. You must use a shared instance.
Also the DisplayTextCommand is implemented "wrong". The set method is redundant as the property's get always returns a new instance of the ICommand.
MessageViewModel.cs
public class MessageViewModel
{
pulic MessageViewModel()
{
}
pulic MessageViewModel(MessageDisplayViewModel messageDisplayViewModel)
{
this.MessageDisplaySmt = messageDisplayViewModel;
}
public void DisplayMessage()
{
this.MessageDisplaySmt.UpdateTextBox("Successfuly downloaded");
}
public MessageDisplayViewModel MessageDisplaySmt { get; set; }
public ICommand DisplayTextCommand { get => new DelegateCommand(DisplayMessage); }
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Alternatively use XAML to set the DataContext (see MainWindow.xaml). Would require a parameterless constructor.
this.DataContext = new MessageViewModel(new MessageDisplayViewModel());
}
}
MainWindow.xaml
<Window>
<!--
Alternative DataContext declaration using XAML instead of C#.
Requires a parameterless constructor for both view model objects.
-->
<Window.DataContext>
<MessageViewModel>
<MessageViewModel.MessageDisplaySmt>
<MessageDisplayViewModel />
</MessageViewModel.MessageDisplaySmt>
</MessageViewModel>
</Window.DataContext>
<StackPanel>
<Button Command="{Binding DisplayTextCommand}"
Content="Push" />
<TextBox Text="{Binding MessageDisplaySmt.MessageDisplay}" />
</StackPanel>
</Window>

WPF Simple Binding to INotifyPropertyChanged Object

I've created the simplest binding. A textbox bound to an object in the code behind.
Event though - the textbox remains empty.
The window's DataContext is set, and the binding path is present.
Can you say what's wrong?
XAML
<Window x:Class="Anecdotes.SimpleBinding"
x:Name="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SimpleBinding" Height="300" Width="300" DataContext="MainWindow">
<Grid>
<TextBox Text="{Binding Path=BookName, ElementName=TheBook}" />
</Grid>
</Window>
Code behind
public partial class SimpleBinding : Window
{
public Book TheBook;
public SimpleBinding()
{
TheBook = new Book() { BookName = "The Mythical Man Month" };
InitializeComponent();
}
}
The book object
public class Book : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
private string bookName;
public string BookName
{
get { return bookName; }
set
{
if (bookName != value)
{
bookName = value;
OnPropertyChanged("BookName");
}
}
}
}
First of all remove DataContext="MainWindow" as this sets DataContext of a Window to a string MainWindow, then you specify ElementName for your binding which defines binding source as another control with x:Name="TheBook" which does not exist in your Window. You can make your code work by removing ElementName=TheBook from your binding and either by assigning DataContext, which is default source if none is specified, of a Window to TheBook
public SimpleBinding()
{
...
this.DataContext = TheBook;
}
or by specifying RelativeSource of your binding to the Window which exposes TheBook:
<TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=TheBook.BookName}"/>
but since you cannot bind to fields you will need to convert TheBook into property:
public partial class SimpleBinding : Window
{
public Book TheBook { get; set; }
...
}

Can you have an event setter bound to an event of the data context?

Im trying to create something like this -
I have an observable collection of points. Each point has a position and a colour. When any points position or colour changes(they implement notification change), I want to "repaint" the background gradient. Currently I have an itemscontrol where I have the sliders bound to the points position and the gradient is initially drawn. Now, I want to know how I can call a function in the code behind of my view when the propertychanged event on a 'point' fires, so that I can repaint the gradient. Im wondering if an event setter can somehow be used?
Whilst I could do the propertychanged event subscribing in code behind, I'd like to do it in XAML?
PLease note : I specifically want to take this approach of manually repainting in code behind for other reasons, so if I could get answers to the specific problem above rather than alternative solutions please.
I guess you can create an attached property to subscribe to PropertyChanged events of the value of the DataContext property.
public static class Props
{
public static DependencyProperty OnPropertyChangedProperty = DependencyProperty.RegisterAttached(
"OnPropertyChanged", typeof(PropertyChangedEventHandler), typeof(Props),
new PropertyMetadata(OnPropertyChangedPropertyChanged));
public static PropertyChangedEventHandler GetOnPropertyChanged (DependencyObject d)
{
return (PropertyChangedEventHandler)d.GetValue(OnPropertyChangedProperty);
}
public static void SetOnPropertyChanged (DependencyObject d, PropertyChangedEventHandler value)
{
d.SetValue(OnPropertyChangedProperty, value);
}
private static void OnPropertyChangedPropertyChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var inpc = (INotifyPropertyChanged)((FrameworkElement)d).DataContext;
if (inpc == null)
throw new ArgumentException("DataContext of the framework element must not be null.");
var oldChanged = (PropertyChangedEventHandler)e.OldValue;
if (oldChanged != null)
inpc.PropertyChanged -= oldChanged;
var newChanged = (PropertyChangedEventHandler)e.NewValue;
if (newChanged != null)
inpc.PropertyChanged += newChanged;
}
}
Usage:
<Window x:Class="So17382721PropertyChangedXaml.MainWindow" x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:So17382721PropertyChangedXaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Foo}">
<!-- Here, we subscribe to DataContext.PropertyChanged;
handler is defined in the MainWindow class -->
<Grid local:Props.OnPropertyChanged="{Binding FooPropertyChanged, ElementName=root}">
<TextBox Text="{Binding Bar, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Foos, ElementName=root}"/>
</Grid>
</Window>
Code-behind:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace So17382721PropertyChangedXaml
{
public partial class MainWindow
{
public ObservableCollection<Foo> Foos { get; private set; }
public MainWindow ()
{
Foos = new ObservableCollection<Foo> {
new Foo { Bar = "1" },
new Foo { Bar = "2" },
new Foo { Bar = "3" },
};
InitializeComponent();
}
private void OnFooPropertyChanged (object sender, PropertyChangedEventArgs e)
{
MessageBox.Show(this, string.Format("{0} of {1} changed.", e.PropertyName, sender));
}
// Subscribing to non-RoutedEvents in XAML is not straightforward, but we can define a property
public PropertyChangedEventHandler FooPropertyChanged
{
get { return OnFooPropertyChanged; }
}
}
public class Foo : INotifyPropertyChanged
{
private string _bar;
public string Bar
{
get { return _bar; }
set
{
_bar = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Note: the attached property Props.OnPropertyChanged expects that DataContext is not changed during lifetime and is already specified. Handling DataContextChanged events is left as an exircize, if you need it.

Basic Silverlight Binding

I can't figure out why the binding changes in my large project wont work. I have simplified it down to a sample project that still doesn't work. I would like to continue to set the datacontext the way I currently am if possible because that is how the other project does it. With the following code the text in SomeText is not showing up in the textbox. How do I fix this?
Code Behind:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
Data Class:
public class ViewModel
{
public string SomeText = "This is some text.";
}
Main User Control:
<UserControl xmlns:ig="http://schemas.infragistics.com/xaml" x:Class="XamGridVisibilityBindingTest.MainPage"
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:XamGridVisibilityBindingTest="clr-namespace:XamGridVisibilityBindingTest" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<TextBox Text="{Binding SomeText}" BorderBrush="#FFE80F0F" Width="100" Height="50"> </TextBox>
</Grid>
</UserControl>
Edit: I am only trying to do one-way binding.
You need to use a property, and make your VM inherit from INotifyPropertyChanged and raise the PropertyChanged event whenever SomeText changes:
public class ViewModel : INotifyPropertyChanged
{
private string someText;
public event PropertyChangedEventHandler PropertyChanged;
public string SomeText
{
get { return someText; }
set
{
someText = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SomeText"));
}
}
}
public ViewModel()
{
SomeText = "This is some text.";
}
}
I figured it out, you can only bind to Properties!
public class ViewModel
{
public string SomeText { get; set; }
public ViewModel()
{
SomeText = "This is some text.";
}
}

Binding is not working with dynamic data updates

I'm new in WPF and I have the following problem.
I have the following class with many properties , but here is only one property for example:
public class StatusData : INotifyPropertyChanged
{
private string m_statusText = String.Empty;
public StatusData()
{
m_statusText = "1234";
}
public string StatusText
{
get
{
return m_statusText;
}
set
{
if (m_statusText != value)
{
m_statusText = value;
NotifyPropertyChanged("StatusText");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Another component of the project changes StatusData and calls Update() function in MainWindow.
So, m_statusData of this MainWindow has changed and I want update the textbox with m_statusText accordingly.
public class MainWindow
{
private StatusData m_statusData = new StatusData();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
grid1.DataContext = m_statusData ;
}
public void Update(StatusData newStatusData)
{
m_statusData = newStatusData;
}
}
Xaml code:
<Window x:Class="WpfApplicationUpdateTextBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="myWin"
xmlns:local="clr-namespace:WpfApplicationUpdateTextBox"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" >
<Grid Name="grid1">
<TextBox Text="{Binding Path=StatusText}" Name="textBox1" />
</Grid>
</Window>
The question is : why the textBox is not updated withnewStatusData.StatusText?
Here, you are assigning the grid's DataContext to m_statusData:
grid1.DataContext = m_statusData ;
And here, you are reassigning m_statusData to something else:
m_statusData = newStatusData;
The problem is that this has no effect on grid1.DataContext, which was set to the previous instance of m_statusData.
In this case, doing grid1.DataContext = newStatusData should solve your problem. However, a better solution would be to create a StatusData property which returns m_statusData. You can then do a RaisePropertyChanged() on it when m_statusData changes.
private void Update(StatusData newStatusData)
{
StatusData = newStatusData;
}
public StatusData StatusData
{
get
{
return m_statusData;
}
set
{
m_statusData = value;
RaisePropertyChanged("StatusData");
}
}
... and then in your XAML, bind your Grid's DataContext to the StatusData property
Edit:
To bind the grid's data context to the StatusData property, you can do this in your XAML:
<Grid Name="grid1" DataContext="{Binding StatusData}">
<TextBox Text="{Binding Path=StatusText}" Name="textBox1" />
</Grid>
You will also need to set the initial DataContext of your window, to make all of the other databindings work (this is a little strange and non-standard, but it will do the trick):
this.DataContext = this;
Your class StatusData has only 1 property: StatusText. If this property is the only thing you want to change with this code:
m_statusData = newStatusData;
You can change it to this:
m_statusData.StatusText = newStatusData.StatusText;
This code will fire the PropertyChangedEvent of StatusData class and that will change TextBox value.

Resources