Ive posted another thread regarding refreshing a silverlight listbox - In the meantime Ive become slightly more familiar with the issue. It has to do with implementing the ObservableCollection interface.
Im using RIA services to populate a listbox with a collection of type ObservableCollection. The problem is refreshing the Items once the database has been updated. I am using a silverlight child window to save the data to the database, and this is happening correctly On the close event of the child window.
Can anyone give me more information as to why the ListBox is not updating.
Thanks
You need to make your poco class that is used within your ObservableCollection implement INotifyChanged.
Example:
<viewModels:LocationsViewModel x:Key="viewModel" />
.
.
.
<ListView
DataContext="{StaticResource viewModel}"
ItemsSource="{Binding Locations}"
IsItemClickEnabled="True"
ItemClick="GroupSection_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="0,0,10,0" Style="{ThemeResource ListViewItemTextBlockStyle}" />
<TextBlock Text="{Binding Latitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Longitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="5,0,0,0" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class LocationViewModel : BaseViewModel
{
ObservableCollection<Location> _locations = new ObservableCollection<Location>();
public ObservableCollection<Location> Locations
{
get
{
return _locations;
}
set
{
if (_locations != value)
{
_locations = value;
OnNotifyPropertyChanged();
}
}
}
}
public class Location : BaseViewModel
{
int _locationId = 0;
public int LocationId
{
get
{
return _locationId;
}
set
{
if (_locationId != value)
{
_locationId = value;
OnNotifyPropertyChanged();
}
}
}
string _name = null;
public string Name
{
get
{
return _name;
}
set
{
if (_name != value)
{
_name = value;
OnNotifyPropertyChanged();
}
}
}
float _latitude = 0;
public float Latitude
{
get
{
return _latitude;
}
set
{
if (_latitude != value)
{
_latitude = value;
OnNotifyPropertyChanged();
}
}
}
float _longitude = 0;
public float Longitude
{
get
{
return _longitude;
}
set
{
if (_longitude != value)
{
_longitude = value;
OnNotifyPropertyChanged();
}
}
}
}
public class BaseViewModel : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void OnNotifyPropertyChanged([CallerMemberName] string memberName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(memberName));
}
}
}
Related
I am trying to realize Wordle(Game) in WPF as a project to practice. In the ViewModel I have a Property(e.g. FirstWord) for the first, second, third, fourth and fifth word, all of which have INotifyPropertyChanged implemented. I would like to avoid creating a property for every single textbox (would be 5x5). Therefore, my model consists of 1st letter, 2nd letter, 3rd letter, etc. In XAML the binding looks like this: {Binding FirstWord.FirstLetter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}.
When debugging, the program does not even jump into the setter, it was not detected that the value has changed. What can be the reason for this?
ViewModel
public class WordleViewModel : ObservableObject
{
private int counter;
public string solutionWord;
private static int[] _stateColor = new int[5];
public static int[] StateColor
{
get { return _stateColor;} private set { _stateColor = value; }
}
private SafeWordsModel _firstWord;
public SafeWordsModel FirstWord
{
get { return _firstWord; }
set
{
_firstWord = value;
OnPropertyChanged();
}
}
//....
public WordleViewModel()
{
solutionWord = "TESTE";
}
}
Model (Do I really need variable + Property and the INotifyChanged here?)
public class SafeWordsModel : ObservableObject
{
private string _firstLetter;
private string _secondLetter;
private string _thirdLetter;
private string _fourthLetter;
private string _fifthLetter;
public string FirstLetter
{
get { return _firstLetter; }
set { _firstLetter = value; OnPropertyChanged(); }
}
public string SecondLetter
{
get { return _secondLetter; }
set { _secondLetter = value; OnPropertyChanged(); }
}
public string ThirdLetter
{
get { return _thirdLetter; }
set { _thirdLetter = value; OnPropertyChanged(); }
}
public string FourthLetter
{
get { return _fourthLetter; }
set { _fourthLetter = value; OnPropertyChanged(); }
}
public string FifthLetter
{
get { return _fifthLetter; }
set { _fifthLetter = value; OnPropertyChanged(); }
}
public SafeWordsModel()
{
this.FirstLetter = _firstLetter;
this.SecondLetter = _secondLetter;
this.ThirdLetter = _thirdLetter;
this.FourthLetter = _fourthLetter;
this.FifthLetter = _fifthLetter;
}
}
View
public WordleView()
{
InitializeComponent();
ViewModel = new WordleViewModel();
}
public WordleViewModel ViewModel
{
get { return DataContext as WordleViewModel; }
set { DataContext = value; }
}
public void FocusNext(object sender, TextChangedEventArgs e)
{
if (((TextBox)sender).MaxLength == ((TextBox)sender).Text.Length)
{
// move focus
var ue = e.OriginalSource as FrameworkElement;
e.Handled = true;
ue.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
XAML for one Word
<StackPanel Orientation="Horizontal"
FocusManager.FocusedElement="{Binding ElementName=tbx11}">
<TextBox x:Name="tbx11"
Style="{StaticResource Input_tbx}"
TextChanged="FocusNext"
Background="{Binding ElementName=Line2_ckb, Path=IsChecked, Converter={StaticResource brush1}}"
Text="{Binding FirstWord.FirstLetter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="tbx12"
Style="{StaticResource Input_tbx}"
TextChanged="FocusNext"
Background="{Binding ElementName=Line2_ckb, Path=IsChecked, Converter={StaticResource brush2}}"
Text="{Binding FirstWord.SecondLetter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="tbx13"
Style="{StaticResource Input_tbx}"
TextChanged="FocusNext"
Text="{Binding FirstWord.ThirdLetter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="tbx14"
Style="{StaticResource Input_tbx}"
TextChanged="FocusNext"
Background="{Binding ElementName=Line2_ckb, Path=IsChecked, Converter={StaticResource brush4}}"
Text="{Binding FirstWord.FourthLetter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="tbx15"
TextChanged="FocusNext"
Style="{StaticResource Input_tbx}"
Background="{Binding ElementName=Line2_ckb, Path=IsChecked, Converter={StaticResource brush5}}"
Text="{Binding FirstWord.FifthLetter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
ObservableObject Class
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
So far it has always worked well with INotifyPropertyChanged. It should set the value that comes from the textbox accordingly in the property and I can also use this value. Feel free to make suggestions on other places in the code, I'm still very new to WPF.
I have a simple ListBox and a TextBox as under.I want to display the selectedvalue property of Listbox in the textbox,but my ViewModel's selected object is always null.
What am i missing here?
My XAML
<StackPanel>
<Canvas>
<TextBox x:Name="TxtMail" Width="244" FontSize="14" Canvas.Left="36" Canvas.Top="34" Height="20" Text="{Binding CurrentRec.Name,Mode=OneWay}" />
<ListBox x:Name="AllMatching" Width="{Binding ElementName=TxtMail,Path=Width}" Height="100" Canvas.Top="54" Canvas.Left="36" DisplayMemberPath="Name" SelectedItem="{Binding CurrentRec,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedValue="Name" SelectedValuePath="Name" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" />
<Button Content="Test" x:Name="cmdtest" Click="cmdtest_Click"/>
</Canvas>
My ViewModel:
public class VM_Data : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int p_ID;
public double p_SP, p_CP;
public string p_Name;
public List<DM_Data> AllData;
public DM_Data CurrentRec;
public VM_Data()
{
LoadData();
}
public int ID
{
get { return p_ID; }
set
{
if (p_ID != value)
{
RaisePropertyChangedEvent("ID");
p_ID = value;
}
}
}
public double SP
{
get { return p_SP; }
set
{
if (p_SP != value)
{
RaisePropertyChangedEvent("SP");
p_SP = value;
}
}
}
public double CP
{
get { return p_CP; }
set
{
if (p_CP != value)
{
RaisePropertyChangedEvent("CP");
p_CP = value;
}
}
}
public string Name
{
get { return p_Name; }
set
{
if (p_Name != value)
{
RaisePropertyChangedEvent("Name");
p_Name = value;
}
}
}
private void LoadData()
{
AllData = new List<DM_Data>();
string[] strNames = "Jatinder;Shashvat;shashikala;shamsher;shahid;justin;jatin;jolly;ajay;ahan;vijay;suresh;namita;nisha;negar;zenith;zan;zen;zutshi;harish;hercules;harman;ramesh;shashank;mandeep;aman;amandeep;amarjit;asim;akshay;amol;ritesh;ritivik;riz;samana;samaira;bhagwandass;bhagwan;bhawna;bhavna".Split(';');
for(int i=0;i<=strNames.GetUpperBound(0);i++)
{
DM_Data NewRec = new DM_Data();
NewRec.CP = new Random().Next(200, 400);
NewRec.SP = new Random().Next(1, 10);
NewRec.ID = i + 1;
NewRec.Name = strNames[i];
AllData.Add(NewRec);
}
AllData = AllData.OrderBy(item => item.Name).ToList();
}
private void RaisePropertyChangedEvent(string Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Property));
}
}
}
My DataModel
public class DM_Data
{
public int p_ID;
public double p_SP, p_CP;
public string p_Name;
public int ID
{
get { return p_ID; }
set { p_ID = value; }
}
public double SP
{
get { return p_SP; }
set { p_SP = value; }
}
public double CP
{
get { return p_CP; }
set { p_CP = value; }
}
public string Name
{
get { return p_Name; }
set { p_Name = value; }
}
MainWindow.Xaml.cs
public partial class MainWindow : Window
{
VM_Data ViewModel;
public MainWindow()
{
InitializeComponent();
ViewModel = new VM_Data();
this.DataContext = ViewModel;
AllMatching.ItemsSource = ViewModel.AllData;
}
private void cmdtest_Click(object sender, RoutedEventArgs e)
{
DM_Data crec = ViewModel.CurrentRec;
}
}
CurrentRec must be a property that raises the PropertyChanged event:
private DM_Data _currentRec;
public DM_Data CurrentRec
{
get { return _currentRec; }
set { _currentRec = value; RaisePropertyChangedEvent("CurrentRec"); }
}
In the code you have posted, it is a field and you cannot bind to fields:
public DM_Data CurrentRec;
You can't bind to fields! CurrentRec must be a property. At now it is a field.
Why do you set ItemsSource in code-behind? Set it in XAML.
You should call RaisePropertyChangedEvent after you've changed backing field, not before.
It is not right pattern for events raising: if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(Property)); } You need to save event delegate to variable first or use ?.Invoke.
Don't create new instances of Random on every iteration of loop because you will get equal values. Create the only one outside of the loop and use it.
What you are doing is kind of MVVM, but not really.
Here is a quick fix anyway:
Please take a look at the Bindings.
<StackPanel>
<Canvas>
<TextBox x:Name="TxtMail" Width="244" FontSize="14" Canvas.Left="36" Canvas.Top="34" Height="20" Text="{Binding ElementName=AllMatching, Path=SelectedItem.Name}" />
<ListBox x:Name="AllMatching"
Width="{Binding ElementName=TxtMail,Path=Width}"
Height="100"
Canvas.Top="54"
Canvas.Left="36"
DisplayMemberPath="Name"
SelectedItem="{Binding CurrentRec,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" />
<Button Content="Test" x:Name="cmdtest" Click="cmdtest_Click"/>
</Canvas>
</StackPanel>
I think you get the idea at: Text="{Binding ElementName=AllMatching, Path=SelectedItem.Name}".
Aditional Information
First:
You fire to early dude. Please first assign the value and then say its changed.
if (p_Name != value)
{
RaisePropertyChangedEvent("Name");
p_Name = value;
}
Second:
Use a ObservableCollection<DM_Data> to let your ListBox know about changes.
Third:
Use the posibility of Binding
Remove AllMatching.ItemsSource = ViewModel.AllData; and go like
<ListBox x:Name="AllMatching"
ItemsSource="{Binding Path=AllData}"
...
/>
And after all of this - please man check out some tutorials. And also refactor your code from VM_Data to DataViewModel thank you sir.
I have the following "element":
public class ReportElementViewModel
{
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
RaisePropertyChanged("Name");
}
}
}
public bool IsChecked
{
get { return _isChecked; }
set
{
if (_isChecked != value)
{
_isChecked = value;
RaisePropertyChanged("IsChecked");
}
}
}
}
My ViewModel contains many ReportElementViewModels:
public abstract class ReportViewModel<TPrimaryModel> : SharedViewModel
where TPrimaryModel : Model, new()
{
public ObservableCollection<ReportElementViewModel> ReportElementViewModels
{
get { return _reportElementViewModels; }
set
{
if (_reportElementViewModels != value)
{
_reportElementViewModels = value;
RaisePropertyChanged("ReportElementViewModels");
}
}
}
}
I removed the members to reduce code complexity but they are implemented correctly.
In my view I want to show all ReportElementViewModels by showing their Name and IsChecked (checkbox).
I thought the ItemsControl would be the right "tool", however it doesn't work (nothing is shown):
<ItemsControl ItemsSource="{Binding ReportElemetViewModels}" Height="auto" VerticalAlignment="Top" Grid.Row="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="269"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You have made a spelling mistake in your Binding.
ItemsSource="{Binding ReportElemetViewModels}"
Should be:
ItemsSource="{Binding ReportElementViewModels}"
I have a ComboBox which is has an ItemTemplate applied on it and is bind to a List of entity return using linq. I'm using mvvm. It is bind to it successfully but when I set the selected value of it from code at runtime to show the selected value coming from db it doesn't select it. For reference here is my ComboBox xaml.
<DataTemplate x:Key="ManufacturerDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image x:Name="imgManufacturer" Width="25" Height="25"
Source="{Binding Path=ManufacturerImage}" Grid.Column="0"/>
<TextBlock x:Name="txtManufacturer" Grid.Column="1" HorizontalAlignment="Left"
VerticalAlignment="Center" Text="{Binding Path=ManufacturerName}"
Tag="{Binding Path=ManufacturerID}"/>
</Grid>
</DataTemplate>
<ComboBox x:Name="cboManufacturer"
SelectionChanged="cboManufacturer_SelectionChanged"
ItemsSource = "{Binding Path=CurrentManufacturers}"
SelectedValue="{Binding Path=SelectedManufacturer}"
Grid.Column="3" Grid.Row="2" Margin="20,9.25,68,7.75"
ItemTemplate="{StaticResource ManufacturerDataTemplate}" TabIndex="6"/>
Here is my part from code behind from viewModel.
List<tblManufacturer> currentManufacturers
= new List<tblManufacturer>();
tblManufacturer selectedManufacturer = null;
public List<tblManufacturer> CurrentManufacturers
{
get
{
return currentManufacturers;
}
set
{
currentManufacturers = value;
NotifyPropertyChanged("CurrentManufacturers");
}
}
public tblManufacturer SelectedManufacturer
{
get
{
return selectedManufacturer;
}
set
{
selectedManufacturer = currentManufacturers.Where(mm => mm.ManufacturerID == Convert.ToInt32(selectedDevice.tblManufacturer.EntityKey.EntityKeyValues[0].Value)).First();
NotifyPropertyChanged("SelectedManufacturer");
}
}
Here is the sample code snippet:
Xaml for ComboBox:
<ComboBox ItemsSource="{Binding ManufacturerList}" DisplayMemberPath="Name" SelectedValuePath="ID"
SelectedItem="{Binding SelectedManufacturer}"/>
ViewModel code :
public class Manufacturer
{
public int ID { get; set; }
public string Name { get; set; }
}
private List<Manufacturer> _manufactuerlist;
private Manufacturer _selectedManufacturer;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public Manufacturer SelectedManufacturer
{
get
{
return _selectedManufacturer;
}
set
{
_selectedManufacturer = value;
NotifyPropertyChanged("SelectedManufacturer");
}
}
public List<Manufacturer> ManufacturerList
{
get
{
return _manufactuerlist;
}
set
{
_manufactuerlist = value;
NotifyPropertyChanged("ManufacturerList");
}
}
And finally Set the Selected Manufacturer in your view model like this:
SelectedManufacturer = _manufactuerlist.Find(m => m.ID == 2);
I'm having an issue with an ObservableCollection getting new items but not reflecting those changes in a ListView. I have enough quirks in the way I'm implementing this that I'm having a hard time determining what the problem is.
My ObservableCollection is implemented thusly:
public class MessageList : ObservableCollection<LobbyMessage>
{
public MessageList(): base()
{
Add(new LobbyMessage() { Name = "System", Message = "Welcome!" });
}
}
I store the collection in a static property (so that its easily accessible from multiple user controls):
static public MessageList LobbyMessages { get; set; }
In the OnLoad event of my main NavigationWindow I have the following line:
ChatHelper.LobbyMessages = new MessageList();
My XAML in the UserControl where the ListView is located reads as:
<ListBox IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Mode=OneWay}"
x:Name="ListBoxChatMessages"
d:UseSampleData="True"
ItemTemplate="{DynamicResource MessageListTemplate}"
IsEnabled="True">
<ListBox.DataContext>
<Magrathea_Words_Tools:MessageList/>
</ListBox.DataContext>
</ListBox>
The initial message that I added in the constructor appears in the UI just fine.
Now, the way I add new items to the collection is from a CallBack coming from a WCF service. I had this code working in a WinForms application and it was neccessary to marshall the callback to the UI thread so I left that code in place. Here is an abbreviated version of the method:
Helper.Context = SynchronizationContext.Current;
#region IServiceMessageCallback Members
/// <summary>
/// Callback handler for when the service has a message for
/// this client
/// </summary>
/// <param name="serviceMessage"></param>
public void OnReceivedServiceMessage(ServiceMessage serviceMessage)
{
// This is being called from the WCF service on it's own thread so
// we have to marshall the call back to this thread.
SendOrPostCallback callback = delegate
{
switch (serviceMessage.MessageType)
{
case MessageType.ChatMessage:
ChatHelper.LobbyMessages.Add(
new LobbyMessage()
{
Name = serviceMessage.OriginatingPlayer.Name,
Message = serviceMessage.Message
});
break;
default:
break;
}
};
Helper.Context.Post(callback, null);
}
While debugging I can see the collection getting updated with messages from the service but the UI is not reflecting those additions.
Any ideas about what I'm missing to get the ListView to reflect those new items in the collection?
I resolved this issue.
Neither the static property or the context of the incoming data had anything to do with the issue (which seems obvious in hindsight).
The XAML which was generated from Expression Blend was not up to the task for some reason. All I did to get this to work was assign the ItemSource to the collection in C#.
ListBoxChatMessages.ItemsSource = ChatHelper.LobbyMessages.Messages;
My XAML is now more simplified.
<ListBox IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Mode=OneWay}" Background="#FF1F1F1F"
Margin="223,18.084,15.957,67.787" x:Name="ListBoxChatMessages"
ItemTemplate="{DynamicResource MessageListTemplate}"
IsEnabled="True"/>
I'm a little confused as to why this works. I was reading the MSDN articles on how to bind data in WPF and they included several binding objects, referencing properties on object, etc. I don't understand why they went to all the trouble when one line of code in the UserControl's constructor does the trick just fine.
You need to make your poco class within the ObservableCollection implement INotifyPropertyChanged.
Example:
<viewModels:LocationsViewModel x:Key="viewModel" />
.
.
.
<ListView
DataContext="{StaticResource viewModel}"
ItemsSource="{Binding Locations}"
IsItemClickEnabled="True"
ItemClick="GroupSection_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="0,0,10,0" Style="{ThemeResource ListViewItemTextBlockStyle}" />
<TextBlock Text="{Binding Latitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Longitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="5,0,0,0" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class LocationViewModel : BaseViewModel
{
ObservableCollection<Location> _locations = new ObservableCollection<Location>();
public ObservableCollection<Location> Locations
{
get
{
return _locations;
}
set
{
if (_locations != value)
{
_locations = value;
OnNotifyPropertyChanged();
}
}
}
}
public class Location : BaseViewModel
{
int _locationId = 0;
public int LocationId
{
get
{
return _locationId;
}
set
{
if (_locationId != value)
{
_locationId = value;
OnNotifyPropertyChanged();
}
}
}
string _name = null;
public string Name
{
get
{
return _name;
}
set
{
if (_name != value)
{
_name = value;
OnNotifyPropertyChanged();
}
}
}
float _latitude = 0;
public float Latitude
{
get
{
return _latitude;
}
set
{
if (_latitude != value)
{
_latitude = value;
OnNotifyPropertyChanged();
}
}
}
float _longitude = 0;
public float Longitude
{
get
{
return _longitude;
}
set
{
if (_longitude != value)
{
_longitude = value;
OnNotifyPropertyChanged();
}
}
}
}
public class BaseViewModel : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void OnNotifyPropertyChanged([CallerMemberName] string memberName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(memberName));
}
}
}