C# WPF ObservableCollection does not update Grid - wpf

My implementation of an ObservableCollection does not update my Grid and I don't know why:
The first XAML-Window 'UserControlStaff.xaml' displays the Grid where the user data can be seen. The second XAML-Window allows to add users and is called 'CreateUser.xaml'.
The code behind the XAML-Windows is attached to this post as well.
My Code goes as follows:
[XAML] UserControlStaff.xaml
<UserControl x:Class="MyApp.UserControlStaff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="786">
<StackPanel x:Name="spStaff" Orientation="Vertical" Visibility="Visible">
<Button Background="Azure" Click="btnCreateUser_Click" HorizontalAlignment="Left" Width="786" Height="40">
<Bold>Mitarbeiter hinzufügen</Bold></Button>
<ScrollViewer MaxHeight="504">
<DataGrid Background="#DBDB72" ItemsSource="{Binding UserDataObject}" CanUserAddRows="false" Width="786" />
</ScrollViewer>
</StackPanel>
</UserControl>
[Code] UserControlStaff.xaml.cs
namespace MyApp
{
/// <summary>
/// Interaktionslogik für UserControlStaff.xaml
/// </summary>
public partial class UserControlStaff : UserControl
{
ObservableCollection<User> mUserDataObject = new ObservableCollection<User>();
public ObservableCollection<User> UserDataObject
{
get
{
return mUserDataObject;
}
}
public UserControlStaff()
{
InitializeComponent();
DataContext = this;
try
{
string connectionString = ConfigurationManager.ConnectionStrings["MyApp.Properties.Settings.ConString"].ConnectionString;
string queryString = string.Empty;
using (SqlConnection connection = new SqlConnection(connectionString))
{
queryString = "SELECT ID, username AS Username, Password AS Passwort, (SELECT role FROM Roles WHERE ID = t1.role) AS Rolle FROM Users t1 ORDER BY ID";
SqlCommand cmd = new SqlCommand(queryString, connection);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable("Users");
sda.Fill(dt);
foreach (DataRow row in dt.Rows)
{
int pID = 0;
string pUsername = "";
string pPassword = "";
string pRole = "";
foreach (DataColumn col in dt.Columns)
{
if (col.ToString().Trim() == "ID")
{
pID = int.Parse(row[col].ToString());
}
else if (col.ToString().Trim() == "Username")
{
pUsername = row[col].ToString();
}
else if (col.ToString().Trim() == "Passwort")
{
pPassword = row[col].ToString();
}
else if (col.ToString().Trim() == "Rolle")
{
pRole = row[col].ToString();
}
}
// Show Users DB table in MainWindow
mUserDataObject.Add(new User { ID = pID, Username = pUsername, Password = pPassword, Role = pRole });
}
// Show Users DB table in MainWindow
//XAML Grid: Name="gridUsers"
//CS Code: gridUsers.DataContext = dt.DefaultView;
}
}
catch {
throw;
}
}
private void btnCreateUser_Click(object sender, RoutedEventArgs e)
{
CreateUser popup = new CreateUser();
popup.Show();
}
private void UpdateUserData()
{
// implement
}
private void DeleteUserData()
{
// implement
}
}
}
[XAML] CreateUser.xaml
<Window x:Class="MyApp.CreateUser"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Mitarbeiter hinzufügen" Height="300" Width="400">
<Grid>
<Label Content="Username" HorizontalAlignment="Left" Margin="10,44,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<Label Content="Passwort" HorizontalAlignment="Left" Margin="10,88,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<Label Content="Rolle" HorizontalAlignment="Left" Margin="10,132,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<TextBox HorizontalAlignment="Left" Height="30" Margin="150,44,0,0" TextWrapping="Wrap" Name="TextBoxUsername" Text="{Binding Path=Username}" VerticalAlignment="Top" Width="180"/>
<TextBox HorizontalAlignment="Left" Height="30" Margin="150,88,0,0" TextWrapping="Wrap" Name="TextBoxPassword" Text="{Binding Path=Password}" VerticalAlignment="Top" Width="180"/>
<ComboBox HorizontalAlignment="Left" Height="30" Margin="150,132,0,0" VerticalAlignment="Top" Width="180" Loaded="ComboBox_Loaded" SelectionChanged="ComboBox_SelectionChanged"/>
<Button Click="btnSaveUserData_Click" Content="Speichern" HorizontalAlignment="Left" Margin="217,219,0,0" VerticalAlignment="Top" Height="30" Width="75"/>
<Button Click="btnCloseWindow_Click" Content="Abbrechen" HorizontalAlignment="Left" Margin="297,219,0,0" VerticalAlignment="Top" Height="30" Width="75"/>
</Grid>
</Window>
[Code] CreateUser.xaml.cs
namespace MyApp
{
/// <summary>
/// Interaktionslogik für CreateUser.xaml
/// </summary>
public partial class CreateUser : Window
{
User userObject;
public CreateUser()
{
InitializeComponent();
this.userObject = new User();
}
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
// ToDO: Rollenzuweisung automatisieren aus Datenbanktabelle MyApp.Roles
List<string> data = new List<string>();
data.Add("Chef");
data.Add("Restaurantmitarbeiter");
data.Add("Fahrer");
// ... Get the ComboBox reference.
var comboBox = sender as ComboBox;
// ... Assign the ItemsSource to the List.
comboBox.ItemsSource = data;
// ... Make the second item selected.
comboBox.SelectedIndex = 1;
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// ... Get the ComboBox.
var comboBox = sender as ComboBox;
// ... Set SelectedItem as Window Title.
string value = comboBox.SelectedItem as string;
//this.Title = "Selected: " + value;
this.userObject.Role = value;
}
private void btnSaveUserData_Click(object sender, RoutedEventArgs e)
{
try
{
this.userObject.Username = TextBoxUsername.Text;
this.userObject.Password = TextBoxPassword.Text;
int UserRole;
// ToDO: Rollenzuweisung automatisieren aus Datenbanktabelle MyApp.Roles
if (this.userObject.Role == "Chef")
{
UserRole = 1;
}
else if (this.userObject.Role == "Restaurantmitarbeiter")
{
UserRole = 2;
}
else if (this.userObject.Role == "Fahrer")
{
UserRole = 3;
}
else
{
UserRole = 2; // Default UserRole is "Restaurantmitarbeiter"
}
if (this.userObject.Username.Trim() != "" && this.userObject.Password.Trim() != "")
{
CreateUserData(this.userObject.Username, this.userObject.Password, UserRole);
// ToDO: Update DataGrid in UserControlStaff
this.Close();
MessageBox.Show("Mitarbeiter hinzugefügt!");
} else {
MessageBox.Show("Bitte Username und Passwort eingeben.");
}
}
catch { throw; }
}
private void CreateUserData(string pUsername, string pPassword, int pRole)
{
string connectionString = ConfigurationManager.ConnectionStrings["MyApp.Properties.Settings.ConString"].ConnectionString;
string queryString = "INSERT INTO Users (ID, username, password, role) VALUES ((SELECT TOP 1 ID+1 FROM Users ORDER BY ID DESC), '" + pUsername + "', '" + pPassword + "', '" + pRole + "')";
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(queryString, connection);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable("Users");
sda.Fill(dt);
string UserRole;
if (pRole == 1)
{
UserRole = "Chef";
}
else if (pRole == 2)
{
UserRole = "Restaurantmitarbeiter";
}
else if (pRole == 3)
{
UserRole = "Fahrer";
}
else
{
UserRole = "Restaurantmitarbeiter"; // Default UserRole is "Restaurantmitarbeiter"
}
// Add to Observable Collection
// ToDO
// mUserDataObject.Add(new User { ID = pID, Username = pUsername, Password = pPassword, Role = pRole });
}
private void btnCloseWindow_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}

Related

How to validate RichTextbox in WPF?

How to validate RichTextbox in WPF? I want to validate the text for email and email separator i.e. emails should be entered with a semicolon.
Xaml:
<StackPanel Orientation="Horizontal">
<RichTextBox x:Name="txtEmail" Style="{StaticResource ContentRichTextBox}"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,0,10,0">
<FlowDocument>
<Paragraph LineHeight="5"></Paragraph>
</FlowDocument >
</RichTextBox>
</StackPanel>
<StackPanel HorizontalAlignment="Center">
<TextBlock x:Name="txterrormessage" Width="300" Foreground="#FFE5572C" FontSize="14" Visibility="Hidden" TextWrapping="Wrap"></TextBlock>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Margin="60,0,0,0">
<Button x:Name="BtnEmail" Style="{StaticResource ShortButtonStyle}" Content="NEXT" Margin="10" Command="{Binding CommandChanged}" CommandParameter="PROJECTS" Click="BtnEmail_Click"/>
</StackPanel>
This is my code:
private void BtnEmail_Click(object sender, RoutedEventArgs e)
{
string richText = new TextRange(txtEmail.Document.ContentStart, txtEmail.Document.ContentEnd).Text;
if (!Regex.IsMatch(richText, #"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$"))
{
txterrormessage.Text = "Enter a valid email";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
else
{
txterrormessage.Visibility = System.Windows.Visibility.Hidden;
}
if (!Regex.IsMatch(richText, #"^((\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)\s*[;]{0,1}\s*)+$"))
{
txterrormessage.Text = "Separate emails with ;";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
}
The code doesnt seem to work....How to validate?
Thanks
The simplest way I've seen to do this is
private void OnVerifyEmail()
{
var recipients = richText.Split(';', StringSplitOptions.RemoveEmptyEntries);
var validator = new System.ComponentModel.DataAnnotations.EmailAddressAttribute();
foreach (var recipient in recipients)
{
var isValid = validator.IsValid(recipient.Trim());
if(!isValid)
{
// do your thing here
}
}
}
format your richText before validation:
richText = Regex.Replace(richText, #"(\n|\r)", "", RegexOptions.Multiline);
Edit:
This is the whole method and probably what you're looking for:
private void BtnEmail_Click(object sender, RoutedEventArgs e)
{
string richText = new TextRange(txtEmail.Document.ContentStart, txtEmail.Document.ContentEnd).Text;
richText = Regex.Replace(richText, #"(\n|\r)", "", RegexOptions.Multiline);
richText = Regex.Replace(richText, #"( ;|; )", ";", RegexOptions.Multiline);
txterrormessage.Visibility = System.Windows.Visibility.Hidden;
if (!Regex.IsMatch(richText, #"^[\W]*([\w+\-.%]+#[\w\-.]+\.[A-Za-z]{2,4}[\W]*,{1}[\W]*)*([\w+\-.%]+#[\w\-.]+\.[A-Za-z]{2,4})[\W]*$"))
{
string[] emails = Regex.Split(richText, ";", RegexOptions.Multiline);
foreach (string item in emails)
{
if (string.IsNullOrEmpty(item))
continue;
if (!Regex.IsMatch(item, #"^\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*$"))
{
txterrormessage.Text = item + " is not a valid email address";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
break;
}
}
if (string.IsNullOrEmpty(txterrormessage.Text))
{
txterrormessage.Text = "Separate emails with ; ";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
}
}

How to make a custom control WPF Resource Dictionary with MVVMLight RelayCommands

Can i / How can I attach commands to a custom control i made and put it into a Resource Dictionary in WPF? I would like to not define this control in multiple places and also not have the code in code-behind file of the XAML for it. Here is the first rendition of my control, its basically a re-orderable listbox of checkboxes. Unchecked items cannot be at the top of the list.
<ListBox Name="listBoxZone" Grid.Row="3" HorizontalContentAlignment="Stretch" Height="200"
ItemsSource="{Binding TheList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0.5" BorderBrush="DarkGray">
<Grid Height="30">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" Click="Reorder_Click" Grid.RowSpan="2" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Width="Auto"/>
<Button Click="UpDownClick" Name="Up" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Margin="1" ToolTip="Up" BorderBrush="{x:Null}" Background="{x:Null}">
<Image Source="/Resources/Icons/sort_up.png"/>
</Button>
<Button Name="Down" Click="UpDownClick" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Margin="1" ToolTip="Down" BorderBrush="{x:Null}" Background="{x:Null}">
<Image Source="/Resources/Icons/sort_down.png"/>
</Button>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And what's currently just in code-behind :
public TaskRolesView()
{
InitializeComponent();
//remove later when i figure this out!
CreateCheckBoxList();
this.DataContext = this;
}
private System.Collections.ObjectModel.ObservableCollection<StoredProc> _theList;
public System.Collections.ObjectModel.ObservableCollection<StoredProc> TheList
{
get { return _theList; }
set
{
if (value != _theList)
{
_theList = value;
FirePropertyChanged("TheList");
}
}
}
public void CreateCheckBoxList()
{
TheList = new System.Collections.ObjectModel.ObservableCollection<StoredProc>();
StoredProc mea = new StoredProc();
mea.Name = "MEA";
mea.IsChecked = true;
StoredProc valic = new StoredProc();
valic.Name = "VALIC";
valic.IsChecked = true;
StoredProc axa = new StoredProc();
axa.Name = "AXA";
axa.IsChecked = true;
StoredProc fidelity = new StoredProc();
fidelity.Name = "Fidelity";
fidelity.IsChecked = true;
StoredProc first = new StoredProc();
first.Name = "Step 1";
first.IsChecked = true;
StoredProc second = new StoredProc();
second.Name = "Step 2";
second.IsChecked = false;
StoredProc last = new StoredProc();
last.Name = "Last";
last.IsChecked = false;
StoredProc another = new StoredProc();
another.Name = "another";
another.IsChecked = false;
StoredProc onemore = new StoredProc();
onemore.Name = "onemore";
onemore.IsChecked = false;
TheList.Add(mea);
TheList.Add(valic);
TheList.Add(axa);
TheList.Add(fidelity);
TheList.Add(first);
TheList.Add(second);
TheList.Add(last);
TheList.Add(another);
TheList.Add(onemore);
}
public class StoredProc
{
public string Name { get; set; }
public bool IsChecked { get; set; }
}
private void UpDownClick(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
StoredProc sp = button.DataContext as StoredProc;
int oldLocation = TheList.IndexOf(sp);
if (oldLocation > 0 && button.Name == "Up")
{
if (oldLocation > 0)
{
if (sp.IsChecked || (TheList[oldLocation - 1].IsChecked == sp.IsChecked))
{
TheList.RemoveAt(oldLocation);
TheList.Insert(oldLocation - 1, sp);
}
}
}
if (oldLocation < TheList.Count - 1 && button.Name == "Down")
{
if (oldLocation + 1 <= TheList.Count)
{
if (sp.IsChecked == false || (TheList[oldLocation + 1].IsChecked == sp.IsChecked))
{
TheList.RemoveAt(oldLocation);
TheList.Insert(oldLocation + 1, sp);
}
}
}
}
private void Reorder_Click(object sender, RoutedEventArgs e)
{
CheckBox checkBox = sender as CheckBox;
IEnumerable<StoredProc> sort;
sort = TheList.OrderByDescending(item => item.IsChecked);
System.Collections.ObjectModel.ObservableCollection<StoredProc> temp = new System.Collections.ObjectModel.ObservableCollection<StoredProc>();
foreach (var item in sort)
{
temp.Add(item);
}
TheList.Clear();
TheList = temp;
}
#region FirePropertyChanged
/// <summary>
/// Property Changed handler
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Needed to update view model
/// </summary>
/// <param name="propertyName"></param>
protected void FirePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion

WPF - Observable Collection new record from xml binding

I have a page with tabcontrol.
I am binding a list of locations a tabitem in the control.
The records are listed in a listview.
I am able to edit records by binding input controls to the listview.selecteditem.
My problem is when I want to add a new record. I want to minimise code behind.
ViewModel:
private ObservableCollection<LocationViewModel> _locations;
public ObservableCollection<LocationViewModel> Locations
{
get { return _locations; }
}
public LocationListViewModel()
{
_locations = new ObservableCollection<LocationViewModel>();
foreach (Service.Location l in service.GetLocationList().OrderBy(l => l.Building).ThenBy(l => l.Floor))
{
_locations.Add(new LocationViewModel
{
id = l.id,
Building = l.Building,
Floor = l.Floor,
RoomNo = l.RoomNo,
MapTitle = l.MapTitle,
MapExtension = l.MapExtension,
Map = l.Map,
DateCreated = l.DateCreated,
CreatedByID = l.CreatedByID,
CreatedByDesc = l.CreatedByDesc,
DateEdited = l.DateEdited,
EditedByID = l.EditedByID,
EditedByDesc = l.EditedByDesc
}
);
}
}
XML:
<TabItem x:Name="tabSettingsLocations" x:Uid="tabSettingsLocations"
Header="Locations"
DataContext="{StaticResource ResourceKey=LocationList}"> .....
Example of successful binding to listview for edits
<TextBox x:Name="txtSettingLocationBuildingEdit"
Margin="90,17,0,0" Style="{DynamicResource SettingsTextBoxStyle}"
Text="{Binding SelectedItem.Building, ElementName=lvwSettingsLocations,
Mode=TwoWay}" />
Example of unsuccessful binding for new record (uses different set of input controls)
<TextBox x:Name="txtSettingLocationBuildingAdd"
Margin="90,17,0,0" Style="{DynamicResource SettingsTextBoxStyle}"
Text="{Binding Building, ElementName=lvwSettingsLocations,
Mode=OneWayToSource}"/>
I also tried to bind the child tab item to the same data source
<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd"
Header="Add New"
DataContext="{StaticResource ResourceKey=LocationList}">
<TextBox x:Name="txtSettingLocationBuildingAdd"
Margin="90,17,0,0" Style="{DynamicResource SettingsTextBoxStyle}"
Text="{Binding Building}"/>
To no avail.
I also tried creating a new child dataview but I want it all to be bound together so that the interface updates whatever I do add or edit.
Anyone help?
Okay so I nailed this in the end. Just wanted to share ... thanks to Silvermind for a good tip on best practice.
Command:
class Location_Add : ICommand
{
private ObservableCollection<LocationViewModel> _llvm;
public ObservableCollection<LocationViewModel> llvm
{
get { return _llvm; }
}
public Location_Add(ObservableCollection<LocationViewModel> passedllvm)
{
_llvm = passedllvm;
}
public bool CanExecute(object parameter)
{
LocationViewModel lvw = parameter as LocationViewModel;
return lvw != null;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
LocationViewModel lvm = parameter as LocationViewModel;
llvm.Add(lvm);
AddLocation(lvm);
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public void AddLocation(LocationViewModel lvm)
{
try
{
Service.SchoolMonitorServiceClient service = new Service.SchoolMonitorServiceClient();
Service.Location loc = new Service.Location();
loc.Building = lvm.Building.Trim();
loc.Floor = lvm.Floor.Trim();
loc.RoomNo = lvm.RoomNo.Trim();
loc.MapTitle = lvm.MapTitle;
loc.MapExtension = lvm.MapTitle.Substring(lvm.MapTitle.IndexOf("."));
loc.Map = lvm.Map;
loc.DateCreated = DateTime.Now;
loc.CreatedByID = (Int32)Application.Current.Resources["UserID"];
loc.DateEdited = lvm.DateEdited;
service.AddLocation(loc);
MessageBox.Show("Your new Location was entered successfully", "Success", MessageBoxButton.OK);
}
catch (Exception e)
{
.....
}
}
}
ViewModel:
class LocationListViewModel
{
Service.SchoolMonitorServiceClient service = new Service.SchoolMonitorServiceClient();
#region Members
private ObservableCollection<LocationViewModel> _locations;
private Location_Add _AddCommand;
#endregion
#region Properties
public ObservableCollection<LocationViewModel> Locations
{
get { return _locations; }
}
#endregion
public LocationListViewModel()
{
_locations = new ObservableCollection<LocationViewModel>();
foreach (Service.Location l
in service.GetLocationList()
.OrderBy(l => l.Building).ThenBy(l => l.Floor))
{
_locations.Add(new LocationViewModel
{
id = l.id,
Building = l.Building,
Floor = l.Floor,
RoomNo = l.RoomNo,
MapTitle = l.MapTitle,
MapExtension = l.MapExtension,
Map = l.Map,
DateCreated = l.DateCreated,
CreatedByID = l.CreatedByID,
CreatedByDesc = l.CreatedByDesc,
DateEdited = l.DateEdited,
EditedByID = l.EditedByID,
EditedByDesc = l.EditedByDesc
}
);
}
_AddCommand = new Location_Add(_locations);
}
public ICommand AddCommand
{
get
{
return _AddCommand;
}
}
}
XML:
xmlns:local="clr-namespace:SchoolMonitor_WPF.ViewModels"
<Page.Resources>
<local:LocationListViewModel x:Key="LocationList" />
<local:LocationViewModel x:Key="NewLocation" />
</Page.Resources>
<TextBox x:Name="txtSettingLocationBuildingAdd" x:Uid="txtSettingLocationBuildingAdd"
Margin="90,17,0,0" Style="{DynamicResource SettingsTextBoxStyle}"
DataContext="{StaticResource ResourceKey=NewLocation}"
Text="{Binding Path=Building}"/>
<Button x:Name="btnSettingsLocationSaveAdd" Content="Submit" Margin="0,80,10,0"
VerticalAlignment="Top" Style="{DynamicResource ButtonStyle}"
HorizontalAlignment="Right" Width="75"
DataContext="{StaticResource ResourceKey=LocationList}"
CommandParameter="{StaticResource ResourceKey=NewLocation}"
Command="{Binding AddCommand}">
Hope this helps someone.

WPF Observablecollection with INotifyPropertyChanged for ListView doesn't work

I have a wpf application and I want to update my listview when I add new value through the UI using Observablecollect. But I don't get what I expect. When I add new value I won't update my list view.
Here is my CS file.
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Windows;
namespace ObservableCollectionTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<TestUser> NameList { get; set; }
public MainWindow()
{
InitializeComponent();
NameList = GetNamesFromDBAsStringList();
ListViewNames.ItemsSource = NameList;
this.DataContext = this;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
TestUser user = new TestUser();
user.UserName = txtName.Text;
NameList.Add(user);
//InsertUserName(txtName.Text);
//NameList = GetNamesFromDBAsStringList();
txtName.Clear();
MessageBox.Show("User added");
}
private ObservableCollection<TestUser> GetNamesFromDBAsStringList()
{
ObservableCollection<TestUser> userList = new ObservableCollection<TestUser>();
SqlConnection connection = new SqlConnection("Data Source=Chakrapani\\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True");
connection.Open();
string sql = "select UserID,UserName from TestUser";
SqlCommand command = new SqlCommand(sql, connection);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
TestUser testUser = new TestUser();
testUser.UserID = (int)reader[0];
testUser.UserName = reader[1].ToString();
userList.Add(testUser);
}
reader.Close();
connection.Close();
return userList;
}
private void InsertUserName(string userName)
{
SqlConnection connection = new SqlConnection("Data Source=CHAKRAPANI\\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True");
connection.Open();
string sql = string.Format("insert into TestUser values ('{0}')", userName);
SqlCommand command = new SqlCommand(sql, connection);
command.ExecuteNonQuery();
connection.Close();
}
}
public class TestUser : INotifyPropertyChanged
{
private int _userId;
private string _userName;
public int UserID
{
get { return _userId; }
set
{
_userId = value;
NotifyPropertyChanged("UserID");
}
}
public string UserName
{
get { return _userName; }
set
{
_userName = value;
NotifyPropertyChanged("UserName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And this is my xaml file
<Window x:Class="ObservableCollectionTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="396">
<Grid>
<Button Content="Add User" HorizontalAlignment="Left" Margin="300,10,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1" Name="BtnAdd"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="264" Name="txtName"/>
<ListView HorizontalAlignment="Left" Height="259" Margin="10,51,0,0" VerticalAlignment="Top" Width="264" Name="ListViewNames" ItemsSource="{Binding MyUserNameList}">
<ListView.View>
<GridView>
<GridViewColumn Header="User ID" DisplayMemberBinding="{Binding UserID}"/>
<GridViewColumn Header="User Name" DisplayMemberBinding="{Binding UserName}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
Please give me a clue to handle the problem.
You forget to add the DataContext in your constructor :
this.DataContext = this;
You should insert it into observable collection instead of re-building it
private void Button_Click_1(object sender, RoutedEventArgs e)
{
TestUser user = new TestUser();
user.UserName = txtName.Text;
InsertUserName(txtName.Text);
NameList.Add(new TestUser {UserName = txtName.Text, UserID = ???}); //TODO: assign correct ID
txtName.Clear();
MessageBox.Show("User added");
}

Listbox binding

Hello I have problem with binding data to Listbox. In shortway... I want list all my Skydrive files.
My XAML
<TextBlock Height="35" HorizontalAlignment="Left" Margin="9,6,0,0" Name="infoTextBlock" Text="" VerticalAlignment="Top" Width="Auto" />
<my:SignInButton Name="signInButton1" ClientId="<correct ClientId>" Scopes="wl.signin wl.basic wl.skydrive" Branding="Windows" TextType="SignIn" SessionChanged="signInButton1_SessionChanged" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="198,-6,0,0" />
<StackPanel Height="578" HorizontalAlignment="Left" Margin="10,50,0,0" Name="StackContentPanel" VerticalAlignment="Top" Width="440">
<ListBox Height="465" Name="FileList" Width="380" ItemsSource="{Binding Files}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
My class and cs.
namespace EReader.ViewModel
{
public class File
{
public File()
{}
private string name;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
}
}
public class FilesManager
{
public ObservableCollection<string> Files;
public FilesManager()
{
Files = new ObservableCollection<string>();
}
}
namespace EReader
{
public partial class MainPage : PhoneApplicationPage
{
private LiveConnectClient client;
// Constructor
public MainPage()
{
InitializeComponent();
}
private void signInButton1_SessionChanged(object sender, LiveConnectSessionChangedEventArgs e)
{
if (e.Status == LiveConnectSessionStatus.Connected)
{
client = new LiveConnectClient(e.Session);
infoTextBlock.Text = "Signed in.";
client.GetCompleted +=
new EventHandler<LiveOperationCompletedEventArgs>(OnGetCompleted);
client.GetAsync("/me/skydrive/files/");
}
else
{
infoTextBlock.Text = "Not signed in.";
client = null;
}
}
void OnGetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
//Gdy uda nam się podłaczyc do konta skydrive
if (e.Error == null)
{
signInButton1.Visibility = System.Windows.Visibility.Collapsed;
#region Nazwa użytkownika
string firstName = "";
string lastName = "";
if (e.Result.ContainsKey("first_name") ||
e.Result.ContainsKey("last_name"))
{
if (e.Result.ContainsKey("first_name"))
{
if (e.Result["first_name"] != null)
{
firstName = e.Result["first_name"].ToString();
}
}
if (e.Result.ContainsKey("last_name"))
{
if (e.Result["last_name"] != null)
{
lastName = e.Result["last_name"].ToString();
}
}
infoTextBlock.Text =
"Hello, " + firstName +" "+ lastName + "!";
}
else
{
infoTextBlock.Text = "Hello, signed-in user!";
}
#endregion
#region Wszyskite pliki
List<object> data = (List<object>)e.Result["data"];
FilesManager fileManager = new FilesManager();
foreach (IDictionary<string,object> items in data)
{
File file = new File();
file.Name= items["name"].ToString();
fileManager.Files.Add(file.Name);
}
FileList.ItemsSource = fileManager.Files;
#endregion
}
else
{
infoTextBlock.Text = "Error calling API: " +
e.Error.ToString();
}
}
}
Files must be a property, not a field.
Furthermore {Binding Name} must be {Binding} instead, because a string has no Name property.
This has to be a public property:
public ObservableCollection<string> Files;
should be
public ObservableCollection<string> Files {get;set;}
Then your binding will work

Resources