[WPF C#]PropertyChanged function called but update does not happen - c#

I have a small problem with my C# code and binding a property.
Here I have the following xaml:
<Image Source="{Binding downloaded, Source={StaticResource itemsViewSource}}" Width="20" Height="20" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Top"/>
And there is the code I'm trying to make working:
class Ressource : INotifyPropertyChanged
{
public String downloaded { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
Debug.WriteLine("Property changed.");
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
My problem is that the NotifyPropertyChanged function is called (the debug appears), the string content is changed but I don't see my image appear.
Does anyone have a solution to this.
Thanks!
EDIT:
After multiple useful answers but no change appearing even if the propertyChanged function is called,I'm starting to wonder if maybe changing the path of the image source is really possible.
Can the image be updated when the path is changed?
Here is the code after the changes suggested:
public class Ressource : INotifyPropertyChanged
{
public String downloaded
{
get
{
return _downloaded;
}
set
{
_downloaded = value;
if (PropertyChanged != null)
PropertyChanged(this,new PropertyChangedEventArgs("downloaded"));
}
}

You should change the property declaration so that view can be notified what source property is changed in View Model and update the control for notified property's value.
private String _downloaded;
public String downloaded
{
get
{
return _downloaded;
}
set
{
_downloaded = value;
NotifyPropertyChanged("downloaded");
}
}

The C# View Model Source Code is
class Ressource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private String _downloaded;
public string downloaded
{
get { return _downloaded; }
set
{
_downloaded= value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("downloaded"));
}
}
}

Add UpdateSourceTrigger=PropertyChanged to you binding.

You can only apply databinding when the accessibility level is public. The default accessibility level of a class is internal. You haven't applied an accessibility level, so your class Ressource is internal. Make it public and it should work.
public class Ressource : INotifyPropertyChanged
UPDATE 1:
If your image has been set to build action Resource you can try this string: "/AssemblyName;component/Assets/available.png".
OR for .Net Framework 4.5:
"pack://application:,,,/AssemblyName;component/Assets/available.png"
Replace AssemblyName with your assembly name (you can use Assembly.GetExecutingAssembly().GetName().Name to get your assembly name dynamically)

Okay so that was a rookie mistake but I prefere showing what was the problem since I found no topic explaining what could have been the problem.
However I found it myself so... I guess no one really needs it. Anyway:
I have not been clear enough.
In my example, I have the image source bound to the downloaded field in the Resource class.
The problem was that the resource objects were contained in another class which did not implement INotifyPropertyChanged.
Once I did, everything works fine.
Thanks to everyone one who tried to help me, sorry for the noobness and lack of clarity.
Code following if anyone struggles with this:
Part of Resource :
public class Ressource : INotifyPropertyChanged
{
private String _downloaded;
public String downloaded
{
get { return this._downloaded; }
set
{
this._downloaded = value;
raiseProperty("downloaded");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void raiseProperty(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And the container :
class personalSeance : INotifyPropertyChanged
{
public ObservableCollection<Ressource> listRess { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void raiseOnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And the simplest binding ever:
<Image Source="{Binding downloaded}" Width="20" Height="20" Margin="3" HorizontalAlignment="Right" VerticalAlignment="Top"/>

Related

Binding in XAML not working, if variable changed in code (WPF)

I'm quite new to coding in C# and XAML and I just can't get the binding to work in XAML. It works once, when I initialise the presenter class, but doesn't update the Textbox Text, if I change the bound variable in the code afterwards.
When the program starts, "200" is displayed in the Textbox. If I press the Button, all the Messageboxes are displayed (showing "100"), but the Textbox still shows "200" instead of "100".
I tried many solutions I found online, but none seemed worked.
The Presenter Class (ViewModel):
class Presenter : ObservableObject
{
float _xText;
public float xText
{
get { return _xText; }
set
{
_xText = value;
RaisePropertyChangedEvent("xText");
}
}
public ICommand Update
{
get { return new DelegateCommand(_Update); }
}
public Presenter()
{
_xText = 200f;
}
void _Update()
{
MessageBox.Show("_Update");
_xText = 100f;
//Debug
MessageBox.Show(_xText.ToString());
MessageBox.Show(xText.ToString());
}
}
The XAML Code (View):
<TextBox IsReadOnly="False"
IsEnabled="True"
Text="{Binding Path=xText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{Binding Update}"/>
The ObservableObject class:
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I hope someone has a solution or can explain where I went wrong.
Thanks.
You are updating the backing field not the property so NotifyPropertyChanged is not getting called. Try the property instead
void _Update()
{
MessageBox.Show("_Update");
xText = 100f;
//Debug
MessageBox.Show(_xText.ToString());
MessageBox.Show(xText.ToString());
}
To fix your problem:
You are setting the backing variable _xText so it isn't running the property xText. (As #Ken-Tucker said)
(EXTRA CHANGES)
I never use float in C# or in the (SQL Server) database. (You will regret it.)
See here "the difference between decimal, double and float".
See if you can replace:
RaisePropertyChangedEvent("xText");
with
RaisePropertyChanged(() => xText);
You will avoid a lot of cases of getting stung with typoes.
You have a property named xText? Hope it is just for the question, otherwise not good. You are writing for the reader. Start here with naming guidelines.

Databinding to a textbox and sharing the object throughout the project

I'm having difficulties with getting a bound textbox to update. I'm still new to WPF development and I must be missing a fundamental concept as I've read nearly everything available on the internet at this point and I'm still confused. Below is my code. First, an overview of what I'm doing to better set the context for my question.
Mainwindow is a Window that contains tabs that load various pages using frame source tags. I believe this might be causing me issues as I'm not sure where the actual object is getting instantiated for each tab, just that the XAML is being loaded.
Scratchpad is a class that contains a textbox, which is going to be updated and used by almost all classes that perform any type of operation to report status and any errors.
Textbox XAML (this is in "ScratchPad_View.xaml" for context)
<TextBox x:Name="scratchMessage"
Text="{Binding Path=ScratchMessage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Right"
Height="300"
Width ="500"
TextWrapping="Wrap"
VerticalAlignment="Top"/>
Code behind XAML
public partial class ScratchPad : Page
{
public ScratchPad()
{
InitializeComponent();
ScratchPad_Model ScratchPad_Model = new ScratchPad_Model();
this.DataContext = ScratchPad_Model;
}
}
Model Implementation
class ScratchPad_Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string _scratchMessage;
public string ScratchMessage;
{
get
{
return _scratchMessage;
}
set
{
if (value != _scratchMessage)
{
_scratchMessage = value;
OnPropertyChanged("ScratchMessage");
}
}
}
// Create the OnPropertyChanged method to raise the event
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Most of this I have cobbled together via responses to other questions on StackOverflow and reading numerous databinding tutorials however it's still not clicking. I'm not sure how to update the contents of the textbox and since I'm loading the page that contains the textbox in the XAML of my mainwindow I'm not sure I'm even referencing the correct object. The mainwindow loads this page in a frame tag, copied below.
<Frame Source="Common/View/ScratchPad_View.xaml" ></Frame>
In the code behind for this XAML, I have the following.
public partial class MainWindow
{
// Create scratchpad object for logging and status display
ScratchPad scratchPad = new ScratchPad();
public MainWindow()
{
InitializeComponent();
}
private void StartVault(object sender, RoutedEventArgs e)
{
// Creates the authentication prompt view object and pass the scratchPad reference for reporting
authPrompt_View _authPrompt_View = new authPrompt_View(scratchPad);
}
}
I pass the reference to the ScratchPad object that I created in the initialization of the mainwindow to all classes so that they can update the contents of the textbox, however I haven't had much luck in getting the binding to work. Once it works, I'm still not quite sure how I'm supposed to append text to the textbox. There's probably a great deal of problems here but I'm hoping to fix some of my conceptual problems and get a better understanding of what I'm doing wrong, thanks in advance!
You can use Application.Properties to set global properties for your project. So probably in SETTER method of textbox bound variable (in your case ScratchMessage), you need to set property in global application properties collection.
Below links explains it very well:
https://msdn.microsoft.com/en-us/library/aa348545(v=vs.100).aspx
http://www.c-sharpcorner.com/Resources/842/application-properties-in-wpf-and-c-sharp.aspx
My understanding is that , You have created the ViewModel for ScratchPad inside the constructor and assigning the DataContext in the same.
So, other windows will not have access to the DataContext.
My suggestion is that Maintain a base ViewModel class and inherit the base Viewmodel in all other ViewModel's.
Add ScratchMessage property inside base viewModel.
So you can access the ScratchMessage property from other viewModel's too.
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _scratchMessage;
public string ScratchMessage
{
get { return _scratchMessage; }
set
{
_scratchMessage = value;
this.OnPropertyChanged("ScratchMessage");
}
}
// Create the OnPropertyChanged method to raise the event
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ViewModel1 : BaseViewModel
{
ViewModel1()
{
this.ScratchMessage = "Message";
}
}

Binding variable with TextBlock

I have my XAML code (inside a Page of the standard blank page template) as
<Grid>
<TextBlock x:Name="tbBindingBlock">
</Grid>
And I have an int named iTestBindingin the code-behind. Is there a simple (since I'm only binding one variable and not a collection) way of binding the two together so that the latest value of iTestBinding (which keeps getting changed from the code-behind) is always displayed inside tbBindingBlock ?
Edit : The code behind is pretty short :
public sealed partial class MainPage : Page
{
public int iTestBinding=0;
you can bind your value directly using the below code
<TextBlock x:Name="tbBindingBlock" Text="{Binding iTestBinding}">
keep your value to bind as a property in code behind like this
public int iTestBinding{ get; set; }
and set the datacontext in page loaded event like this
this.DataContext = this;
if you want to update the value on button click and to be reflected in UI, you need to implement the PropertyChangedEventHandler.
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
call RaisePropertyChanged("changed property name") where-ever you updated the value of the property.
Its better to keep a custom get set for the property so that we can call this method from the setter.for example
private string myText;
public string MyText {
get {
return myText;
}
set {
myText = value;
RaisePropertyChanged("MyText");
}
}

Image Binding to string

I am unable to display the image which is in this location Resources/Images/abc.png.
Here is what i am doing:
public class A
{
private string image;
public string Image
{
get { return image; }
set
{
if (value != this.image)
{
image = value;
}
}
}
}
In my .CS file:
if (somecondition)
{
a.Image = #"Resources/Images/abc.png";
}
In my XAML file:
<DataTemplate x:Key="TopicDataTemplate" >
<Image Stretch="None"
Grid.Row="1"
Source="{Binding Image}"/>
</DataTemplate>
But its not displaying the image, how to fix this ? What am i doing wrong here ?
Your image path should be ok, provided that there actually is a file named abc.png in a folder named Images in another folder named Resources in your Visual Studio project, and that its Build Action is set to Resource (which is the default).
Update I'm not sure if the above is also true for Windows Phone. I guess that the default conversion from string to ImageSource might not be as capable on that platform as it is in WPF.
However, on either platform, if you want to change the Image property during runtime, you need to implement a property change mechanism that notifies the data binding that the Image property has changed. One way is to implement the INotifyPropertyChanged interface in your class A:
public class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string image;
public string Image
{
get { return image; }
set
{
image = value;
RaisePropertyChanged("Image");
}
}
protected void RaisePropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Obviously it is also necessary that the Image binding is properly set up, i.e. that the DataContext of the templated item holds a reference to an instance of class A.

how to implement observable int in wpf ViewModel?

In my mvvm ViewModel I have such field
public int Delta { get; private set; }
However when I update it like that:
Delta = newValue;
UI is not refreshed.
I was thinking that databinding will do that for me. For example I can declare collection as ObservableCollection and then databinding will work.
However there are no ObservableInt, how to say View that it need to be refreshed then?
Probably I should raise some event "notify property changed" or something?
You have two choices:
Implement the INotifyPropertyChanged interface on your class.
Inherit from DependencyObject and implement Delta as a DependencyProperty.
The simplest option is #1. You can implement the INotifyPropertyChanged interface on your class quite easily:
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set { _delta = value; PropertyChanged?.Invoke(nameof(Delta)); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can read more about using and implementing dependency properties on MSDN.
While we're at it with improving the answer, some of the other new additions of c# 6.0 and 7.0 help make it ever more compact:
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get => _value;
set { _value = value; NotifyPropertyChanged(nameof(Value)); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
This way, you aren't using any "embedded values" (i.e - the property's name) and are keeping the code refactor-safe.
And there's also no need for redundant code blocks due to c# 6.0 and 7.0's new Expression body features
Using #LBushKin's Answer, i modified it to
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get { return _value; }
set { _value = value; NotifyPropertyChanged("Value"); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(String propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and to set it up:
class MainWindow ...
// a bool with initial value of true
public static Prop<bool> optionBool { get; set; } = new Prop<bool>{ Value = true };
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// connect UI to be able to use the Prop
DataContext = this;
}
and to use it:
<Grid ...
<CheckBox Content="Da Check" ... IsChecked="{Binding optionBool.Value}"/>
There is also a Collection and 2-Properties version here:
Utils.ObservableProperties.cs (this repo contains several related classes)
Just implement INotifyPropertyChanged Interface in your class and use it to raise a PropertyChanged for your Property and then UI will update. If you are using an MVVM project template then there is a good chance you already have a helper method implemented you only need to use it.
MSDN INotifyPropertyChanged
GalaSoft MVVM Light Toolkit
The ObservableCollection raises events automatically but for your own properties you have to raise the events yourself.
A good example is here: http://www.codeproject.com/Tips/228352/Naming-Properties-in-MVVM?display=Print
I'd suggest using mvvm light: http://mvvmlight.codeplex.com, I used it in silverlight and wpf applications. Very easy to use and provides a messageing system between model, view model and view.
Adding on to https://stackoverflow.com/a/8316100/5725669, there is a new and easy way to do this without remembering to keep track of PropertyChanged?.Invoke(nameof(Delta)); in every location
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set {
_delta = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public YourClass()
{
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
It makes use of CallerMemberName for skipping manual entries for property name. More details on this MSDN Doc

Categories