ListPicker Data Binding and INotifyPropertyChanged - c#

So I have two ListPickers, Device Type and Device Name.
If I select Tablet in Device Type, I want the Device Name ListPicker to show options like Ipad, Dell Venue 8, etc.
If I select Phone in Device Type, I want the Device Name ListPicker to show options like Iphone, Samsung Galaxy, etc and so on.
So how can I go about making a data binding between these two ListPickers and also implement INotifyPropertyChanged so changes in one ListPicker are dynamically reflected in the other?

You can do the following :
In your xaml :
<toolkit:ListPicker x:Name="DeviceType" ItemSource="{Binding DeviceTypeList}" SelectedItem="{Binding SelectedDeviceType, Mode=TwoWay}"/>
<toolkit:ListPicker x:Name="DeviceName" ItemSource="{Binding DeviceNameList}" />
In your code :
public class ClassName : NotifyChangements
{
private YourType selectedDeviceType;
public YourType SelectedDeviceType
{
get { return selectedDeviceType; }
set
{
selectedDeviceType = value;
NotifyPropertyChanged("SelectedDeviceType");
MAJDeviceName();
}
}
// Later in code.
public void MAJDeviceName()
{
// Add code here that fill the DeviceNameList according to the SelectedDeviceType.
}
}
And for the NotifyChangements class :
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class NotifyChangements : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public bool NotifyPropertyChanged<T>(ref T variable, T valeur, [CallerMemberName] string property = null)
{
if (object.Equals(variable, valeur)) return false;
variable = valeur;
NotifyPropertyChanged(property);
return (true);
}
}
You also have to add the List<YourType> DeviceNameList as an attribute and call the NotifyPropertyChanged("DeviceNameList") in the setter of that attribute in order to make it bind the datas.
Moreover, I'll let you change the variable and type names as you haven't provided any code samples !

Related

Two Way binding to a Dependency Property in a User Control and call a method

I know, title is a little confusing so let me explain. I have a user control that has a dependency property. I access this dependency property with a regular property called Input. In my view model I also have a property called Input. I have these two properties bound together in XAML using two-way binding as shown below:
<uc:rdtDisplay x:Name="rdtDisplay" Input="{Binding Input, Mode=TwoWay}" Line1="{Binding myRdt.Line1}" Line2="{Binding myRdt.Line2}" Height="175" Width="99" Canvas.Left="627" Canvas.Top="10"/>
Okay in my view model, I call a method whenever the value of Input is changed as shown in my property:
public string Input
{
get
{
return input;
}
set
{
input = value;
InputChanged();
}
}
The problem with this is that when I set the value of Input in my view model it only updates the value of the variable input as per my setter in my property. How can I get this to update back to the dependency property in the user control? If I leave the code input = value; out then I get a compilation error.
I need something like this:
public string Input
{
get
{
return UserControl.Input;
}
set
{
UserControl.Input = value;
InputChanged();
}
}
If I make the Input property in my view model look like this:
public string Input
{
get; set;
}
then it works, however, I am unable to call the InputChanged() method that I need to call when the Property is changed. All suggestions are appreciated.
Implement INotifyPropertyChanged in your ViewModel
public class Sample : INotifyPropertyChanged
{
private string input = string.Empty;
public string Input
{
get
{
return input;
}
set
{
input = value;
NotifyPropertyChanged("Input");
InputChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
In your case, you can do it in the code behind of your usercontrol

WPF DataGrid binding to List<Type>

I'm trying to bind a WPF DataGrid to a List<ClassName>.
Below is my DataGrid:
<DataGrid ItemsSource="{Binding Source=FileProcessing}" AutoGenerateColumns="True"></DataGrid>
Below I am binding the list with database records:
FileProcessing = GetFileProcessingInfo(dtDateStart, dtDateEnd);
The FileProcessing is defined as a property below:
public List<FileProcessing_T> FileProcessing { get; set; }
The GetFileProcessingInfo Method also returns a List<FileProcessing_T> object.
The FileProcessing list does get some records from the database but the grid does not bind the data from the list.
I will appreciate your help.
You can keep your databinding.
But your have to implement the INotfifyPropertyChanged interface in the class where the FileProcessing property is located.
Because in the setter of FileProcessing you have to perform the change notification.
public ObservableCollection<FileProcessing_T> FileProcessing
{
get
{
return _fileProcessing;
}
set
{
if (_fileProcessing != value)
{
_fileProcessing = value;
RaisePropertyChanged("FileProcessing");
}
}
}
ObservableCollection<FileProcessing_T> _fileProcessing;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Otherwise the UI control will not know (not be notified) that the bound data has changed.
This will be enough to fix your problem.
It would even work if you continued to use List<FileProcessing_T> instead of ObservableCollection<FileProcessing_T>, however the ObservableCollection also supports change notifications if single elements are added and removed from the collection while List does not.

Elegant way to implement INotifyPropertyChanged across many controls

I'm building a WPF application and I'm slowly uncovering some of the joys and also the frustrations of using WPF. My latest question involves updating the UI using INotifyPropertyChanged
My app has stacked UserControls with each UserControl containing multiple controls, so overall there are hundreds of controls which update every second providing live data. In order to update all controls I'm using something similar to below which does currently work as intended.
namespace ProjectXAML
{
public partial class ProjectX : UserControl, INotifyPropertyChanged
{
#region Declare Getter/Setter with INotifyPropertyChanged groupx3
private string m_group1Text1;
public string group1Text1
{
get
{
return m_group1Text1;
}
set
{
m_group1Text1 = value;
NotifyPropertyChanged("group1Text1");
}
}
private string m_group1Text2;
public string group1Text2
{
get
{
return m_group1Text2;
}
set
{
m_group1Text2 = value;
NotifyPropertyChanged("group1Text2");
}
}
private string m_group2Text1;
public string group2Text1
{
get
{
return m_group2Text1;
}
set
{
m_group2Text1 = value;
NotifyPropertyChanged("group2Text1");
}
}
private string m_group2Text2;
public string group2Text2
{
get
{
return m_group2Text2;
}
set
{
m_group2Text2 = value;
NotifyPropertyChanged("group2Text2");
}
}
private string m_group3Text1;
public string group3Text1
{
get
{
return m_group3Text1;
}
set
{
m_group3Text1 = value;
NotifyPropertyChanged("group3Text1");
}
}
private string m_group3Text2;
public string group3Text2
{
get
{
return m_group3Text2;
}
set
{
m_group3Text2 = value;
NotifyPropertyChanged("group3Text2");
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
/// Notifies the property changed.
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
#endregion
}
}
My questions are:
Is there a more elegant way to raise PropertyChanged events for lots of controls rather than lots of get/set code?
Is there a way to raise 1 PropertyChanged event covering the whole UserControl containing multiple controls instead of a separate event for every control? Is there a better method than what I'm attempting?
In strict reference to this part of your question..."Is there a way to raise 1 PropertyChanged event covering the whole UserControl containing ".
Yes, you can raise a PropertyChanged notification which says all my properties on my object are updated.
Use:
NotifyPropertyChanged(null);
then this informs the listener of INotifyPropertyChanged that all properties have changed on an object.
This isn't normally used...and can be abused....and cause inefficient updates e.g. if you were only changing a few properties and used that.
But you could argue the case for using it if you have lots of properties in your object, that you were always changing anyway at the same time...and you wanted to collapse lots of individual notifications into 1 that was raised after you had modified all properties.
Example use case (i.e. presumes you are updating all your groups in some way):
void UpdateAllGroupTextProperties()
{
group1Text1 = "groupA";
group1Text2 = "groupA2";
group2Text1 = "groupB";
group2Text2 = "groupB2";
group3Text1 = "groupC";
group3Text2 = "groupC2";
NotifyPropertyChanged(null);
}
For point 1 if you are using VS 2012 you can do the below
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
and then you can use your set property method without having to hard code the name of the properties.
Note the above code is an except of the below link
http://danrigby.com/2012/03/01/inotifypropertychanged-the-net-4-5-way/
Use the design pattern model view controler. So the model will raise the changes for you. Together with MVVM the controls will see with its dependency objects the changes and view them automatically.

INotifyPropertyChanged Implemented now what?

I am trying to create a list box that displays a set of data that will be updated over time. I have a simple list box:
<ListBox Name="lbRegisters" ItemsSource="{Binding}" />
And I have defined a class for my objects:
public class register : INotifyPropertyChanged
{
private int address;
public int Address { get { return address; } }
private int value;
public int Value
{
get{ return value; }
set{
this.value = value;
OnValueChanged("Value");
}
}
public register(int a)
{
address = a;
value = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnValueChanged(string name){
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public override string ToString()
{
return address + ": " + value;
}
}
And I hold a list of these in an ObservableCollection<register>. Then set the ListBox.ItemsSource=registerslist; in the CodeBehind. The list is initialized and the inital data displays correctly.
Now what do I need to do to get my ListBox to update when a "register.Value" changes. The event handler is called but there nothing is subscribed to the event.
I guess I need to trigger something in the ListBox or ObservableCollection to tell the GUI to update. I have read dozens of posts of a similar problem but they all seem to indicate that once you have implemented INotofyPropertyChanged it just automagicaly works.
What is the next step?
The problem is on your ToString() function. Yes it could be used to display complex string in ListView items but it is not a proper way to bindings because ListView does not knows when part of this string was changed.
Do the following:
1. Declare property on register class like
public string AddressValue
{
get { return address + ": " + value; }
}
2. Add OnValueChanged("AddressValue") in value and address setters like:
public int Value
{
get{ return value; }
set{
this.value = value;
OnValueChanged("Value");
OnValueChanged("AddressValue")
}
}
3. Declare you ListBox with ItemTemplate like:
<ListBox x:Name="lbRegisters" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding AddressValue}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So the idea is adding new property to register class which will be updated when address and value changed. And bind list item text to this property.
I think understand the spirit of what you're asking (even though it's obviously unfinished ATM). I'd recommend using something that inherits from DependencyObject and using dependency properties. Use the propdp snippet in Visual Studio. It will save you a ton of boilerplate code and wiring.

C# - Dynamic properties and RaisePropertyChanged

I have following class which I use for radio button binding
public class RadioButtonSwitch : ViewModelBase
{
IDictionary<string, bool> _options;
public RadioButtonSwitch(IDictionary<string, bool> options)
{
this._options = options;
}
public bool this[string a]
{
get
{
return _options[a];
}
set
{
if (value)
{
var other = _options.Where(p => p.Key != a).Select(p => p.Key).ToArray();
foreach (string key in other)
_options[key] = false;
_options[a] = true;
RaisePropertyChanged("XXXX");
else
_options[a] = false;
}
}
}
XAML
<RadioButton Content="Day" IsChecked="{Binding RadioSwitch[radio1], Mode=TwoWay}" GroupName="Monthly" HorizontalAlignment="Left" VerticalAlignment="Center" />
ViewModel
RadioSwitch = new RadioButtonSwitch(
new Dictionary<string, bool> {{"radio1", true},{"radio2", false}}
);
I'm having problem with RaisePropertyChanged() in my Class. I'm not sure what value I should be putting in order to raise the change.
I tried putting:
Item[]
a
[a]
I keep getting following error:
This is so in case of any change I can handle it in my view accordingly. Please do not give me solutions for List for radio buttons etc.
The problem is that you are implementing an indexer, not an ordinary property. Although the binding subsystem supports indexers, MVVMLight and INotifyPropertyChanged do not.
If you want to use an indexer you need to:
Use a collection base class such as ObservableCollection<T>
Implement INotifiyCollectionChanged and raise that event instead
The first option isn't realistic because you are already deriving from ViewModelBase and have to continue to do that. Since implementing INotifiyCollectionChanged is a little bit of work, the easiest approach is to:
add a property to RadioButtonSwitch that is an observable collection of boolean values (ObservableCollection<bool>)
Then change you binding to add one more path element and you are done.
Edit:
Based on your comment and rereading your question, I think implementing INotifyCollectionChanged is the easiest. Here is the rewrite of your RadioButtonSwitch class which actually no longer needs to derive from the MVVMLight base class, although you still could if you wanted to.
The careful reader will notice that we use a sledgehammer and "reset" the whole collection when any element of the collection is modified. This is not just laziness; it is because the indexer uses a string index instead of an integer index and INotifyCollectionChanged doesn't support that. As a result, when anything changes we just throw up our hands and say the whole collection has changed.
public class RadioButtonSwitch : INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void RaiseCollectionChanged()
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
IDictionary<string, bool> _options;
public RadioButtonSwitch(IDictionary<string, bool> options)
{
this._options = options;
}
public bool this[string a]
{
get
{
return _options[a];
}
set
{
if (value)
{
var other = _options.Where(p => p.Key != a).Select(p => p.Key).ToArray();
foreach (string key in other)
_options[key] = false;
_options[a] = true;
RaiseCollectionChanged();
}
else
_options[a] = false;
}
}
}
GalaSoft.MvvmLight has the following code to check property name before raising PropertyChanged event.
public void VerifyPropertyName(string propertyName)
{
if (GetType().GetProperty(propertyName) == null)
throw new ArgumentException("Property not found", propertyName);
}
GetType().GetProperty("Item[]") obviously returns null.
This is why it is failing.
I think, the quickest workaround for you would be not to use ViewModelBase from this library, but implement your own version, that doesn't do this check:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
If you implement this class you will be able to run RaisePropertyChanged("Item[]").

Categories