OnPropertyChanged and instance property - c#

i have a WPF window called Layer_Properties, a WPF window called MainWindow and a MapViewModel.
in the MapViewModel i create an object<Layer_Properties> and i want to observe any changes of a Layer_Properties property called Stops_List in order to refresh a treeview in MainWindow.xaml
class Layer_Properties
public partial class Layer_Properties : Window, INotifyPropertyChanged
{
//Properties
public MainWindow mw = (MainWindow)Application.Current.MainWindow;
private ObservableCollection<Stop> _Stops_List = new ObservableCollection<Stop>();
public ObservableCollection<Stop> Stops_List // property
{
get { return _Stops_List; } // get method
set { _Stops_List = value; } // set method
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class MapViewModel
public class MapViewModel : INotifyPropertyChanged
{
public MapViewModel()
{
_activeLayerProperties = new Layer_Properties();
_numberOfStops= _activeLayerProperties.Stops_List.Count;
}
private Map _map;
public MapView _mapView;
public MainWindow mw = (MainWindow)Application.Current.MainWindow;
private ObservableCollection<Layer_Properties> layersPool = new ObservableCollection<Layer_Properties>();
public ObservableCollection<Layer_Properties> LayersPool
{
get { return layersPool; }
set
{
layersPool = value;
OnPropertyChanged(nameof(LayersPool));
}
}
private Layer_Properties _activeLayerProperties;
public Layer_Properties activeLayerProperties
{
get { return _activeLayerProperties; }
set
{
_activeLayerProperties = value;
OnPropertyChanged(nameof(activeLayerProperties.Stops_List));
}
}
private int _numberOfStops;
public int numberOfStops
{
get { return _numberOfStops; }
set
{
_numberOfStops = value;
if(_numberOfStops >0)
{
//Refresh treeview
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the <see cref="MapViewModel.PropertyChanged" /> event
/// </summary>
/// <param name="propertyName">The name of the property that has changed</param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var propertyChangedHandler = PropertyChanged;
if (propertyChangedHandler != null)
propertyChangedHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
The logic is as follows;
add all Layer_Properties instances to an ObservableCollection<Layer_Properties> LayersPool.
Copy layer_Properties instance to a _mapViewModel.activeLayerProperties in order to know what is the active instance.
Refresh the treeview everytime Stops_List.Count changes. Any help will be appreciated.

Related

WPF IsEnabled Binding to Property in Collection, which Depends on other Property in another Collection

Solution link: https://github.com/fallingsappy/portfolio/tree/master/DDrop
Hi, everyone! I have a problem with IsEnabled binding of TabItem. It kinda hard to explain, but I try. I have a collection called User. User contains UserSeries collection of Series class. UserSeries have another collection called DropPhotosSeries. Also UserSeries have property called:
private bool _canDrawPlot;
public bool CanDrawPlot
{
get
{
return _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters != null).ToList().Count > 1 && _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters == null).ToList().Count == 0;
}
set
{
_canDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("CanDrawPlot"));
}
}
Based on this property. TabItem should be enabled or disabled. But this TabItem doesn't seems to notice changes of the property. I've tried many approaches, but still nothing. Here some XAML:
<TabItem IsEnabled="{Binding ElementName=AppMainWindow, Path=User.IsAnySelectedSeriesCantDrawPlot}" Name="CombinedSeriesPlot" Header="Общий график серий">
<uc:ScatterPlot User="{Binding User, ElementName=AppMainWindow, UpdateSourceTrigger=PropertyChanged}"/>
</TabItem>
Some of the codebehind:
public class User : INotifyPropertyChanged
{
private ObservableCollection<Series> _userSeries;
public ObservableCollection<Series> UserSeries
{
get
{
return _userSeries;
}
set
{
_userSeries = value;
OnPropertyChanged(new PropertyChangedEventArgs("UserSeries"));
}
}
private bool _isAnySelectedSeriesCantDrawPlot;
[NotMapped]
public bool IsAnySelectedSeriesCantDrawPlot
{
get
{
return _userSeries?.Where(x => x?.CanDrawPlot == false).ToList().Count > 0;
}
set
{
_isAnySelectedSeriesCantDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsAnySelectedSeriesCantDrawPlot"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
I've tried to create dependent property in UserClass, but i think property in UserSeries should be enough somehow:
public class Series : INotifyPropertyChanged
{
private ObservableCollection<DropPhoto> _dropPhotosSeries;
public ObservableCollection<DropPhoto> DropPhotosSeries
{
get
{
return _dropPhotosSeries;
}
set
{
_dropPhotosSeries = value;
OnPropertyChanged(new PropertyChangedEventArgs("DropPhotosSeries"));
}
}
private bool _canDrawPlot;
public bool CanDrawPlot
{
get
{
return _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters != null).ToList().Count > 1 && _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters == null).ToList().Count == 0;
}
set
{
_canDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("CanDrawPlot"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
Maybe DropPhoto should throw some event, that CanDrowProperty need to update itself and update TabItem? I really don't know. But here is DropPhoto class:
public class DropPhoto : INotifyPropertyChanged
{
private Drop _drop;
public Drop Drop
{
get
{
return _drop;
}
set
{
_drop = value;
OnPropertyChanged(new PropertyChangedEventArgs("Drop"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
It's contains Drop class, which has RadiusInMeters property:
public class Drop : INotifyPropertyChanged
{
private double? _radiusInMeters;
public double? RadiusInMeters
{
get
{
return _radiusInMeters;
}
set
{
_radiusInMeters = value;
OnPropertyChanged(new PropertyChangedEventArgs("RadiusInMeters"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
For full solution. Please check github link.
It's hard to find out what exactly wrong in your code but I'll try to answer directly to the question with simple example code
public class FirstClass : INotifyPropertyChanged
{
SecondClass secondClass = new SecondClass();
private bool _firstProperty;
public bool FirstProperty
{
get => _firstProperty;
set
{
_firstProperty = value;
OnPropertyChanged(nameof(FirstProperty));
}
}
private void SecondClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(secondClass.SecondProperty)) FirstProperty = secondClass.SecondProperty;
}
public FirstClass()
{
secondClass.PropertyChanged += SecondClass_PropertyChanged;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class SecondClass : INotifyPropertyChanged
{
private bool _secondProperty;
public bool SecondProperty
{
get => _secondProperty;
set
{
_secondProperty = value;
OnPropertyChanged(nameof(SecondProperty));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
In this example any change of SecondProperty will cause change of the FirstProperty.

TextBox binding value int, in order to use it

how to get the value of the textbox and try to use it as int with binding?
<TextBox Text="{Binding SelectedAmount}"/>
I have tried like this, but the value of the binding is 0
public string SelectedAmount
{
get { return _selectedAmount; }
set { _selectedAmount = value; }
}
That is my main class, but the valau of the textbox stay 0, it does´t change
public partial class MainWindow : Window
{
int deposit;
int weeks;
int total;
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyClass();
}
public class MyClass : INotifyPropertyChanged
{
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([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int _selectedAmount;
public int SelectedAmount
{
get
{
return this._selectedAmount;
}
set
{
if (value != this._selectedAmount)
{
this._selectedAmount = value;
NotifyPropertyChanged();
}
}
}
}
public void BtnCompute_Click_1(object sender, RoutedEventArgs e)
{
MyClass ff = new MyClass();
int cc = ff.SelectedAmount;
deposit = cc;
}
}
}
You can bind Text to int with no effort.
When using bindings, you should either derive the class containing bindable properties from the interface INotifyPropertyChanged or the class DependencyObject. otherwise the binding will show only the default (initial) values.
public class MyClass : INotifyPropertyChanged
{
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([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int _selectedAmount;
public int SelectedAmount
{
get
{
return this._selectedAmount;
}
set
{
if (value != this._selectedAmount)
{
this._selectedAmount = value;
NotifyPropertyChanged();
}
}
}
}
as in here
or
public class MyClass : DependencyObject
{
/// <summary>
/// Gets or Sets SelectedAmount Dependency Property
/// </summary>
public int SelectedAmount
{
get { return (int)GetValue(SelectedAmountProperty); }
set { SetValue(SelectedAmount Property, value); }
}
public static readonly DependencyProperty SelectedAmountProperty =
DependencyProperty.Register("SelectedAmount ", typeof(int), typeof(MyClass), new PropertyMetadata(0));
}
also do not forget to set the DataContext of your view.
//in view's constructor:
this.DataContext = new MyClass();
or
<UserControl>
<UserControl.DataContext>
<vm:MyClass/>
</UserControl.DataContext>
</UserControl>
Simply use like this,
public void BtnCompute_Click_1(object sender, RoutedEventArgs e)
{
MyClass ff = new MyClass();
int amount;
int.TryParse(ff.SelectedAmount, out amount);
deposit = amount;
}

INotifyPropertyChanged not report of changing inside my derive class

I have this Base class:
public class MyFileInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _file;
private int _bytesSent;
public MyFileInfo(string file)
{
}
public string File
{
get { return _file; }
set { _file = value; }
}
public int BytesSent
{
get { return _bytesSent; }
set
{
_bytesSent = value;
OnPropertyChanged("BytesSent");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And the derive class:
public class MyFile : MyFileInfo
{
public MyFile(MyFileInfo myFileInfo)
}
this.File = pcapInfo.myFileInfo;
this.BytesSent = pcapInfo.BytesSent;
}
public DoWork()
{
// here BytesSent is changing
}
{
OK so i have the base and the derive class.
Inside the derive class my property BytesSent is changing but my UI not.
This is my Collection:
private ObservableCollection<MyFile> files{ get; set; }
Maybe i need to define the OnPropertyChanged method in the derive class ?
ObservableCollection doesn't notify when properties within the items change. It will only notify you when the list it self changes, such as if an item was added or removed.
Look here for more info: ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)

ObservableCollection updating List

I am new in WPF and MVVM also. I'm searching for USB devices in my program. But if I connect a new device, it is required to restart program to became visible.
How to do it, that refresh immediately.
Currently I have in my class in which I search device this:
public List<Devices> devices = new List<Devices>();
public void FindDevices() // spremeni v public bool da dobis feedback
{
_deviceList = HidDevices.Enumerate(VendorID, ProductID).ToArray();
... devices.Add(new Devices()
{
DeviceId = nod + 1,
ManufacturerId = deviceManufacturerstring[nod],
ProductId = deviceProductstring[nod],
SerialNumberId = deviceSNstring[nod],
HardwareVersionId = "test4",
FirmwareVersionId = "test5",
DateOfManufaturedId = "test6"
});
On hole for loop I add device to List. I need for loop because I read some data from each device.
I later add this devices in List in ViewModel:
public class Windows1ViewModel : ViewModelBase
{
public ObservableCollection<Devices> devfuck { get; protected set; }
List<Devices> _devicelist;
public List<Devices> Devices
{
get { return _devicelist; }
set { _devicelist = value; }
}
public Windows1ViewModel()
{
USBmiddleware cs = new USBmiddleware();
cs.FindDevices();
devfuck = new ObservableCollection<Devices>();
foreach (var item in cs.devices)
{
devfuck.Add(item);
}
List<Devices> keks = cs.devices;
NotifyPropertyChanged("devfuck");
}
public List<Devices> lvdevices
{
get { return _devicelist; }
set { _devicelist = value; }
}
What to change? Where to add INotifyPropertyChanged? Or how to solve my problem?
Please for help. Thanks!
My ViewModelBase
public class ViewModelBase : INotifyPropertyChanged, IDisposable
{
protected ViewModelBase()
{
}
#region DisplayName
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void NotifyPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected virtual void NotifyPropertyChangedAll(object inOjbect)
{
foreach (PropertyInfo pi in inOjbect.GetType().GetProperties())
{
NotifyPropertyChanged(pi.Name);
}
}
public virtual void Refresh()
{
NotifyPropertyChangedAll(this);
}
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// Child classes can override this method to perform
/// clean-up logic, such as removing event handlers.
/// </summary>
protected virtual void OnDispose()
{
}
~ViewModelBase()
{
string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
System.Diagnostics.Debug.WriteLine(msg);
}
}
You can do it by using a timer which gets called ever so often (as needed). Of course it would be sexier if you could do this using event management...
Thankfully there is a way to do this :
var watcher = new ManagementEventWatcher();
var query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
watcher.Query = query;
watcher.Start();
as stated here: Detecting USB drive insertion and removal using windows service and c#
You can then subscribe to the event and invoke your
FindDevices()
From there.
You need to implement the INotifyPropertyChanged interface in your Windows1ViewModel class. Then you need to call it from your property setter, or after you set the property as you are currently doing.
Next, you should set up a DispatcherTimer to call your FindDevices method every so often and then update the ObservableCollection property from there.

Time Ticking in C# WPF MVVM

I am doing a program, which must have a clock hanging every second, main problem is that I am beginner in WPF and MVVM :)
But otherwise my clock is running just not refreshing. I have special class for only Time and Date purpose.
Here is my code:
TimeDate class:
public class TimeDate : ViewModelBase
{
public string SystemTimeHours;
string SystemTimeLong;
string SystemDateLong;
string SystemTimeZoneLong;
string SystemTimeShort;
string SystemDateShort;
string SystemTimeZoneShort;
public void InitializeTimer()
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
SystemTimeHours = DateTime.Now.ToString("HH:mm tt");
}
}
ViewModel:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
TimeDate td = new TimeDate();
td.InitializeTimer();
HoursTextBox = td.SystemTimeHours;
}
private string _hourstextbox;
public string HoursTextBox
{
get
{ return _hourstextbox; }
set
{
if (value != _hourstextbox)
{
_hourstextbox = value;
NotifyPropertyChanged("HoursTextBox");
}
}
}
}
And also NotifyProperty in ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged, IDisposable
{
#region Constructor
protected ViewModelBase()
{
}
#endregion // Constructor
#region DisplayName
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void NotifyPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected virtual void NotifyPropertyChangedAll(object inOjbect)
{
foreach (PropertyInfo pi in inOjbect.GetType().GetProperties())
{
NotifyPropertyChanged(pi.Name);
}
}
public virtual void Refresh()
{
NotifyPropertyChangedAll(this);
}
#endregion // INotifyPropertyChanged Members
#region IDisposable Members
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// Child classes can override this method to perform
/// clean-up logic, such as removing event handlers.
/// </summary>
protected virtual void OnDispose()
{
}
/// </summary>
~ViewModelBase()
{
string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
System.Diagnostics.Debug.WriteLine(msg);
}
#endregion // IDisposable Members
}
What to do, that Clock will refresh every second? Please help
As MSDN:
Reasons for using a DispatcherTimer opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set on the DispatcherTimer.
I make shorter example:
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="98" Width="128"
xmlns:local="clr-namespace:WpfApplication1">
<Window.DataContext>
<!-- This will auto create an instance of ViewModel -->
<local:ViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Name="lblTimer" Grid.Row="1" Content="{Binding Path=CurrentTime}"></Label>
</Grid>
</Window>
CS:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class ViewModel : ViewModelBase
{
private string _currentTime;
public DispatcherTimer _timer;
public string CurrentTime
{
get
{
return this._currentTime;
}
set
{
if (_currentTime == value)
return;
_currentTime = value;
OnPropertyChanged("CurrentTime");
}
}
public ViewModel()
{
_timer = new DispatcherTimer(DispatcherPriority.Render);
_timer.Interval = TimeSpan.FromSeconds(1);
_timer.Tick += (sender, args) =>
{
CurrentTime = DateTime.Now.ToLongTimeString();
};
_timer.Start();
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Hope this help.
You have to specify the DispatcherTimer as a field in your viewmodel class, not just as a local variable in ctor.
Garbage Collector will destroy all local variables when they are out of scope.
Here is my implementation of the clock :)
public class MainWindowViewModel : ViewModelBase
{
private readonly DispatcherTimer _timer;
public MainWindowViewModel()
{
_timer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(1)};
_timer.Start();
_timer.Tick += (o, e) => OnPropertyChanged("CurrentTime");
}
public DateTime CurrentTime { get { return DateTime.Now; } }
}
<TextBlock Text="{Binding CurrentTime, StringFormat={}{0:HH:mm tt}}" />
An example of a static wrapper for a clock and binding to it:
using System.Threading;
public static class ClockForWpf
{
private static readonly Timer timer = new Timer(Tick, null, 0, 10);
private static void Tick(object state)
{
Time = DateTime.Now;
TimeChanged?.Invoke(null, EventArgs.Empty);
}
public static event EventHandler TimeChanged;
public static DateTime Time { get; private set; }
}
<TextBlock Text="{Binding Path=(local:ClockForWpf.Time)}"/>

Categories

Resources