Two Way binding not working when set via Code behind - c#

I am creating dynamic control in code behind and setting it's visibility property binding to the property in the code behind. But when the property value is changed, it's not updating visibility of the control.
Binding:
Binding assetsVisibilityBinding = new Binding();
assetsVisibilityBinding.Source = this;
assetsVisibilityBinding.Path = new PropertyPath("IsLocalSearchEnabled");
assetsVisibilityBinding.Mode = BindingMode.TwoWay;
assetsVisibilityBinding.Converter = Resources["BooleanToVisibilityConverter"] as IValueConverter;
assetsStackPanel.SetBinding(StackPanel.VisibilityProperty, assetsVisibilityBinding);
Property(Using fody):
public bool IsLocalSearchEnabled { get; set; }

maybe your class which contains the property needs to implement the interface
INotifyPropertyChanged
Let's assume your class name be A
then snippet will be
class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public bool isLocalSearchEnabled = false;
public bool IsLocalSearchEnabled
{
get { return isLocalSearchEnabled ;}
set { isLocalSearchEnabled = value; this.OnPropertyChanged("IsLocalSearchEnabled");
}
}
What happens here when you implement INotifyPropertyChanged is the event PropertyChanged
is triggered when the value of isLocalSearchEnabled is set (regardless of old value and new value) and OnPropertyChanged is called with the name of Public property

It seems you have not implemented INotifyPropertyChanged interface, please see the detailed example INotifyPropertyChanged

Did you set the assetsStackPanel DataContext the binding need the source ,that you should set the DataContext ont only set the source.
If you set the property in this in xaml.cs that you should make it public.
Binding assetsVisibilityBinding = new Binding();
assetsVisibilityBinding.Source = this;
assetsVisibilityBinding.Path = new PropertyPath("IsLocalSearchEnabled");
assetsVisibilityBinding.Mode = BindingMode.TwoWay;
assetsVisibilityBinding.Converter = Resources["BooleanToVisibilityConverter"] as IValueConverter;
assetsStackPanel.DataContex=this;
assetsStackPanel.SetBinding(StackPanel.VisibilityProperty, assetsVisibilityBinding);
For I have not read frameWork ,I think you can try use property by INotifyPropertyChanged to know whether code is right.
And you can use BindingOperations.SetBinding
Try use resharper in xaml and write Visibility="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=IsLocalSearchEnabled}",mode=TwoWay.If it can work that is mean the FrameWork can work.

Thanks. But as mentioned in the post I am actually using
Fody(github.com/Fody/PropertyChanged). Which automatically implements
that
I have checked the complied class, by using Fody PropertyChanged the property changed notification wasn't successfully implemented.
[ImplementPropertyChanged]
public sealed partial class MainPage : Page
{
public bool IsLocalSearchEnabled { get; set; }
public MainPage()
{
this.InitializeComponent();
SetBinding();
this.DataContext = this;
}
public void SetBinding()
{
Binding assetsVisibilityBinding = new Binding();
assetsVisibilityBinding.Source = this;
assetsVisibilityBinding.Path = new PropertyPath("IsLocalSearchEnabled");
assetsVisibilityBinding.Mode = BindingMode.TwoWay;
assetsVisibilityBinding.Converter = Resources["BooleanToVisibilityConverter"] as IValueConverter;
assetsStackPanel.SetBinding(StackPanel.VisibilityProperty, assetsVisibilityBinding);
}
}
I would suggest you reporting issue to Fody to fix it.
The standard way as follows:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
bool isLocalSearchEnabled;
public bool IsLocalSearchEnabled
{
get { return isLocalSearchEnabled; }
set
{
if (value != isLocalSearchEnabled)
{
isLocalSearchEnabled = value;
OnPropertyChanged("IsLocalSearchEnabled");
}
}
}
public MainPage()
{
this.InitializeComponent();
SetBinding();
this.DataContext = this;
}
public void SetBinding()
{
Binding assetsVisibilityBinding = new Binding();
assetsVisibilityBinding.Source = this;
assetsVisibilityBinding.Path = new PropertyPath("IsLocalSearchEnabled");
assetsVisibilityBinding.Mode = BindingMode.TwoWay;
assetsVisibilityBinding.Converter = Resources["BooleanToVisibilityConverter"] as IValueConverter;
assetsStackPanel.SetBinding(StackPanel.VisibilityProperty, assetsVisibilityBinding);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Or you can easily use a wrapper class: BindableBase

Related

WPF C# PropertyChanged always null

I have played around with this for a while and decided to see if someone can help, I have set in the constructor of StatusInfo the DataContext = this and didn't work. When I write a string to ScreenStatusBarText it does call the OnPropertyChanged method but every time the PropertyChanged value is null. I The status block I have at the bottom of the screen. I have a tab section above this stack panel that has many components that use bindings and work.
Screen Code
<StackPanel Margin="0,1047,0,0">
<Grid Name="StatusBarItemGrid">
<TextBlock Name="StatusBarText" Text="may the force be with you" VerticalAlignment="Center" HorizontalAlignment="Stretch" />
</Grid>
</StackPanel>
Data Model:
public partial class StatusInfo : INotifyPropertyChanged
{
private string screenStatusBarText;
public StatusInfo()
{
BindScreenStatusBarText();
screenStatusBarText = "Initialized";
}
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged("StatusBarText");
}
}
private void BindScreenStatusBarText()
{
Binding b = new Binding();
b.Source = screenStatusBarText;
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Path = new PropertyPath("StatusBarText");
MainWindow.mainWindow.StatusBarText.SetBinding(TextBlock.TextProperty, b);
MainWindow.mainWindow.StatusBarText.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
My main :
public partial class MainWindow : Window
{
public static StatusInfo status;
public MainWindow()
{
InitializeComponent();
SourceInitialized += MainWindow_SourceInitialized;
}
private void MainWindow_SourceInitialized(object sender, EventArgs e)
{
SetUpDisplay();
}
private void SetUpDisplay()
{
status = new StatusInfo();
}
}
Set the Binding in XAML instead of code behind:
<TextBlock Text="{Binding ScreenStatusBarText}" />
And use a view model like
public class StatusInfo : INotifyPropertyChanged
{
private string screenStatusBarText = "Initialized";
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged(nameof(ScreenStatusBarText));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
}
with an instance of the view model class assigned to the MainWindow's DataContext:
private readonly StatusInfo statusInfo = new StatusInfo();
public MainWindow()
{
InitializeComponent();
DataContext = statusInfo;
}
You may now access the view model class at any time later, e.g. in an event handler of an element of MainWindow:
statusInfo.ScreenStatusBarText = "Something";
I think your going to struggle doing your binding in code behind.
Having said that, with regards to why your PropertyChanged value is null. You've simply made a typo, as-is you're notifying subscribers that a property that doesn't exist has changed. One solution to avoid such typos is to use nameof.
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged(nameof(ScreenStatusBarText));
}
}
It occurred to me you may also have meant that your event was null. This simply means you don't have any subscribers. See Why is my "Event" always null?.
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) // I have a subscriber.
handler(this, new PropertyChangedEventArgs(propertyName));
}

UWP MVVM Data Binding for dummies (textbox.text from String)

Well, having a go at MVVM with UWP template 10. I have read many pages, and although everyone tries to say its really easy, I still can't make it work.
To put it into context, OCR is being run on an image, and I would like the text to be displayed in textbox automatically.
Here is my Model:
public class TextProcessing
{
private string _ocrText;
public string OcrText
{
get { return _ocrText; }
set
{
_ocrText = value;
}
}
}
Here is my ViewModel:
public class ScanPageViewModel : ViewModelBase, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private TextProcessing _ocrTextVM;
public ScanPageViewModel()
{
_ocrTextVM = new TextProcessing();
}
public TextProcessing OcrTextVM
{
get { return _ocrTextVM; }
set {
_ocrTextVM = value;
this.OnPropertyChanged("OcrTextVM");
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Here is my View:
<TextBox x:Name="rtbOcr"
Text="{Binding OcrTextVM.OcrText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Firstly, that is not working. Could someone try to show where I am going wrong?
Then, the data is coming from a Services file, how would the Services update the value? What would be the correct code?
Thanks in advance.
Following code is cite from code.msdn (How to achieve MVVM design patterns in UWP), it will be helpful for you:
Check you code step by step.
1.ViewModel implemented interface INotifyPropertyChanged,and in property set method invoked PropertyChanged, like this:
public sealed class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
_productName = value;
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(ProductName)));
}
}
}
}
2.Initialize you ViewMode in you page, and set DataContext as the ViewMode, like this:
public sealed partial class MainPage : Page
{
public MainPageViewModel ViewModel { get; set; } = new MainPageViewModel();
public MainPage()
{
...
this.DataContext = ViewModel;
}
}
3.In you xaml, binding data from viewMode, like this:
<TextBox Text="{Binding Path=ProductName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="ProductNameTextBox" TextChanged="ProductNameTextBox_TextChanged" />
Your OnPropertyChanged call on OcrTextVM isn't actually called in your case, since you set the value in the constructor to its backing field and bypass the property.
If you set the value via the property, it should work:
public ScanPageViewModel()
{
OcrTextVM = new TextProcessing();
}
Of course your view needs to know that ScanPageViewModel is its DataContext. Easiest way to do it is in the constructor of the code-behind of your view:
public OcrView()
{
DataContext = new ScanPageViewModel();
InitializeComponent();
}
Assuming your OCR service is returning a new TextProcessing object on usage, setting the property of OcrTextVM should suffice:
public class ScanPageViewModel : ViewModelBase, INotifyPropertyChanged
{
//...
private void GetOcrFromService()
{
//...
TextProcessing value = OcrService.Get();
OcrTextVM = value;
}
}
On a note, the OcrTextVM name doesn't really reflect what the property is doing, since it doesn't look like it's a viewmodel. Consider renaming it.
Actually, it is very easy once I manage to understand. Here is the code needed to update a TextBox.Text
In the Models:
public class DisplayText : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
}
In the XAML file:
<TextBox Text="{Binding Helper.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ... />
In the ViewModels:
private DisplayText _helper = new DisplayText();
public DisplayText Helper
{
get { return _helper; }
set
{
_helper = value;
}
}
Then any mod from the ViewModels:
Helper.Text = "Whatever text, or method returning a string";

INotifyPropertyChanged not firing on ViewModel inside ItemsControl

I'm using ObservableCollection<MyItemViewModel> myItemVMList as the ItemsSouce. I am able to bind Command perfectly but the INotifyPropertyChanged isn't working. Here's my code:
public class MyItemViewModel: INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
public MyItem MyItem { set; get; }
private RelayCommand _ChangeMyItemPropertyValue;
public ICommand ChangeMyItemPropertyValueCommand {
get {
if (_ChangeMyItemPropertyValue == null) _ChangeMyItemPropertyValue = new RelayCommand(o => ChangeMyItemPropertyValue());
return _ChangeMyItemPropertyValue;
}
}
private ChangeMyItemPropertyValue() {
MyItem.SomeProperty = someDifferentValue;
// NEITHER OF THESE CALLS WORK
OnPropertyChanged("MyItem.SomeProperty");
OnPropertyChagned("SomeProperty");
}
}
Needless to say, the binding is set as Content="{Binding MyItem.SomeProperty}" inside the DataTemplate, and it shows the correct value. Problem is it isn't updated when I run the function.
SideNote: If I implement the INotifyPropertyChanged inside MyItem it works, but I want it on the ViewModel.
If I implement the INotifyPropertyChanged inside MyItem it works, but I want it on the ViewModel
Yes, because that's how it's designed. How it's supposed to know it should listen to your ViewModel's property changed event? It doesn't bind to it, it binds to the model, so it listens to the changes on the model.
You have two choices basically:
Implement INotifyPropertyChanged on MyItem
Bind to the ViewModel
Content="{Binding SomeProperty}"
And add a wrapper property:
public string SomeProperty
{
get { return MyItem.SomeProperty; }
set
{
MyItem.SomeProperty = value;
OnPropertyChanged("SomeProperty");
}
}
You should prefer binding to the ViewModel if you want to follow MVVM practices.
Side note: If you add [CallerMemberName] to OnPropertyChanged like this:
protected void OnPropertyChanged([CallerMemberName] string name = null) {
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
You'll be able to skip the property name altogether:
public string SomeProperty
{
get { return MyItem.SomeProperty; }
set
{
MyItem.SomeProperty = value;
OnPropertyChanged(); // <-- no need for property name anymore
}
}

Is it possible to bind to the ColumnSpan property?

I am having trouble binding to the ColumnSpan property. After many hours of debugging, I am wondering if it is even possible.
public partial class MainPage : PhoneApplicationPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int myProperty;
public MainPage()
{
InitializeComponent();
Binding binding = new Binding("MyProperty");
binding.Source = this;
btn0.SetBinding(Grid.ColumnSpanProperty, binding);
}
public int MyProperty
{
get
{
return myProperty;
}
set
{
myProperty = value;
OnPropertyChanged("MyProperty");
}
}
protected virtual void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Is it possible to bind to Grid.Column but not Grid.ColumnSpan? If it's possible with ColumnSpan, what am I doing wrong?
Thx a lot, Martin, that was it. Compiling fails if property is Zero.
So sick, I lost a day because of this.

Why doesn't the INotifyPropertyChanged work?

I used a Multibinding to bind some properties and use the INotifyPropertyChanged interface to notify these properties'changes.But sadly,it seems that the INotifyPropertyChanged didn't work. The PropertyChangedEventHandler "PropertyChanged" was null all the time.
Questions:
A. Could you please tell me why the event is null?In my mind,there should be a default method for the event PropertyChangedEventHandler,am I wrong?(Resolved,thanks!)
B. Just like some friends said,I tried again without using the INotifyPropertyChanged.But the target's property's value seems to be not changed...
Properties
public static readonly DependencyProperty LeftOffsetProperty = DependencyProperty.Register("LeftOffset", typeof(double), typeof(NetworkTaskLable), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty TopOffsetProperty = DependencyProperty.Register("TopOffset", typeof(double), typeof(NetworkTaskLable), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));
public double LeftOffset
{
get { return (double)GetValue(LeftOffsetProperty); }
set { SetValue(LeftOffsetProperty, value); }
}
public double TopOffset
{
get { return (double)GetValue(TopOffsetProperty); }
set { SetValue(TopOffsetProperty, value); }
}
Multibinding (It seems work well.By using the converter to calculate a location which is affected by "TopOffset" and "LeftOffset")
var multibinding = new MultiBinding() { Converter = new BeginAndStartDateToLeftConverter_NetworkTaskLable(), ConverterParameter = NetworkView };
multibinding.Bindings.Add(new Binding("Start"));
multibinding.Bindings.Add(new Binding("StartDate") { Source = NetworkView });
multibinding.Bindings.Add(new Binding("LeftOffset") { Source = this });
MainCanvas.SetBinding(LeftProperty, multibinding);
INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void CallPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)//It seems to be null all the time!!!And the properties'changes were never notified!!!
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
Notify the change
SetValue(LeftOffsetProperty, moveAdorner.LeftOffset);
CallPropertyChanged("LeftOffset");
SetValue(TopOffsetProperty, moveAdorner.TopOffset);
CallPropertyChanged("TopOffset");
You don't need to implement INotifyPropertyChanged for dependency properties. Binding will track changes of these properties automatically.
Set Mode=TwoWay anywhere in your binding ?
You need to override OnPropertyChanged and check to see if the property is the one you are looking for.
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property == MyProperty)
{
// do something
}
base.OnPropertyChanged(e);
}

Categories

Resources