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));
}
Related
My tooltip should show how long my program is running. So I try to add +1 to my tooltip, but that doesn't work.
That is my xaml code:
<StatusBarItem >
<Image ToolTip="{Binding Path=ToolTipStatus}"/>
</StatusBarItem>
And thats my C# code:
private string _toolTipStatus = "0";
private string ToolTipStatus
{
get { return _toolTipStatus; }
}
private void Example()
{
_toolTipStatus = _toolTipStatus + 1;
}
First, nowhere in this code is there any reason for the UI to guess when or if your private field has changed. Second, your property is private too, so the UI can't see it either. Finally, repeatedly appending "1" to a string is going to get you a string that looks like "11111111111111111111111111111" after the timer fires a few times. If that's what you want, that's fine, but I think it might not be.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyViewModel : ViewModelBase
{
private int _toolTipStatus = 0;
private int ToolTipStatus
{
get { return _toolTipStatus; }
protected set {
if (_toolTipStatus != value)
{
_toolTipStatus = value;
OnPropertyChanged(nameof(ToolTipStatus));
}
}
}
}
private void Example()
{
ToolTipStatus += 1;
}
You won't say if you've got a viewmodel. You won't say what class your code is in or how (or if) it gets called. All your properties are private. You won't say what the XAML looks like or even if there is any. I sense a theme of obsessive secrecy here. You need to learn when to open up and share.
And you need a viewmodel, and you need it to implement INotifyPropertyChanged.
You should refresh your xaml someway. The best way I think is inheriting the form from INotifyPropertyChanged.
Then declare the event and the raise method like this
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropChanged(string name)
{
var eh = this.PropertyChanged;
if (eh != null)
{
eh(this, new PropertyChangedEventArgs(name));
}
}
then your property ToolTipStatus should be this:
private string toolTipStatus;
public string ToolTipStatus
{
get { return toolTipStatus; }
set
{
toolTipStatus = value;
RaisePropChanged("ToolTipStatus");
}
}
I don't understand why my rectangles are not being shown.
I made the xaml, and data binded the canvas, and init properly.
What am I missing such that it only shows a blank screen.
It should show a digital figure 8.
MODEL:
namespace Final
{
class Model : INotifyPropertyChanged
{
// define our property chage event handler, part of data binding
public event PropertyChangedEventHandler PropertyChanged;
// implements method for data binding to any and all properties
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private double _topTopHorizontal;
public double topTopHorizontal
{
get { return _topTopHorizontal; }
set
{
_topTopHorizontal = value;
OnPropertyChanged("topTopHorizontal");
}
}
private double _leftTopHorizontal;
public double leftTopHorizontal
{
get { return _leftTopHorizontal; }
set
{
_leftTopHorizontal = value;
OnPropertyChanged("leftTopHorizontal");
}
}
public void initModel()
{
topTopHorizontal = 50;
leftTopHorizontal = 50;
}
}
}
Main
public partial class MainWindow : Window
{
private Model model;
public MainWindow()
{
InitializeComponent();
}
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// create an instance of our Model
model = new Model();
model.initModel();
}
}
}
You haven't set the DataContext for this window.
In constructor add:
public MainWindow()
{
InitializeComponent();
model = new Model();
DataContext = model;
}
Therefor, your window can access "leftTopHorizontal" and "topTopHorizontal".
And in your xaml change:
Canvas.Top ="{Binding topTopHorizontal}"
Canvas.Left="{Binding leftTopHorizontal}"
with:
Canvas.Top ="{Binding model.topTopHorizontal}"
Canvas.Left="{Binding model.leftTopHorizontal}"
I have the visibility of a progress bar bound to The following property within my viewmodel:
public string CalcProgVisibility
{
get
{
return Calculation.CalcProgVisibility;
}
set
{
}
}
Calculation is my model, which can change the value. When the value changes within the model, what do I need to do to make sure the view is aware of this change?
EDIT:
Here is the property within my model too. I am using onpropertychanged but its not making it to the view.
I am changing the value within the model, the view is bound to my viewmodel and the viewmodel si trying to return a value taken from the model. I am updating the value on the model, and cannot push the fact that it has updated the value all the way down to the view, I can only get the viewmodel to see it has changed...
I updated the entire code. I hope it's clear now.
Define your control BindingMode = TwoWay
<TextBox Visibility="{Binding Path=CalcProgVisibility, Mode=TwoWay}"...
and call the OnPropertyChanged method on the setter of the property in your view model and also in your model
//Model
public class Calculation : INotifyPropertyChanged
{
private string _calcProgVisibility;
public string CalcProgVisibility
{
get { return _calcProgVisibility; }
set
{
_calcProgVisibility = value;
OnPropertyChanged("CalcProgVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
//ViewModel
public class ViewModel : INotifyPropertyChanged
{
public ViewModel(Calculation model)
{
this.CalcProgVisibility = model.CalcProgVisibility;
model.PropertyChanged += (s, e) => UpdateEntity(s as Calculation);
}
private void UpdateEntity(Calculation source)
{
CalcProgVisibility = source.CalcProgVisibility;
}
private string _calcProgVisibility;
public string CalcProgVisibility
{
get { return _calcProgVisibility; }
set
{
_calcProgVisibility = value;
OnPropertyChanged("CalcProgVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Your Viewmodel has to implement the INotifyPropertyChanged Interface. To fire it in your case your viewmodel must also be aware of changes in your model object. So your model object could also implement INotifyPropertyChanged, or you use some form of the observer pattern.
If your model implements INotifyPropertyChanged, your viewmodel must manually register for this event and implement an handler. This could in turn trigger the PropertyChange event of the viewmodel then.
Another but in my opinion ugly way would be to scan (per timer or background thread) through your viemodel and check if a value changed since the last scan and then trigger a property changed event.
The first solution could look like this:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace StackOverflow
{
[TestClass]
public class IntegrationTest
{
[TestMethod]
public void NotifyPropertyChangeShouldFireOnViewModelWhenModelChanges()
{
//Arrange
Model model = new Model();
ViewModel sut = new ViewModel(model);
bool notifyPropertyChangeOnViewModelWasCalled = false;
sut.PropertyChanged += (sender, e) => { notifyPropertyChangeOnViewModelWasCalled = true; };
//Act
model.CalcValue = 4711;
//Assert
Assert.IsTrue(notifyPropertyChangeOnViewModelWasCalled, "NotifyPropertyChange was not fired on ViewModel");
}
}
public class ObjectWithNotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Model : ObjectWithNotifyPropertyChanged
{
private double calcValue;
public double CalcValue
{
get
{
return calcValue;
}
set
{
if (calcValue != value)
{
calcValue = value;
RaisePropertyChanged();
}
}
}
}
public class ViewModel : ObjectWithNotifyPropertyChanged
{
public ViewModel(Model model)
{
this.model = model;
model.PropertyChanged += model_PropertyChanged;
}
void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "CalcValue":
RaisePropertyChanged("CalcValue");
break;
}
}
private Model model;
public double CalcValue
{
get
{
return model.CalcValue;
}
}
}
}
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.
I'm new to Silverlight and i'm trying to use Databinding.
This looks simple but it's not working and I can't find why...
In my MainPage.xaml
<map:Map Name="bing_map" Height="578" Width="480"
ZoomLevel="{Binding ZoomLevel, Mode=TwoWay}"
Center="{Binding Center, Mode=TwoWay}"
CredentialsProvider="{StaticResource BingMapsKey}" />
As you can see, I'm attempting a binding on ZoomLevel and Center.
In my MainPage.xaml.cs
The class inherit from INotifyPropertyChanged
In the constructor:
ZoomLevel = 12.0;
Center = new GeoCoordinate(0, 0);
The properties:
private double _zoom_level;
private double ZoomLevel
{
get { return _zoom_level; }
set {
if (_zoom_level == value) return;
_zoom_level = value;
RaisePropertyChanged("ZoomLevel");}
}
private GeoCoordinate _center;
private GeoCoordinate Center
{
get { return _center; }
set {
if (_center == value) return;
_center = value;
RaisePropertyChanged("Center"); }
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
I'm I forgetting something?
I have stuck on this for 3 hours starting to be a while for a simple binding...
Thank you in advance for your help! :)
Try changing the properties to public:
private double _zoom_level;
public double ZoomLevel
{
get { return _zoom_level; }
set {
if (_zoom_level == value) return;
_zoom_level = value;
RaisePropertyChanged("ZoomLevel");}
}
private GeoCoordinate _center;
public GeoCoordinate Center
{
get { return _center; }
set {
if (_center == value) return;
_center = value;
RaisePropertyChanged("Center"); }
}
And also set the View DataContext: (as Ray mentioned in his answer)
public partial class MainPage
{
public MainPage()
{
this.DataContext = this;
}
}
It is highly recommended to use the MVVM pattern.
In addition to the properties needing to be public (as per MichaelS's answer), bindings reference the object that is set to the control's DataContext (or its parent's DataContext).
So typically you wouldn't have your Window implement INotifyPropertyChanged but you would create another class (normally called a ViewModel) that implements INotifyPropertyChanged and set that to the Window's DataContext.
e.g.
public class MainWindowViewModel : INotifyPropertyChanged
{
private GeoCoordinate _center;
public GeoCoordinate Center
{
get { return _center; }
set
{
if (_center == value) return;
_center = value;
RaisePropertyChanged("Center"); }
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then in your MainPage.xaml.cs you could do something like this
public partial class MainPage
{
public MainPage(MainWindowViewModel vm)
{
this.DataContext = vm;
}
}
Of course, a quick fix for you might be to just set your DataContext for the page to be itself.
e.g.
public partial class MainPage
{
public MainPage()
{
this.DataContext = this;
}
}