I want to change an image source from different classes (pages) including a settings flyout
I was acknowledged that i have to make the image as a global variable. But couldn't figure out how to do it. Is there any other way ?
Since you want a change to affect all pages - a shared view model is your best option. Create your view model class first:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace App.ViewModel
{
public sealed class MyViewModel : INotifyPropertyChanged
{
#region BindableBase implementation
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region MyImage
/// <summary>
/// Backing field for the MyImage property.
/// </summary>
private ImageSource myImage;
/// <summary>
/// Gets or sets a value indicating ....
/// </summary>
public string MyImage
{
get { return this.myImage; }
set { this.SetProperty(ref this.myImage, value); }
}
#endregion
}
}
Then in App.xaml instantiate it as a resource:
<Application
x:Class="App.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="using:App.ViewModel">
<Application.Resources>
<viewModel:MyViewModel
x:Key="MyViewModel"/>
</Application.Resources>
</Application>
Then anywhere you want to use that image put something like:
<Image
Source="{Binding MyImage, Source={StaticResource MyViewModel}"/>
Then when you want to change the image you can do something like this:
((MyViewModel)App.Current.Resources["MyViewModel"]).MyImage = new BitmapImage();
Are you using ViewModel classes to do your codes?
If yes, you can create a BaseViewModel class and there you can place your global property.
And all your viewmodel classes must have to inherit BaseViewModel. So that from any viewmodel you can change or set the Image source by accessing the base class property.
Public class BaseViewModel
{
//place your global properties here
Public string Name {get;set;}
}
Public class ViewModel1:BaseViewModel
{
//you can access the global property here
void Method1()
{
Name="something";
}
}
Public class ViewModel2:BaseViewModel
{
//you can access the global property here
void Method2()
{
Name="something";
}
}
Hope this helps.
Thanks
Related
If the property event will trigger I want a method that will be called
For example. If the name of person is changed the MethodOne() will be also called. How to implement this with INotifyPropertyChanged on another class for example on WPF MainWindow.xaml.cs? Thank You
public class ObservableObject : INotifyPropertyChanged
{
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
}
...
There a few ways that you can get notified of property changed.
Generally you should always define properties using DependencyProperty.Register and then add access methods.
In the example that follows change YourClass to be your class name.
Declare the property like this, note that the new PropertyMetadata specifies a static method to call when the property is changed. This can be useful but is optional and can be omitted.
/// <summary>
/// Backs the IsBusy dependency property.
/// </summary>
public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.Register(
"IsBusy",
typeof( bool ),
typeof( YourClass ),
new PropertyMetadata( false, new PropertyChangedCallback( OnIsBusyChanged ) ) );
then provide an access method that will use the underlying backing property, so that any changes can be tracked.
/// <summary>
/// Gets or sets a value indicating whether the busy indicator should show.
/// </summary>
public bool IsBusy
{
get
{
return ( bool )GetValue( IsBusyProperty );
}
set
{
SetValue( IsBusyProperty, value );
}
}
The following is the static interface to the dependency property that is required to be static. In here we will receive the dependency object that will usually be an instance of your class and thus we can invoke an object method from that class.
/// <summary>
/// IsBusyProperty property changed handler.
/// </summary>
/// <remarks>
/// This is the static version that is invoked by the dependency property
/// with the appropriate object, so see if d is our class and if so then
/// we can invoke the method on the class
/// </remarks>
/// <param name="d">Instance of class that changed its IsBusy.</param>
/// <param name="e">Event arguments.</param>
private static void OnIsBusyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
if (d is YourClass)
{
var bb = d as YourClass;
bb.OnIsBusyChanged(e);
}
}
and finally the method that will be called when the property is changed in the object
/// <summary>
/// IsBusyProperty property changed handler.
/// </summary>
/// <param name="e">Event arguments.</param>
protected virtual void OnIsBusyChanged(DependencyPropertyChangedEventArgs e)
{
// your code
// you could notify a parent or another event
// handler
}
As an alternative you can directly attach to the PropertyChanged event.
In your constructor (or somewhere)
PropertyChanged += somePropertyChanged;
this will then call the method somePropertyChanged which is defined as follows:
private void somePropertyChanged(object sender, PropertyChangedEventArgs e)
{
// sender is the object that caused the change
// and is usually your class
Console.WriteLine("Property {0} changed", e.PropertyName);
}
The first approach of specifying a callback in DependencyProperty.Register can be useful when you have a set of properties that can be considered as a group and when a similar action will be taken.
I wish to use attached properties in my application by making use of the code below (and taken from here). However, Intellisense in Visual Studio will not show the registered Attached Properties for some reason unknown to me. My application is an add-in and constrained to be a class library (.Net Framework 4.7.2)
Viewing the code below and taking cognizance of the definition for Attached Properties, I would think that the attached property would be defined as such
public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
"Value",
typeof(Property),
**typeof(Parent)**,
new UIPropertyMetadata(
default(Property),
new PropertyChangedCallback(OnValuePropertyChanged),
new CoerceValueCallback(OnValuePropertyUpdated)
));
and NOT as such
public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
"Value",
typeof(Property),
**typeof(BaseAttachedProperty<Parent, Property>)**,
new UIPropertyMetadata(
default(Property),
new PropertyChangedCallback(OnValuePropertyChanged),
new CoerceValueCallback(OnValuePropertyUpdated)
));
Notice the difference between typeof(Parent) and typeof(BaseAttachedProperty<Parent, Property>). However, when I use this code in a Visual Studio Class Library, Intellisense does not show the defined Attached Properties that are defined for example:
public class IsBusyProperty : BaseAttachedProperty<IsBusyProperty, bool>
{
}
Intellisense shows the cryptic information: local:BaseAttachedProperty`2.Value which I understand refers to the Base Class with two parameters. Is there some particular method to handle Generics and Base Classes with WPF so that Attached Properties are recognized as being in this Namespace and also being consumable? My goal is to ease the creation and consumption of Attached Properties in my application. I have done the cleaning, deletion of the bin folder to no avail. However, when I write an own AP for example:
public class IsBusyProperty
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.RegisterAttached("Value", typeof(bool), typeof(IsBusyProperty), new PropertyMetadata(false));
public static bool GetValue(DependencyObject obj)
{
return (bool)obj.GetValue(ValueProperty);
}
public static void SetValue(DependencyObject obj, bool value)
{
obj.SetValue(ValueProperty, value);
}
}
the code compiles without the dreaded error that the attached property is not found in the namespace local:... or the No implementation exception!
Here is the reference code taken from here.
using System;
using System.ComponentModel;
using System.Windows;
namespace Fasetto.Word
{
/// <summary>
/// A base attached property to replace the vanilla WPF attached property
/// </summary>
/// <typeparam name="Parent">The parent class to be the attached property</typeparam>
/// <typeparam name="Property">The type of this attached property</typeparam>
public abstract class BaseAttachedProperty<Parent, Property>
where Parent : new()
{
#region Public Events
/// <summary>
/// Fired when the value changes
/// </summary>
public event Action<DependencyObject, DependencyPropertyChangedEventArgs> ValueChanged = (sender, e) => { };
/// <summary>
/// Fired when the value changes, even when the value is the same
/// </summary>
public event Action<DependencyObject, object> ValueUpdated = (sender, value) => { };
#endregion
#region Public Properties
/// <summary>
/// A singleton instance of our parent class
/// </summary>
public static Parent Instance { get; private set; } = new Parent();
#endregion
#region Attached Property Definitions
/// <summary>
/// The attached property for this class
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
"Value",
typeof(Property),
typeof(BaseAttachedProperty<Parent, Property>),
new UIPropertyMetadata(
default(Property),
new PropertyChangedCallback(OnValuePropertyChanged),
new CoerceValueCallback(OnValuePropertyUpdated)
));
/// <summary>
/// The callback event when the <see cref="ValueProperty"/> is changed
/// </summary>
/// <param name="d">The UI element that had it's property changed</param>
/// <param name="e">The arguments for the event</param>
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Call the parent function
(Instance as BaseAttachedProperty<Parent, Property>)?.OnValueChanged(d, e);
// Call event listeners
(Instance as BaseAttachedProperty<Parent, Property>)?.ValueChanged(d, e);
}
/// <summary>
/// The callback event when the <see cref="ValueProperty"/> is changed, even if it is the same value
/// </summary>
/// <param name="d">The UI element that had it's property changed</param>
/// <param name="e">The arguments for the event</param>
private static object OnValuePropertyUpdated(DependencyObject d, object value)
{
// Call the parent function
(Instance as BaseAttachedProperty<Parent, Property>)?.OnValueUpdated(d, value);
// Call event listeners
(Instance as BaseAttachedProperty<Parent, Property>)?.ValueUpdated(d, value);
// Return the value
return value;
}
/// <summary>
/// Gets the attached property
/// </summary>
/// <param name="d">The element to get the property from</param>
/// <returns></returns>
public static Property GetValue(DependencyObject d) => (Property)d.GetValue(ValueProperty);
/// <summary>
/// Sets the attached property
/// </summary>
/// <param name="d">The element to get the property from</param>
/// <param name="value">The value to set the property to</param>
public static void SetValue(DependencyObject d, Property value) => d.SetValue(ValueProperty, value);
#endregion
#region Event Methods
/// <summary>
/// The method that is called when any attached property of this type is changed
/// </summary>
/// <param name="sender">The UI element that this property was changed for</param>
/// <param name="e">The arguments for this event</param>
public virtual void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { }
/// <summary>
/// The method that is called when any attached property of this type is changed, even if the value is the same
/// </summary>
/// <param name="sender">The UI element that this property was changed for</param>
/// <param name="e">The arguments for this event</param>
public virtual void OnValueUpdated(DependencyObject sender, object value) { }
#endregion
}
}
I have created a new user control. I would like to listen for when the Visibility property has changed so that I can do some extra work at the same time. I know that it is a dependency property, but it isn't one I created, so I am struggling to understand how to hook into it. In WinRT apps, there is not OverrideMetadata method, which seems to be the most common way to do this. I also tried creating a new dependency property that registered to the existing property name, but that callback was never fired.
I have to believe that there is some way for a dependency object to listen for it's own property changes. What am I missing?
I have used something like this in my user control. You can then subscribe to the VisibilityChanged event. Note that I am using the new keyword on the property.
/// <summary>
/// The current visiblity of this user control.
/// </summary>
private Visibility _visibility;
/// <summary>
/// Gets or sets the visibility of a UIElement.
/// A UIElement that is not visible is not rendered and does not communicate its desired size to layout.
/// </summary>
/// <returns>A value of the enumeration. The default value is Visible.</returns>
public new Visibility Visibility
{
get { return _visibility; }
set
{
bool differ = false;
if (value != _visibility)
{
_visibility = value;
differ = true;
}
base.Visibility = value;
if (differ)
{
RaiseVisibilityChanged(value);
}
}
}
/// <summary>
/// Raised when the <see cref="Visibility"/> property changes.
/// </summary>
public event EventHandler<VisibilityChangedEventArgs> VisibilityChanged;
/// <summary>
/// Raises the <see cref="VisibilityChanged"/> event of this command bar.
/// </summary>
/// <param name="visibility">The new visibility value.</param>
private void RaiseVisibilityChanged(Visibility visibility)
{
if (VisibilityChanged != null)
{
VisibilityChanged(this, new VisibilityChangedEventArgs(visibility));
}
}
/// <summary>
/// Contains the arguments for the <see cref="SampleViewModel.VisibilityChanged"/> event.
/// </summary>
public sealed class VisibilityChangedEventArgs : EventArgs
{
/// <summary>
/// The new visibility.
/// </summary>
public Visibility NewVisibility { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="VisibilityChangedEventArgs"/> class.
/// <param name="newVisibility">The new visibility.</param>
/// </summary>
public VisibilityChangedEventArgs(Visibility newVisibility)
{
this.NewVisibility = newVisibility;
}
}
I have an observable collection I am trying to serialize to disk. The error that is received is :
Type 'VisuallySpeaking.Data.GrammarList' with data contract name
'GrammarList:http://schemas.datacontract.org/2004/07/VisuallySpeaking.Data'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to
DataContractSerializer."} System.Exception
{System.Runtime.Serialization.SerializationException}
Here is my data object:
namespace VisuallySpeaking.Data
{
[CollectionDataContract]
public class GrammarList : ObservableCollection<GrammarDataObject>
{
public GrammarList() : base()
{
Add(new GrammarDataObject("My Name", "My name is","Assets/SampleAssets/MyName.png"));
Add(new GrammarDataObject("Where is", "Where is",""));
Add(new GrammarDataObject("Dog", "I have a dog","/Assets/SampleAssets/westie.jpg"));
}
}
[DataContract]
public class GrammarDataObject : VisuallySpeaking.Common.BindableBase
{
private string _Name;
private string _SpeakingText;
private string _ImagePath;
public GrammarDataObject(string Name, string SpeakingText, string ImagePath)
{
this.Name = Name;
this.SpeakingText = SpeakingText;
this.ImagePath = ImagePath;
}
[DataMember]
public string Name
{
get { return _Name; }
set
{
if (this._Name != value)
{
this._Name = value;
this.OnPropertyChanged("Name");
}
}
}
[DataMember]
public string SpeakingText
{
get { return _SpeakingText; }
set
{
if (this._SpeakingText != value)
{
this._SpeakingText = value;
this.OnPropertyChanged("SpeakingText");
}
}
}
[DataMember]
public string ImagePath
{
get { return _ImagePath; }
set
{
if (this._ImagePath != value)
{
this._ImagePath = value;
this.OnPropertyChanged("ImagePath");
}
}
}
}
Based on Fresh's comments, I have added BindableBase in here as well.
namespace VisuallySpeaking.Common
{
/// <summary>
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
/// </summary>
[Windows.Foundation.Metadata.WebHostHidden]
[DataContract(IsReference = true)]
public abstract class BindableBase : INotifyPropertyChanged
{
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I assume that I have somehow marked my GrammarList class incorrectly, but it escapes me as to how to resolve.
UPDATE:
Following the error message (of course), I added the KnowTypeAttribute and it appeared to work:
[CollectionDataContract(Name = "GrammarList"),KnownType(typeof(GrammarList))]
public class GrammarList : ObservableCollection<GrammarDataObject>
Again, thanks to Fresh, I updated the CollectionDataContract with the Name="GrammarList", but now the issue comes when I rehydrate the XML file from disk. I get the following error message:
Expecting element 'GrammarList' from namespace
'http://schemas.datacontract.org/2004/07/VisuallySpeaking.Data'..
Encountered 'Element' with name 'GrammarDataObject', namespace
'http://schemas.datacontract.org/2004/07/VisuallySpeaking.Data'.
The serialized XML looks like this:
<?xml version="1.0"?>
<GrammarDataObject xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/VisuallySpeaking.Data" i:type="GrammarList">
<GrammarDataObject xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i1">
<ImagePath>Assets/SampleAssets/MyName.png</ImagePath>
<Name>My Name</Name>
<SpeakingText>My name is</SpeakingText>
</GrammarDataObject>
<GrammarDataObject xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i3">
<ImagePath/>
<Name>Where is</Name>
<SpeakingText>Where is</SpeakingText>
</GrammarDataObject>
<GrammarDataObject xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i4">
<ImagePath>/Assets/SampleAssets/westie.jpg</ImagePath>
<Name>Dog</Name>
<SpeakingText>I have a dog</SpeakingText>
</GrammarDataObject>
</GrammarDataObject>
Why is the XML outer tags not listed as "GrammarList"? I would assume that is what the deserializer is looking for. When I manually edit the serialized xml to place GrammarList as the outside tags, it deserializes appropriately. I feel sure I am missing something again!
UPDATE AGAIN
When I was serializing I had the following code:
DataContractSerializer serializer = new DataContractSerializer(typeof(GrammarDataObject));
I changed it to serialize to GrammarList and presto, fixed!!! thanks for the help Fresh.
DataContractSerializer serializer = new DataContractSerializer(typeof(GrammarList));
Looking at the XML output, it looks like the name of the collection is lost when its de-serialized.
Try setting the Name property on the CollectionDataContract i.e.
[CollectionDataContract(Name="GrammarList"),KnownType(typeof(GrammarList))]
public class GrammarList : ObservableCollection<GrammarDataObject>
I'm trying to bind a datagrid (WPF C# 4.0 with WPF Toolkit) to the fields of a structure. I basically have a datagrid with two columns. The first I would like to be labels, the second I would like to be the members of a structure. Perferably I would like to treat the series of rows as a single structure which can then be used elsewhere in my program.
Currently I have the program adding new instances of some class for each row, but I would prefer to treat it all as one... is there a way?
Thanks
ViewModel
/// <summary>
/// DataGridRowViewModel
/// </summary>
public class DataGridRowViewModel:BaseViewModel
{
/// <summary>
/// structureField
/// </summary>
private string structureField;
private string lableText;
/// <summary>
/// StructureField
/// </summary>
public string StructureField
{
get { return structureField; }
set
{
structureField= value;
OnPropertyChanged("StructureField");
}
}
/// <summary>
/// StructureField
/// </summary>
public string LableText
{
get { return lableText; }
set
{
lableText= value;
OnPropertyChanged("LableText");
}
}
}
/// <summary>
/// DataGridRowViewModel
/// </summary>
public class MainViewModel:BaseViewModel
{
/// <summary>
/// structureField
/// </summary>
private ObservableCollection<DataGridRowViewModel> rowCollection;
//Make Property with INotifyPropertyChanged
/// <summary>
/// Default Constructor
/// </summary>
public MainViewModel()
{
RowCollection = new ObservableCollection<DataGridRowViewModel>();
FillCollectionWithStructureFields();
}
private void FillCollectionWithStructureFields()
{
//Fill Add New Instances of DataGridRowViewModel with required Label
// and Structure Filead Values
}
}
View
Bind the collection to DataGrid with required Columns...