I have a ViewModel and a Class. They look like this:
//viewmodel
public class MyViewModel : ViewModelBase {
private MyClass myClass;
public MyClass MyClass{
get{
return myClass;
}
set{
this.myClass = value;
base.OnPropertyChanged();
}
}
private string testString;
public string TestString{
get{
return testString;
}
set{
this.testString = value;
base.OnPropertyChanged();
}
}
public MyViewModel(){
this.MyClass = new MyClass();
this.TestString = "blah, blah, blah"
}}
//class
public class MyClass : ViewModelBase{
private string myString;
public string MyString{
get {
return myString
}
set{
this.myString = value;
base.OnPropertyChanged();
}
}
public MyClass (){
this.MyString = "25"; }}
The base class - ViewModelBase implements INotifyChange and contains OnPropertyChanged handler logic.
I have a UserControl where I wish to bind values from MyClass like this:
<TextBlock Text="{x:Bind Path=MyViewModel.MyClass.MyString, Mode=TwoWay}"></TextBlock>
However this does not work. Value is binded in initialisation correctly, but any change in MyViewModel.MyClass.MyString is not reflected in texblock, the text remains the same. The OnPropertyChange is raised, the breakpoint in ViewModelBase is hit with MyString value changed, but it is somehow not propagated to texblock.
Binding on simple value from MyClass works like charm, this textblock is updated, when property is changed:
<TextBlock Text="{x:Bind Path=MyViewModel.TestString, Mode=TwoWay}"></TextBlock>
What am I missing? Why the textblock with binding to "MyViewModel.MyClass.MyString" is not being updated?
I have tested the code you provided and don't think there is any problem with it. Where the problem could be, however, is your Page's code-behind. By any chance, aren't you using this as the property declaration?
public MyViewModel MyViewModel => new MyViewModel();
Because in this case, every access to MyViewModel property evaluates as a new instance of MyViewModel class. In this configuration, you would properly change the property, but the UI would never notice, as you would update the property on a new instance. If you instead use
public MyViewModel MyViewModel { get; } = new MyViewModel();
You will get the correct behavior of creating only one instance when the page is created.
I have the following code:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public MyViewModel MyViewModel { get; } = new MyViewModel();
private void Button_Click(object sender, RoutedEventArgs e)
{
MyViewModel.MyClass.MyString = "test";
}
}
And my simple ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Related
I have been trying to detect it when these variables change, but I don't know how to do that since bools aren't supported by the "PropertyChanged" function.
I also tried using the communityToolKit, but I have no idea how to use that.
I want it to call the function "IconUpdater"
public class Status : INotifyPropertyChanged
{
public static bool isWorking { get; set; } = Preferences.Get("IsWorking", true);
public static bool isPaused { get; set; } = Preferences.Get("IsPaused", false);
public static void IconUpdater()
{
// The function I want to call \\
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can use PropertyChanged event to notify the changes of IsEnabled property in your viewmodel.
Here's the code snippet below for your reference:
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isWorking;
public bool IsEnabled
{
get
{
return _isWorking;
}
set
{
if(_isWorking != value)
{
_isWorking = value;
var args = new PropertyChangedEventArgs(nameof(IsEnabled));
PropertyChanged?.Invoke(this, args);
}
}
}
}
I recommend using the Community Toolkit MVVM package: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
You can then simply do the following to use the INotifyPropertyChanged interface:
using CommunityToolkit.Mvvm;
public class MyViewModel : ObservableObject
{
private bool _myBool;
public bool MyBool
{
get => _myBool;
set => SetProperty(ref _myBool, value);
}
}
You can also modify the code in such a way that you directly call any other method from within the setter:
private bool _myBool;
public bool MyBool
{
get => _myBool;
set
{
SetProperty(ref _myBool, value);
IconUpdater();
}
}
Please mind that your class is using static properties. You cannot use INotifyPropertyChanged for that.
I have a background-thread which updates a Integer-value once per second. How can I map this Integer to a Text-Field of my XAML form that the Form always shows the current value and updates automatically if the Integer changes?
You can use a ViewModel with a binding.
With OnPropertyChanged() it will automatically changed and displayed in your UI.
here is an example to give you an idea
use in your xaml:
<TextBox x:Name="MyTextBox" Text="{Binding Name}".../>
in your code behind:
...
var vm = new ViewModel("Nr.7");
this.BindingContext = vm;
foreach(var x in Whatever)
{
vm.Name = x;
}
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MyAppNamespace
{
// This class implements INotifyPropertyChanged
// to support one-way and two-way bindings
// (such that the UI element updates when the source
// has been changed dynamically)
public class ViewModel : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
}
public ViewModel(string value)
{
this.name = value;
}
public string Name
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// 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));
}
}
}
In my project I am setting a bool property as true when constructing my ViewModel.
In the View, I have a RadioButton which is bound to the value of this property. The first time I open the View, the RadioButton is "checked" (perfect, exactly what I want!).
However, if I close and then re-open the View, the RadioButton is not "checked", despite the bound property having a value of 'true'. In my constructor I am setting 'StaggeredMode' to true, but the setter is being called three times (firstly, value = true; secondly, value = false; thirdly, value = true)
Any help will be appreciated!
XAML:
<RadioButton GroupName="AppointmentStart"
Content="Staggered"
IsChecked="{Binding StaggeredMode, Mode=TwoWay}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="310,247,0,0" />
C#:
private bool staggeredMode;
public bool StaggeredMode
{
get { return staggeredMode; }
set { staggeredMode = value; }
}
Your StaggeredMode property should be observable so the view can be notified about property changes from your ViewModel.
You can implement INotifyPropertyChanged by yourself or use one of existing implementations like this, this or this.
Here is a basic implementation of INotifyPropertyChanged interface:
public abstract class ObservableObject : INotifyPropertyChanged
{
public virtual void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
Then you derive your ViewModel from ObservableObject and raise OnPropertyChanged event every time your property value has been changed:
public class MyViewModel : ObservableObject
{
...
private bool staggeredMode;
public bool StaggeredMode
{
get { return staggeredMode; }
set
{
staggeredMode = value;
OnPropertyChanged("StaggeredMode");
}
}
...
}
I am binding a Datagrid to Observablecollection.Below is my ObservableCollection class.
But the Property changed is always NULL and it is null even after Making my XAML like this.
Please Guide me in this
Thanks!
<DataGridTextColumn Binding="{Binding, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged} Header = "Serial" />
public class itemobject
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged( String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
int sno1;
public int Sno
{
get
{ return sno1; }
set
{
if (value != sno1)
{
sno1= value;
NotifyPropertyChanged("Sno");
}
}
}
In XAML you should specify which property exactly changed.
<DataGridTextColumn Binding="{Binding Path = Sno} Header = "Serial" />
And like i did, you need to create ViewModelBase class.
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable {
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = null) {
var handle = PropertyChanged;
handle?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public virtual void Dispose() => PropertyChanged = null;
}
public class itemobject : ViewModelBase{
int sno1;
public int Sno{
get => sno1;
set{
if (value != sno1){
sno1= value;
OnPropertyChanged(nameof(Sno));
}
}
}
I've been trying my hand at creating a simple user control with dependencyproperty and binding it, but it doesn't seem to work, not sure why. I'll just dive straight into code, please ignore the fact that the control doesn't make sense, it's just for illustrative purposes (written in WP8 if that matters).
My simple user control, it's basically a line with a property to turn it off or on.
<Grid x:Name="LayoutRoot" Background="Transparent">
<Line Height="105" Width="105" X2="100" Y2="100" Visibility="{Binding LineVisible}" Stroke="#FFFC1515" StrokeThickness="5"/>
</Grid>
public partial class SimpleUserControl : UserControl
{
public SimpleUserControl()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty LineVisibleProperty = DependencyProperty.Register("LineVisible", typeof(bool), typeof(SimpleUserControl), new PropertyMetadata(new PropertyChangedCallback(OnLineVisibleChanged)));
public bool LineVisible
{
get { return (bool)GetValue(LineVisibleProperty); }
set { SetValue(LineVisibleProperty, value); }
}
private static void OnLineVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool newvalue = (bool)e.NewValue;
Visibility vis = newvalue ? Visibility.Visible : Visibility.Collapsed;
(d as SimpleUserControl).Visibility = vis;
}
}
The test app
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<uc:SimpleUserControl LineVisible="{Binding class1.Vis}"/>
</Grid>
public partial class MainPage : PhoneApplicationPage
{
public Class1 class1 { get; set; }
public MainPage()
{
InitializeComponent();
DataContext = this;
}
private void PhoneApplicationPage_Loaded_1(object sender, RoutedEventArgs e)
{
class1 = new Class1() { Vis = false };
}
}
The class1 that it's bound to
public class Class1 : INotifyPropertyChanged
{
private bool _vis;
public bool Vis
{
get { return _vis; }
set
{
_vis = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Vis"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
It doesn't seem to work, however, if it is set explicitly like below it works.
<uc:SimpleUserControl LineVisible="False"/>
I'm sure it's something simple, but I'm not seeing it.
Thanks for any help.
The problem was I was setting the DataContext = this in the UserControl and when binding to Vis in the testapp, it would override and search for Vis in the UserControl (which of course does not exist there). I did see binding errors in the debug output window which confirms this. The solution was to set the LayoutRoot of the UserControl to this as was mentioned in the link I posted earlier.
The Visibility property of WPF controls does not use bool values, it requires the Visibility enum. Thus you have two options:
Change LineVisibiltyProperty to Visibility instead of bool.
Use a converter to bind to bool and convert to Visibility.
I would suggest using the second option as this in my opinion is the better solution.
This might be helpful.