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.
Related
I would like to bind two properties in the viewmodel.
public class MainViewModel : INotifyPropertyChanged
{
public string Format
{
get { return format; }
set
{
if (format != value)
{
format = value;
OnPropertyChanged("Format");
}
}
}
public string FilterString
{
get { return filter; }
set { filter = SomeFunction(value); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
So when the Format property changes It invokes FilterString set method.
What is the correct way of doing this.
You can use a WPF multibinding to bind a control to two fields, but in this case I think it makes more sense that the ViewModel would change FilterString when the Format is changed.
You'd use a multibinding when a control's value depends on two things because that's a GUI feature. In your case the ViewModel properties are logically linked, so it makes more sense for the code to be in the ViewModel.
To help you decide which pattern to use, ask yourself whether you'd want the same behaviour if you reused the ViewModel elsewhere.
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";
}
}
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"/>
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");
}
}
I am a beginner to use MVVM in WPF and found that it seem impossible to change the value of a textbox or a label. Here is an example.
In Xaml:
The original value of Name is "Peter".
But after I press a button which invoke a command in the ViewModel and change the value of Name to be
"John". So, suppose the value of the text box will be changed to John as well. However, it doesn't change.
I have found a lot of example in the net and found that none of them implemented this kind of functions. What I have learnt from them is to use Command and ItemsSource of ListView.
The value of ListView will change when I use button to raise command to change the ItemsSource of the view. Its value will change automatically when the Binding to ItemsSource changed.
However, I cannot make the value of TextBox or Label change even the value of the bindings to them are changed already.
Actually, I am really quite young in MVVM. I think I still have so much that I don't know.
Could you give me an example of how exactly I should do to make change to textbox after a button click? By the way, I am not quite sure how to make command for button. It seem to involve so much codes that I found in the sample from the net. Is there any simplier way?
Thank you very much.
Your ViewModel needs to implement INotifyPropertyChanged .
Documentation see here
public class Bar : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string foo;
public string Foo
{
get { return this.foo; }
set
{
if(value==this.foo)
return;
this.foo = value;
this.OnPropertyChanged("Foo");
}
}
private void OnPropertyChanged(string propertyName)
{
if(this.PropertyChanged!=null)
this.PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
Your view model should implement INotifyPropertyChanged so that WPF knows that you've altered a value of a property.
Here is an example from
// This is a simple customer class that
// implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private string customerNameValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
var listeners = PropertyChanged;
if (listeners != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged("CustomerName");
}
}
}
}