wpf-mvvm how can i validate my data with icommand? - c#

I'm new to WPF development and I don't get any further with google either. I don't understand how I can validate my model data with ICommand. I understand that there are annotations and that the ICommand interface offers a canExecute method. For example I want to say that the Name and the Surname is required.
I tried it with [Required(ErrorMessage = "Title is required.")] in my Model class, but I don't get it to work. may someone can help me.
So far I have the following:
public class Student : INotifyPropertyChanged
{
private string name;
private string surname;
private int age;
private string course;
public string Course
{
get { return course; }
set
{
course = value;
OnPropertyChanged();
}
}
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged();
}
}
public string Surname
{
get { return surname; }
set
{
surname = value;
OnPropertyChanged();
}
}
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class StudentViewModel
{
private IList<Student> _studentList;
public StudentViewModel()
{
_studentList = new List<Student>
{
new Student{Name="Todd",Surname="Johnsen",Age=29,Course="Software-Development"},
new Student{Name="Mike",Surname="Kroshka",Age=31,Course="Marketing"},
new Student{Name="Marie",Surname="Tedd",Age=21,Course="Marketing"},
new Student{Name="Susane",Surname="Müller",Age=31,Course="Marketing"},
new Student{Name="Herbert",Surname="Rehl",Age=18,Course="Software-Development"},
new Student{Name="Markus",Surname="Stanz",Age=23,Course="Software-Development"},
new Student{Name="Sabine",Surname="Bergsen",Age=19,Course="Marketing"}
};
}
public IList<Student> Students
{
get { return _studentList; }
set { _studentList = value; }
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if(mUpdater == null)
{
mUpdater = new Updater();
}
return mUpdater;
}
set
{
mUpdater = value;
}
}
}
public class Updater : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
}
}
<Window x:Class="Student_list_mvvm_wpf_core.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Student_list_mvvm_wpf_core"
mc:Ignorable="d"
Title="Student-list" Height="350" Width="600">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0">Name</Label>
<Label Grid.Row="1">Surname</Label>
<Label Grid.Row="2">Age</Label>
<Label Grid.Row="3">Course</Label>
<Button x:Name="btnUpdateStudent" Grid.Row="3" Grid.Column="2"
Width="100" Margin="2" HorizontalAlignment="Left"
Command="{Binding Path=UpdateCommand}">Update Student</Button>
<ListView x:Name="studentGrid" ItemsSource="{Binding Students}"
Grid.Row="4" Grid.ColumnSpan="3" Margin="5">
<ListView.View>
<GridView x:Name="gridStudent">
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100" />
<GridViewColumn Header="Surname" DisplayMemberBinding="{Binding Surname}" Width="100" />
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}" Width="50" />
<GridViewColumn Header="Course" DisplayMemberBinding="{Binding Course}" Width="200" />
</GridView>
</ListView.View>
</ListView>
<TextBox Grid.Row="0" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedItem.Name, ElementName=studentGrid}"
x:Name="txtName"></TextBox>
<TextBox Grid.Row="1" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedItem.Surname, ElementName=studentGrid}"
x:Name="txtSurname"></TextBox>
<TextBox Grid.Row="2" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedItem.Age, ElementName=studentGrid}"
x:Name="txtAge"></TextBox>
<TextBox Grid.Row="3" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedItem.Course, ElementName=studentGrid}"
x:Name="txtCourse"></TextBox>
</Grid>

Using IDataErrorInfo to validate your model looks more suited in your case. First, Update your ViewModel to have a property that bound to the ListView's SelectedItem:
public Student SelectedStudent
{
get { return _selectedStudent; }
set { _selectedStudent = value; }
}
Second, update the Student's model to do data validation:
public class Student : INotifyPropertyChanged, IDataErrorInfo
{
private string name;
private string surname;
private int age;
private string course;
public string Course
{
get { return course; }
set
{
course = value;
OnPropertyChanged();
}
}
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged();
}
}
public string Surname
{
get { return surname; }
set
{
surname = value;
OnPropertyChanged();
}
}
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
public string Error => string.Empty;
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Name")
{
if (string.IsNullOrEmpty(Name))
result = "Name is required.";
}
if (columnName == "Surname")
{
if (string.IsNullOrEmpty(Surname))
result = "Surname is required.";
}
return result;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Finally, update your xaml:
-Bind between your ListView's SelectedItem and the Property that you've created.
-Validate the TextBox's Text
<TextBox Grid.Row="0" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedStudent.Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true, NotifyOnValidationError=False}"
x:Name="txtName"></TextBox>
<TextBox Grid.Row="1" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedStudent.Surname,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true, NotifyOnValidationError=False}"
x:Name="txtSurname"></TextBox>
-Add a MultiDataTrigger to enable and disable the Button when the suited conditions are met.
<Button x:Name="btnUpdateStudent" Grid.Row="3" Grid.Column="2"
Width="100" Margin="2" HorizontalAlignment="Left"
Content="Update Student" Command="{Binding UpdateCommand}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=txtName, Path=(Validation.HasError)}" Value="false" />
<Condition Binding="{Binding ElementName=txtSurname, Path=(Validation.HasError)}" Value="false" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Output:
Here is a Gist for the full source. If you insist on using the canExecute, this should help.

Related

How to update data in a new non-modal window after changing the selection to ListView?

I have a user list in MainWindow. After pressing the preview button, a non-modal window for data editing opens. Updated data are changed in real time in the main window. The question is how to bind the windows so that because the user changes from the list in the main window, he changes in real time in an already open non-modal window.
WPF does not recommend coding business logic directly in xaml.cs
files.
It is recommended that you write code using the MVVM pattern
ViewModel
public class podgladUzytkownika : INotifyPropertyChanged
{
private string imie;
private string nazwisko;
private string mail;
public string Mail
{
get => mail;
set => this.SetValue(ref mail, value);
}
public string Nazwisko
{
get => nazwisko;
set => this.SetValue(ref nazwisko, value);
}
public string Imie
{
get => imie;
set => this.SetValue(ref imie, value);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetValue<T>(ref T oldValue, T newValue, [CallerMemberName] string propertyName = null)
{
oldValue = newValue;
OnPropertyChanged(propertyName);
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MainWindowViewModel
{
private int refreshCount;
public ICommand RefreshCommand { get; }
public ICommand PodgladUzytkownikaShow { get; }
public podgladUzytkownika data { get; }
public MainWindowViewModel()
{
data = new podgladUzytkownika();
PodgladUzytkownikaShow = new Command(PodgladUzytkownikaShowExecute);
RefreshCommand = new Command(RefreshCommandExecute);
}
private void PodgladUzytkownikaShowExecute(object obj)
{
var window = new Window();
window.DataContext = data;
window.Show();
}
private void RefreshCommandExecute(object obj)
{
// Data updates are passed to the view
refreshCount++;
data.Imie = nameof(data.Imie) + refreshCount;
data.Nazwisko = nameof(data.Nazwisko) + refreshCount;
data.Mail = nameof(data.Mail) + refreshCount;
}
}
View
// MainWindow.xaml
<StackPanel x:Name="StackPanel1">
<Button Content="PodgladUzytkownika" Command="{Binding Path=PodgladUzytkownikaShow}"/>
<Button Content="Refresh" Command="{Binding Path=RefreshCommand}"/>
</StackPanel>
// window.xaml
<StackPanel >
<TextBlock Text="{Binding Path=Imie }"/>
<TextBlock Text="{Binding Path=Nazwisko }"/>
<TextBlock Text="{Binding Path=Mail }"/>
</StackPanel>
Demo
this.MainWindow.DataContext =new MainWindowViewModel();
After chasing one problem after another with your comments, the problem is your code is not well-designed. Using data bindings (one of the prime benefits of WPF), you can stop chasing your tail with trying to figure out how to update one UI when data changes. Here is a simplified version of your code that will always ensure the UI matches the data you wish to manipulate.
MainWindow.xaml
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Label Margin="3" Grid.ColumnSpan="3">Lista użytkowników</Label>
<Button Margin="3" Padding="3" Grid.Row="2" Grid.ColumnSpan="3" Click="Zamknij_Click">Zamknij</Button>
<StackPanel Margin="3" Grid.Column="2" Grid.Row="1">
<!--<Button Name="Dodaj" Click="Dodaj_Click" Margin="3" Padding="10,3" >Dodaj...</Button>-->
<!--<Button Name="Usun" IsEnabled="False" Click="Usun_Click" Margin="3" Padding="10,3" >Usuń</Button>-->
<!--<Button Name="Edytuj" IsEnabled="False" Click="Edytuj_Click" Margin="3" Padding="10,3" >Edytuj...</Button>-->
<Button Name="Podglad" IsEnabled="False" Click="Podglad_Click" Margin="3" Padding="10,3" >Podgląd...</Button>
</StackPanel>
<ListView SelectionMode="Single" SelectionChanged="Selection_Changed" Name="lv_uzytkownicy" Margin="3" Grid.Row="1">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Imię"
DisplayMemberBinding="{Binding Imie}"/>
<GridViewColumn Header="Nazwisko"
DisplayMemberBinding="{Binding Nazwisko}" />
<GridViewColumn Header="Mail"
DisplayMemberBinding="{Binding Mail}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<GridSplitter Grid.Column="1" Grid.Row="1" Width="5" ResizeDirection="Columns" HorizontalAlignment="Center"/>
</Grid>
MainWindow.xaml.cs
using System.Collections.ObjectModel;
public partial class MainWindow : Window
{
public ObservableCollection<Uzytkownik> listaUzytkownikow = new ObservableCollection<Uzytkownik>();
Podglad_Uzytkownika podgladUzytkownika;
public MainWindow()
{
InitializeComponent();
lv_uzytkownicy.ItemsSource = listaUzytkownikow;
listaUzytkownikow.Add(new Uzytkownik("Mietek", "Żul", "sikalafa#wp.pl"));
listaUzytkownikow.Add(new Uzytkownik("Franek", "Alpinista", "halo#gmail.pl"));
listaUzytkownikow.Add(new Uzytkownik("Stefan", "Ulążka", "mam.to#o2.pl"));
this.DataContext = this;
}
private void Podglad_Click(object sender, RoutedEventArgs e)
{
podgladUzytkownika = new Podglad_Uzytkownika();
podgladUzytkownika.DataContext = lv_uzytkownicy.SelectedItem;
podgladUzytkownika.Show();
}
private void Zamknij_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void Selection_Changed(object sender, SelectionChangedEventArgs e)
{
if (lv_uzytkownicy.SelectedItems.Count > 0) Podglad.IsEnabled = true;
else Podglad.IsEnabled = false;
if (podgladUzytkownika != null && podgladUzytkownika.IsVisible)
{
podgladUzytkownika.DataContext = lv_uzytkownicy.SelectedItem;
}
}
}
Podglad_Uzytkownika.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Margin="3">Imię</Label>
<Label Margin="3" Grid.Row="1">Nazwisko</Label>
<Label Margin="3" Grid.Row="2">Email</Label>
<TextBox Name="imieTextBox" Text="{Binding Imie, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1"/>
<TextBox Name="nazwiskoTextBox" Text="{Binding Nazwisko, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1" Grid.Row="1"/>
<TextBox Name="mailTextBox" Text="{Binding Mail, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1" Grid.Row="2"/>
<Grid HorizontalAlignment="Center" Grid.ColumnSpan="2" Grid.Row="3" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="pierwsza" />
</Grid.ColumnDefinitions>
<Button Margin="3" Padding="20, 5" Name="Podglad" Click="Podglad_Click" IsDefault="True">Zamknij</Button>
</Grid>
</Grid>
Podglad_Uzytkownika.xaml.cs
public partial class Podglad_Uzytkownika : Window
{
public Podglad_Uzytkownika()
{
InitializeComponent();
}
private void Podglad_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
Uzytkownik.cs
public class Uzytkownik : INotifyPropertyChanged
{
private string imie;
private string nazwisko;
private string mail;
public Uzytkownik(string imie, string nazwisko, string mail)
{
this.Imie = imie;
this.Nazwisko = nazwisko;
this.Mail = mail;
}
public string Imie { get => this.imie; set { this.imie = value; OnPropertyChanged(); } }
public string Nazwisko { get => this.nazwisko; set { this.nazwisko = value; OnPropertyChanged(); } }
public string Mail { get => this.mail; set { this.mail = value; OnPropertyChanged(); } }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Show models data in TextBlocks

I'm need to show data from ViewModel in simple grid which contain labels and textBlock for data. I'm try to bind data to grid and use ElementName to bind grid's DataContext with textBlocks.
Book ViewModel:
namespace Books.ViewModels
{
public class BookViewModel : IViewModel, INotifyPropertyChanged
{
private Book book = new Book();
private ICommand AddCommand;
private ICommand RemoveCommand;
private ICommand ChangeCommand;
public event PropertyChangedEventHandler PropertyChanged;
public BookViewModel()
{
//initialize commands;
}
public string Name
{
get { return book.Name; }
set { book.Name = value; }
}
public string Authors
{
get
{
return string.Join(", ", book.Authors.Select(x => x.Name));
}
set
{
//need to implemented
}
}
public string ISBN
{
get { return book.ISBN; }
set { book.ISBN = value; }
}
public int Pages
{
get { return book.Pages; }
set { book.Pages = value; }
}
public string Tags
{
get
{
return string.Join(", ", book.Tags);
}
set
{
//not implemented
}
}
public int PublicationYear
{
get { return book.PublicationYear; }
set { book.PublicationYear = value; }
}
public string House
{
get
{
return book.House.Name;
}
set
{
//not implemented
}
}
public ICommand AddClick
{
get
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Add"));
return AddCommand;
}
}
public ICommand RemoveClick
{
get
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Remove"));
return RemoveCommand;
}
}
public ICommand ChangeClick
{
get
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Change"));
return ChangeCommand;
}
}
}
}
grid in Xaml:
<Grid x:Name="bookGrid"
Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Title:"/>
<Label Grid.Row="1" Grid.Column="0" Content="Author(s):"/>
<Label Grid.Row="2" Grid.Column="0" Content="ISBN:"/>
<Label Grid.Row="3" Grid.Column="0" Content="Pages:"/>
<Label Grid.Row="4" Grid.Column="0" Content="Tags:"/>
<Label Grid.Row="5" Grid.Column="0" Content="Publication Year:"/>
<Label Grid.Row="6" Grid.Column="0" Content="Publication House:"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="3" Text="{Binding ElementName=bookGrid, Path=Name}"/>
<TextBlock Grid.Column="1" Grid.Row="1" Margin="3" Text="{Binding ElementName=bookGrid, Path=Authors}"/>
<TextBlock Grid.Column="1" Grid.Row="2" Margin="3" Text="{Binding ElementName=bookGrid, Path=ISBN}"/>
<TextBlock Grid.Column="1" Grid.Row="3" Margin="3" Text="{Binding ElementName=bookGrid, Path=Pages}"/>
<TextBlock Grid.Column="1" Grid.Row="4" Margin="3" Text="{Binding ElementName=bookGrid, Path=Tags}"/>
<TextBlock Grid.Column="1" Grid.Row="5" Margin="3" Text="{Binding ElementName=bookGrid, Path=PublicationYear}"/>
<TextBlock Grid.Column="1" Grid.Row="6" Margin="3" Text="{Binding ElementName=bookGrid, Path=House}"/>
</Grid>
Code behind:
bookGrid.DataContext = Manager.books.First();
Just change the TextBlock bindings to something like:
<TextBlock Grid.Column="1" Grid.Row="0" Margin="3" Text="{Binding Name}"/>
Add a notifier method tho the viewmodel:
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And call this method after each property change like:
public string Name
{
get { return book.Name; }
set
{
book.Name = value;
this.NotifyPropertyChanged();
}
}

Unable to bind ViewModel to View

I want to Display a list of pictures in a LongListSelector on Windows Phone 8.
That´s my Model:
public class LocationMediaModel : INotifyPropertyChanged
{
private string _id;
public string ID
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
private string _mediatype;
public string MediaType
{
get
{
return _mediatype;
}
set
{
if (value != _mediatype)
{
_mediatype = value;
NotifyPropertyChanged("MediaType");
}
}
}
private string _url;
public string URL
{
get
{
return _url;
}
set
{
if (value != _url)
{
_url = value;
NotifyPropertyChanged("URL");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here´s my ViewModel:
public class LocationMediaViewModel : INotifyPropertyChanged
{
public LocationMediaViewModel()
{
this.MediaItems = new ObservableCollection<LocationMediaModel>();
}
public ObservableCollection<LocationMediaModel> MediaItems { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
// Properties vom Model <-> ViewModel:
private string _id;
public string ID
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
private string _mediatype;
public string MediaType
{
get
{
return _mediatype;
}
set
{
if (value != _mediatype)
{
_mediatype = value;
NotifyPropertyChanged("MediaType");
}
}
}
private string _url;
public string URL
{
get
{
return _url;
}
set
{
if (value != _url)
{
_url = value;
NotifyPropertyChanged("URL");
}
}
}
public bool IsDataLoaded
{
get;
private set;
}
public void LoadData()
{
// Sample data; replace with real data
this.MediaItems.Add(new LocationMediaModel() { ID = "3e3b1e48-8463-424b-8256-15c569358dfb", MediaType = "jpg", URL = "http://media.contoso.com/thumbs/f3be161f-cffc-485e-8f1d-52cfcebc1c79.jpg" });
this.MediaItems.Add(new LocationMediaModel() { ID = "e5e7cb87-04bb-498a-ab57-83bd5c3425c4", MediaType = "jpg", URL = "http://media.contoso.com/thumbs/99FBBBB0-9C16-4E0A-B6E7-A1A709638D06.jpg" });
this.MediaItems.Add(new LocationMediaModel() { ID = "b45931bf-0778-45cc-9efa-fc1aa397ccd9", MediaType = "jpg", URL = "http://media.contoso.com/thumbs/dff96a4f-f22f-4c6d-af24-504a1c80b7c4.jpg" });
this.MediaItems.Add(new LocationMediaModel() { ID = "a23e31b1-9232-086c23fe2279ab1bb22dd0", MediaType = "jpg", URL = "http://media.contoso.com/thumbs/1d1315c7-3292-4e67-8189-5db5393ec801.jpg" });
this.MediaItems.Add(new LocationMediaModel() { ID = "22991670-32b2-45f6-be69-3892b9587865", MediaType = "jpg", URL = "http://media.contoso.com/thumbs/433EA3D7-B9BF-4F3E-96C8-EEC1B9B04896.jpg" });
this.IsDataLoaded = true;
}
}
And finaly my XAML view:
<phone:PhoneApplicationPage
x:Class="WhaGoO.LocationPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DataContext="{d:DesignData DesignData/LocationDetailsSampleData.xaml}"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<Grid x:Name="LayoutRoot" Background="Transparent">
<phone:Pivot x:Name="pvtMainPivot" SelectionChanged="evt_PivotControl_SelectionChanged">
<!-- ++++++++++++++++++ Header -->
<phone:Pivot.Title>
<StackPanel HorizontalAlignment="Center">
<Image Stretch="None" HorizontalAlignment="Left" Margin="-5,0,0,0" MinWidth="50" MaxHeight="50" Source="/mAppData/logo.png"/>
</StackPanel>
</phone:Pivot.Title>
<!-- ++++++++++++++++++ EREIGNISSE -->
<phone:PivotItem x:Name="pivotitemFavoriten" Header="Ereignisse">
<StackPanel Margin="0,0,0,15" >
<Grid VerticalAlignment="Top" Margin="0,0,5,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="120" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" TextTrimming="WordEllipsis" Text="LocationName" TextWrapping="NoWrap" Style="{StaticResource PhoneTextLargeStyle}" VerticalAlignment="Top" Margin="0,0,0,22" />
<Image Grid.Column="0" Width="138" Height="25" Source="/mAppData/stars-3.png" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="0"/>
<TextBlock Grid.Column="1" Text="distance" TextWrapping="NoWrap" Style="{StaticResource PhoneTextSubtleStyle}" HorizontalAlignment="Right" Margin="0,0,-3,40" VerticalAlignment="Bottom"/>
<TextBlock Grid.Column="1" Text="lastupload" TextWrapping="NoWrap" Style="{StaticResource PhoneTextSubtleStyle}" HorizontalAlignment="Right" Margin="0,0,-3,20" VerticalAlignment="Bottom"/>
<TextBlock Grid.Column="1" Text="ratingscnt" TextWrapping="NoWrap" Style="{StaticResource PhoneTextSubtleStyle}" HorizontalAlignment="Right" Margin="0" VerticalAlignment="Bottom"/>
</Grid>
<TextBlock Grid.Column="1" Text="Marlene-Dietrich-Platz 1" TextWrapping="NoWrap" Style="{StaticResource PhoneTextSubtleStyle}" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Bottom"/>
<TextBlock Grid.Column="1" Text="Berlin" TextWrapping="NoWrap" Style="{StaticResource PhoneTextSubtleStyle}" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Bottom"/>
<phone:LongListSelector Name="lls_PhotoHub" Margin="0" IsGroupingEnabled="False" LayoutMode="Grid" DataContext="LocationMediaViewModel" ItemsSource="{Binding MediaItems}" GridCellSize="108,108" >
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Image Width="100" Height="100" Source="{Binding URL}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</StackPanel>
</phone:PivotItem>
<!-- ++++++++++++++++++ 2.PivotItem-->
<phone:PivotItem x:Name="pivotitemUmgebung" Header="Karte">
</phone:PivotItem>
</phone:Pivot>
</Grid>
</phone:PhoneApplicationPage>
But the binding seems not working -> no Images are displayed.
Can anyone help?
As Florent Gz pointed out, I don't see any DataContext initialization either.
In your PhoneApplicationPage code behind, in the constructor, add this:
this.DataContext = new LocationMediaViewModel();

Binding Validation.HasError property in MVVM

I am currently implementing a ValidationRule to check if some invalid character are in a TextBox. I am happy that setting the class I have implemented that inherits ValidationRule on my TextBox sets it in red when such characters are found, but I would also like to use the Validation.HasError property or the Validation.Errors property to pop a messagebox telling the user that there are errors in the various textboxes in the page.
Is there a way to bind a property in my ViewModel to the Validation.HasError and/or to the Validation.Errors properties in order for me to have access to them in my ViewModel?
Here is my error style for the TextBox:
<Style x:Key="ErrorValidationTextBox" TargetType="{x:Type pres:OneTextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right"
Foreground="Red"
FontSize="12pt"
Text="{Binding ElementName=MyAdorner,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
</TextBlock>
<AdornedElementPlaceholder x:Name="MyAdorner"/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is how I declare my TextBox (OneTextBox encapsulates the regular WPF TextBox) in my XAML:
<pres:OneTextBox Watermark="Name..." Margin="85,12,0,0" Style="{StaticResource ErrorValidationTextBox}"
AcceptsReturn="False" MaxLines="1" Height="22" VerticalAlignment="Top"
HorizontalAlignment="Left" Width="300" >
<pres:OneTextBox.Text>
<Binding Path="InterfaceSpecification.Name" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<interfaceSpecsModule:NoInvalidCharsRule/>
</Binding.ValidationRules>
</Binding>
</pres:OneTextBox.Text>
</pres:OneTextBox>
The Validation.HasError is readonly property, therefore Binding will not work with this property. This can be seen in ILSpy:
public virtual bool HasError
{
get
{
return this._validationError != null;
}
}
As an alternative, you should see a great article which provides a solution in the form of use attached dependency properties, there you will see a detailed explanation of the example.
Below is a full example from this article, I just translated it under C#, the original language is VB.NET:
XAML
<Window x:Class="HasErrorTestValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HasErrorTestValidation"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:TestData />
</Window.DataContext>
<StackPanel>
<TextBox x:Name="TestTextBox"
local:ProtocolSettingsLayout.MVVMHasError="{Binding Path=HasError}">
<TextBox.Text>
<Binding Path="TestText" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:OnlyNumbersValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock>
<TextBlock.Text>
<Binding Path="HasError" StringFormat="HasError is {0}"/>
</TextBlock.Text>
</TextBlock>
<TextBlock>
<TextBlock.Text>
<Binding Path="(Validation.HasError)" ElementName="TestTextBox" StringFormat="Validation.HasError is {0}"/>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Window>
Code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
#region Model
public class TestData : INotifyPropertyChanged
{
private bool _hasError = false;
public bool HasError
{
get
{
return _hasError;
}
set
{
_hasError = value;
NotifyPropertyChanged("HasError");
}
}
private string _testText = "0";
public string TestText
{
get
{
return _testText;
}
set
{
_testText = value;
NotifyPropertyChanged("TestText");
}
}
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string sProp)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sProp));
}
}
#endregion
}
#endregion
#region ValidationRule
public class OnlyNumbersValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var result = new ValidationResult(true, null);
string NumberPattern = #"^[0-9-]+$";
Regex rgx = new Regex(NumberPattern);
if (rgx.IsMatch(value.ToString()) == false)
{
result = new ValidationResult(false, "Must be only numbers");
}
return result;
}
}
#endregion
public class ProtocolSettingsLayout
{
public static readonly DependencyProperty MVVMHasErrorProperty= DependencyProperty.RegisterAttached("MVVMHasError",
typeof(bool),
typeof(ProtocolSettingsLayout),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
null,
CoerceMVVMHasError));
public static bool GetMVVMHasError(DependencyObject d)
{
return (bool)d.GetValue(MVVMHasErrorProperty);
}
public static void SetMVVMHasError(DependencyObject d, bool value)
{
d.SetValue(MVVMHasErrorProperty, value);
}
private static object CoerceMVVMHasError(DependencyObject d,Object baseValue)
{
bool ret = (bool)baseValue;
if (BindingOperations.IsDataBound(d,MVVMHasErrorProperty))
{
if (GetHasErrorDescriptor(d)==null)
{
DependencyPropertyDescriptor desc = DependencyPropertyDescriptor.FromProperty(Validation.HasErrorProperty, d.GetType());
desc.AddValueChanged(d,OnHasErrorChanged);
SetHasErrorDescriptor(d, desc);
ret = System.Windows.Controls.Validation.GetHasError(d);
}
}
else
{
if (GetHasErrorDescriptor(d)!=null)
{
DependencyPropertyDescriptor desc= GetHasErrorDescriptor(d);
desc.RemoveValueChanged(d, OnHasErrorChanged);
SetHasErrorDescriptor(d, null);
}
}
return ret;
}
private static readonly DependencyProperty HasErrorDescriptorProperty = DependencyProperty.RegisterAttached("HasErrorDescriptor",
typeof(DependencyPropertyDescriptor),
typeof(ProtocolSettingsLayout));
private static DependencyPropertyDescriptor GetHasErrorDescriptor(DependencyObject d)
{
var ret = d.GetValue(HasErrorDescriptorProperty);
return ret as DependencyPropertyDescriptor;
}
private static void OnHasErrorChanged(object sender, EventArgs e)
{
DependencyObject d = sender as DependencyObject;
if (d != null)
{
d.SetValue(MVVMHasErrorProperty, d.GetValue(Validation.HasErrorProperty));
}
}
private static void SetHasErrorDescriptor(DependencyObject d, DependencyPropertyDescriptor value)
{
var ret = d.GetValue(HasErrorDescriptorProperty);
d.SetValue(HasErrorDescriptorProperty, value);
}
}
As an alternative to the use of ValidationRule, in MVVM style you can try to implement IDataErrorInfo Interface. For more info see this:
Enforcing Complex Business Data Rules with WPF
all perfect work set NotifyOnValidationError="True" on binding;
(or maybe with binding group also possible)
then use
<Button IsEnabled="{Binding ElementName=tbPeriod, Path=(Validation.HasError)}"
sample with one textBox:
<val:RangeRulecan be changed to ms sample agerangerule etc
<TextBox MaxLength="5" x:Name="tbPeriod" HorizontalAlignment="Left" VerticalAlignment="Top" Width="162" Margin="10,10,0,0" Style="{StaticResource TextBoxInError}">
<TextBox.Text>
<Binding Path="ReportPeriod" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<val:RangeRule Min="70" Max="5000" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
In response to Anatoliy's request for an example of a non-working project:
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestAttachedPropertyValidationError">
<Style TargetType="{x:Type local:TextBoxCustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TextBoxCustomControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Grid.Resources>
<Label
Grid.Row ="0"
Grid.Column="0"
Content="Enter a numeric value:" />
<TextBox
Grid.Row ="0"
Grid.Column="2"
local:HasErrorUtility.HasError="{Binding NumericPropHasError, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Text="{Binding NumericProp, Mode=TwoWay, UpdateSourceTrigger=LostFocus, RelativeSource={RelativeSource TemplatedParent}}" />
<Label
Grid.Row ="1"
Grid.Column="0"
Content="Value entered:" />
<Label
Grid.Row ="1"
Grid.Column="2"
Content="{TemplateBinding NumericProp}" />
<Label
Grid.Row ="2"
Grid.Column="0"
Grid.ColumnSpan="3"
Visibility="{TemplateBinding NumericPropHasError, Converter={StaticResource BooleanToVisibilityConverter}}"
Foreground="Red"
Content="Not a numeric value" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TextBoxCustomControl.cs
using System.Windows;
using System.Windows.Controls;
namespace TestAttachedPropertyValidationError
{
public class TextBoxCustomControl : Control
{
static TextBoxCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxCustomControl), new FrameworkPropertyMetadata(typeof(TextBoxCustomControl)));
}
public static readonly DependencyProperty NumericPropProperty =
DependencyProperty.Register("NumericProp", typeof (int), typeof (TextBoxCustomControl), new PropertyMetadata(default(int)));
public int NumericProp
{
get { return (int) GetValue(NumericPropProperty); }
set { SetValue(NumericPropProperty, value); }
}
public static readonly DependencyProperty NumericPropHasErrorProperty =
DependencyProperty.Register("NumericPropHasError", typeof (bool), typeof (TextBoxCustomControl), new PropertyMetadata(default(bool)));
public bool NumericPropHasError
{
get { return (bool) GetValue(NumericPropHasErrorProperty); }
set { SetValue(NumericPropHasErrorProperty, value); }
}
}
}
HasErrorUtility.cs
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace TestAttachedPropertyValidationError
{
class HasErrorUtility
{
public static readonly DependencyProperty HasErrorProperty = DependencyProperty.RegisterAttached("HasError",
typeof(bool),
typeof(HasErrorUtility),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
null,
CoerceHasError));
public static bool GetHasError(DependencyObject d)
{
return (bool)d.GetValue(HasErrorProperty);
}
public static void SetHasError(DependencyObject d, bool value)
{
d.SetValue(HasErrorProperty, value);
}
private static object CoerceHasError(DependencyObject d, Object baseValue)
{
var ret = (bool)baseValue;
if (BindingOperations.IsDataBound(d, HasErrorProperty))
{
if (GetHasErrorDescriptor(d) == null)
{
var desc = DependencyPropertyDescriptor.FromProperty(Validation.HasErrorProperty, d.GetType());
desc.AddValueChanged(d, OnHasErrorChanged);
SetHasErrorDescriptor(d, desc);
ret = Validation.GetHasError(d);
}
}
else
{
if (GetHasErrorDescriptor(d) != null)
{
var desc = GetHasErrorDescriptor(d);
desc.RemoveValueChanged(d, OnHasErrorChanged);
SetHasErrorDescriptor(d, null);
}
}
return ret;
}
private static readonly DependencyProperty HasErrorDescriptorProperty = DependencyProperty.RegisterAttached("HasErrorDescriptor",
typeof(DependencyPropertyDescriptor),
typeof(HasErrorUtility));
private static DependencyPropertyDescriptor GetHasErrorDescriptor(DependencyObject d)
{
var ret = d.GetValue(HasErrorDescriptorProperty);
return ret as DependencyPropertyDescriptor;
}
private static void SetHasErrorDescriptor(DependencyObject d, DependencyPropertyDescriptor value)
{
d.SetValue(HasErrorDescriptorProperty, value);
}
private static void OnHasErrorChanged(object sender, EventArgs e)
{
var d = sender as DependencyObject;
if (d != null)
{
d.SetValue(HasErrorProperty, d.GetValue(Validation.HasErrorProperty));
}
}
}
}
ViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace TestAttachedPropertyValidationError
{
public class ViewModel :INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _vmNumericProp;
private bool _vmNumericPropHasError;
public int VmNumericProp
{
get { return _vmNumericProp; }
set
{
_vmNumericProp = value;
OnPropertyChanged();
}
}
public bool VmNumericPropHasError
{
get { return _vmNumericPropHasError; }
set
{
_vmNumericPropHasError = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainWindow.xaml
<Window x:Class="TestAttachedPropertyValidationError.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestAttachedPropertyValidationError"
Title="MainWindow" Height="350" Width="525">
<StackPanel Margin="10">
<StackPanel.Resources>
<local:ViewModel x:Key="VM1"/>
<local:ViewModel x:Key="VM2"/>
</StackPanel.Resources>
<Label Content="Custom Control...}"></Label>
<local:TextBoxCustomControl
Margin="10"
DataContext="{StaticResource VM1}"
NumericProp="{Binding VmNumericProp}"
NumericPropHasError="{Binding VmNumericPropHasError}"/>
<Label Content="Regular XAML...}" Margin="0,20,0,0"/>
<Grid
Margin="10"
DataContext="{StaticResource VM2}"
>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Grid.Resources>
<Label
Grid.Row ="0"
Grid.Column="0"
Content="Enter a numeric value:" />
<TextBox
Grid.Row ="0"
Grid.Column="2"
local:HasErrorUtility.HasError="{Binding VmNumericPropHasError, Mode=TwoWay}"
Text="{Binding VmNumericProp, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
<Label
Grid.Row ="1"
Grid.Column="0"
Content="Value entered:" />
<Label
Grid.Row ="1"
Grid.Column="2"
Content="{Binding VmNumericProp}" />
<Label
Grid.Row ="2"
Grid.Column="0"
Grid.ColumnSpan="3"
Visibility="{Binding VmNumericPropHasError, Converter={StaticResource BooleanToVisibilityConverter}}"
Foreground="Red"
Content="Not a numeric value" />
</Grid>
</StackPanel>

Filtered ObservableCollection does not update ListView on WPF UI

I am trying to filter a ObservableCollection databinded to a ListView on my WPF UI. However, when the collection refreshes, the ListView becomes blank and doesn't show anything. I am also trying to teach myself the MVVM pattern. The issue is probably how I'm not refreshing the control, but how can I get it to refresh in the MVVM pattern? I'm used to doing Winforms and code behinds, and the filter logic happens when the user clicks on the Search button - which calls the ApplicationModel.Search method.
Model code:
public class AppObject
{
public string Name { get; set; }
public string Description { get; set; }
public string Owner { get; set; }
public string Email { get; set; }
public AppObject(string name, string desc, string owner, string email)
{
this.Name = name;
this.Description = desc;
this.Owner = owner;
this.Email = email;
}
}
public class ApplicationsModel : ObservableCollection<AppObject>
{
private static object threadLocker;
private static ApplicationsModel current;
static ApplicationsModel()
{
threadLocker = new object();
}
public static ApplicationsModel Current
{
get
{
lock (threadLocker)
{
if (current == null)
{
current = new ApplicationsModel();
}
}
return current;
}
}
private ApplicationsModel()
{
this.Refresh();
}
private ApplicationsModel(IEnumerable<AppObject> collection)
: base(collection)
{
}
private void Refresh()
{
try
{
// Query database to get the initial data - this code works fine
}
catch (Exception e)
{
string error = String.Format("Could not refresh repository list: {0}", e.Message);
MessageBox.Show(error, "Error Refreshing", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public void Search(string key)
{
IEnumerable<AppObject> newList = current.Items.Where<AppObject>(t => t.Name.Equals(key));
foreach (AppObject app in newList)
{
Console.WriteLine(app.Name);
Console.WriteLine(app.Description);
}
current.Clear();
current = null;
current = new ApplicationsModel(newList);
}
public void ClearSearch()
{
Console.WriteLine("ClearSearch method called");
}
}
ViewModel code:
public class ApplicationViewModel
{
public ObservableCollection<AppObject> AppCollection { get; set; }
static string searchString;
static string emailString;
public AppObject SelectedApp { get; set; }
public string AppToSearch
{
get
{
return searchString;
}
set
{
searchString = value;
}
}
public string AppToRequest
{
get
{
return SelectedApp.Email;
}
set
{
SelectedApp.Email = value;
}
}
private SearchButtonCommand searchButtonCmd;
private ClearButtonCommand clearButtonCmd;
private EmailButtonCommand emailButtonCmd;
public ApplicationViewModel()
{
this.AppCollection = ApplicationsModel.Current;
}
public ICommand SearchButtonPressed
{
get
{
if (this.searchButtonCmd == null)
{
this.searchButtonCmd = new SearchButtonCommand();
}
return this.searchButtonCmd;
}
}
public ICommand ClearButtonPressed
{
get
{
if (this.clearButtonCmd == null)
{
this.clearButtonCmd = new ClearButtonCommand();
}
return this.clearButtonCmd;
}
}
public ICommand EmailButtonPressed
{
get
{
if (this.emailButtonCmd == null)
{
this.emailButtonCmd = new EmailButtonCommand();
}
return this.emailButtonCmd;
}
}
private class SearchButtonCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
string searchkey = ApplicationViewModel.searchString;
ApplicationsModel.Current.Search(searchkey);
}
public bool CanExecute(object parameter)
{
return true;
}
}
private class ClearButtonCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
ApplicationsModel.Current.ClearSearch();
}
public bool CanExecute(object parameter)
{
return true;
}
}
private class EmailButtonCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
string targetEmail = ApplicationViewModel.emailString;
}
public bool CanExecute(object parameter)
{
return true;
}
}
}
UI XAML:
<Window.DataContext>
<vm:ApplicationViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Height="84" HorizontalAlignment="Left" Margin="0,5,5,5" Name="imgNexusLogo" Stretch="Fill" VerticalAlignment="Top" Width="600" Source="C:\source\Nexus\NexusShop\Images\nexus1bannerlong.png" />
<Grid Grid.Row="1" HorizontalAlignment="Center" Margin="0,5,5,5" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Search for Application">
<Label.Foreground>
<SolidColorBrush Color="LightCyan" />
</Label.Foreground>
</Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="3" Width="500" Text="{Binding AppToSearch}" />
<Button Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" Width="100" Height="20" Margin="3" Background="LightCyan" Content="Search" vm:ButtonBehaviour.SearchCommand="{Binding SearchButtonPressed}" />
<Button Grid.Row="0" Grid.Column="3" HorizontalAlignment="Right" Width="100" Height="20" Margin="3" Background="LightCyan" Content="Clear Search" vm:ButtonBehaviour.ClearCommand="{Binding ClearButtonPressed}"/>
</Grid>
<ListView Grid.Row="2" BorderBrush="Black" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=AppCollection}" SelectedItem="{Binding SelectedNexusApp}">
<ListView.View>
<GridView>
<GridViewColumn Header="Application Name" Width="100" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Application Description" Width="800" DisplayMemberBinding="{Binding Description}"/>
<GridViewColumn Header="Application Owner" Width="100" DisplayMemberBinding="{Binding Owner}"/>
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="3" HorizontalAlignment="Center" Width="200" Height="30" Margin="3" Background="LightCyan" Content="Request Application" vm:ButtonBehaviour.EmailCommand="{Binding EmailButtonPressed}" />
</Grid>
Again, The functionality which filters occurs when the search button is clicked, and it runs the method on ApplicationModel.Search(string stringtofilteron). I based my MVVM classes on this tutorial: http://www.codeproject.com/Articles/81484/A-Practical-Quick-start-Tutorial-on-MVVM-in-WPF
Thanks for your help!
Fixed code:
public class ApplicationViewModel : INotifyPropertyChanged
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
ApplicationsModel.Current.Search(this.searchString);
NotifyPropertyChanged();
And I changed the Refresh method in my model to do the filtering there.
Your ViewModel needs to Implement INotifyPropertyChanged, and notify when AppCollection has been changed.
Reference:
http://msdn.microsoft.com/en-us/library/ms229614.aspx

Categories

Resources