How to use the RelayCommand - c#

I have application that works with Model View ViewModel.
In my model I have a List based on my Client class.
public class Client
{
public string Name { get; set; }
public string Ip { get; set; }
public string Mac { get; set; }
}
In my ClientRepository I make a List from XML file with my Client class.
public ClientRepository()
{
var xml = "Clients.xml";
if (File.Exists(xml))
{
_clients = new List<Client>();
XDocument document = XDocument.Load(xml);
foreach (XElement client in document.Root.Nodes())
{
string Name = client.Attribute("Name").Value;
string Ip = client.Element("IP").Value;
string Mac = client.Element("MAC").Value;
_clients.Add(new Client() { Mac = Mac, Name = Name, Ip = Ip });
}
}
}
In my UI/UX I have 3 Textboxes 1 for MAC, 1 IP and 1 Name I also have a Button thats has a binding to AddClientCommand.
<Label Grid.Row="0" Grid.Column="0" Content="Host Name:"/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="tbHostName" Height="20" Text="{Binding Path=newClient.Name, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Row="1" Grid.Column="0" Content="IP Address:"/>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="tbIP" Height="20" Text="{Binding Path=newClient.Ip, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Row="2" Grid.Column="0" Content="MAC Address"/>
<TextBox Grid.Row="2" Grid.Column="1" x:Name="tbMAC" Height="20" Text="{Binding Path=newClient.Mac, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Row="3" Grid.Column="0" Content="Remove" x:Name="bRemove" Margin="3 0 3 0" Click="bRemove_Click"/>
<Button Grid.Row="3" Grid.Column="1" Content="Add" x:Name="bAdd" Margin="3 0 3 0" Click="bAdd_Click" Command="{Binding AddClientCommand}"/>
To come to my point: What I want to know is what is the best way to implement the AddClientCommand?
What I currently have and I know it doesn't work:
public ClientViewModel()
{
_repository = new ClientRepository();
_clients = _repository.GetClients();
WireCommands();
}
private void WireCommands()
{
AddClientCommand = new RelayCommand(AddClient);
}
public Client newClient
{
get
{
return _newClient;
}
set
{
_newClient = value;
OnPropertyChanged("newClient");
AddClientCommand.isEnabled = true;
}
}
public void AddClient()
{
_repository.AddClient(newClient);
}
RelayCommand class:
public class RelayCommand : ICommand
{
private readonly Action _handler;
private bool _isEnabled;
public RelayCommand(Action handler)
{
_handler = handler;
}
public bool isEnabled
{
get { return true; }
set
{
if (value != isEnabled)
{
_isEnabled = value;
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
}
public bool CanExecute(object parameter)
{
return isEnabled;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_handler();
}
}

I recommend you to use DelegateCommands, you will find this class in many MVVM frameworks:
public ICommand AddClientCommand
{
get
{
return new DelegateCommand(AddClient, CanExecuteAddClient);
}
}
I also see that _clients is of type List<Client>. If you are binding this to the UI to see the clients list, changes will not be notified unless you use ObservableCollection<Client>
Edit: As someone pointed out in comments, you should create the _newClient. Be aware of creating a new one for each client added, or you will end up adding the same instance of Client over and over!

Have you just tried putting your command into a property something like this?:
public ICommand AddClientCommand
{
get { return new RelayCommand(AddClient, CanAddClient); }
}
public bool CanAddClient()
{
return newClient != null;
}
Put whatever logic you want to inside the CanAddClient to enable or disable the ICommand.
Ahhhh... I see... you have the wrong implementation of the RelayCommand. You need one that uses the CanExecuteChanged event handler... you can find the correct implementation in the RelayCommand.cs page on GitHub.

Related

How to get property and call command from different ViewModel with mvvm pattern

I have a ViewModel with all the properties that i will need in every sub ViewModel.
It's the first time i try to split commands and viewmodel to multiple files. Last time everything was in the same ViewModel and it was a pain to work with it. Everything shows up as expected but i want to find a way to pass the same data in every viewmodel.
From my GetOrdersCommand, i want to get the HeaderViewModel.SelectedSource property. I didn't find any way to do it without getting a null return or loosing the property data...
I would like to call my GetOrdersCommand from HeaderView button too.
Any tips how i can achieve this ? Perhaps, my design is not good for what i'm trying to do ?
MainWindow.xaml
<views:HeaderView Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" DataContext="{Binding HeaderViewModel}" LoadHeaderViewCommand="{Binding LoadHeaderViewCommand}"/>
<TabControl TabStripPlacement="Bottom" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2">
<TabItem Header="General">
</TabItem>
<TabItem Header="Orders">
<views:OrderView DataContext="{Binding OrderViewModel}" GetOrdersCommand="{Binding GetOrdersCommand}"/>
</TabItem>
</TabControl>
HeaderView.xaml
<DockPanel>
<ComboBox DockPanel.Dock="Left" Width="120" Margin="4" VerticalContentAlignment="Center" ItemsSource="{Binding SourceList}" SelectedItem="{Binding SelectedSource}" DisplayMemberPath="SourceName"/>
<Button x:Name="btnTest" HorizontalAlignment="Left" DockPanel.Dock="Left" Margin="4" Content="Test"/>
</DockPanel>
HeaderView.xaml.cs
public partial class OrderView : UserControl
{
public ICommand GetOrdersCommand
{
get { return (ICommand)GetValue(GetOrdersCommandProperty); }
set { SetValue(GetOrdersCommandProperty, value); }
}
public static readonly DependencyProperty GetOrdersCommandProperty =
DependencyProperty.Register("GetOrdersCommand", typeof(ICommand), typeof(OrderView), new PropertyMetadata(null));
public OrderView()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (GetOrdersCommand != null)
{
GetOrdersCommand.Execute(this);
}
}
}
MainViewModel.cs
private OrderViewModel orderViewModel;
public OrderViewModel OrderViewModel { get; set; } // Getter, setter with OnPropertyChanged
private HeaderViewModel headerViewModel;
public HeaderViewModel HeaderViewModel { get; set; } // Getter, setter with OnPropertyChanged
public MainViewModel()
{
HeaderViewModel = new HeaderViewModel();
OrderViewModel = new OrderViewModel();
}
HeaderViewModel.cs
public ICommand LoadHeaderViewCommand { get; set; }
public HeaderViewModel()
{
LoadHeaderViewCommand = new LoadHeaderViewCommand(this);
}
GetOrdersCommand.cs
public class GetOrdersCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly OrderViewModel _orderViewModel;
public GetOrdersCommand(OrderViewModel orderViewModel)
{
_orderViewModel = orderViewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
/* Build Order List according to HeaderViewModel.SelectedSource */
_orderViewModel.Orders = new ObservableCollection<Order>()
{
new Order { ID = 1, IsReleased = false, Name = "Test1"},
new Order { ID = 2, IsReleased = true, Name = "Test2"},
};
}
}
Thanks guys ! I moved my commands to their owning ViewModel as suggested.
I tried MVVVM Light Tools and found about Messenger Class.
I used it to send my SelectedSource (Combobox from HeaderView) from HeaderViewModel to OrderViewModel. Am i suppose to use Messenger class like that ? I don't know, but it did the trick!!!
I thought about moving GetOrdersCommand to OrderViewModel, binding my button command to OrderViewModel, binding SelectedSource as CommandParameter but i didn't know how i was suppose to RaiseCanExecuteChanged when HeaderViewModel.SelectedSource changed... Any advice?
MainWindow.xaml
<views:HeaderView DataContext="{Binding Source={StaticResource Locator}, Path=HeaderVM}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"/>
<TabControl TabStripPlacement="Bottom" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2">
<TabItem Header="General">
</TabItem>
<TabItem Header="Orders">
<views:OrderView DataContext="{Binding Source={StaticResource Locator}, Path=OrderVM}"/>
</TabItem>
</TabControl>
OrderViewModel.cs
private ObservableCollection<Order> _orders;
public ObservableCollection<Order> Orders
{
get { return _orders; }
set
{
if (_orders != value)
{
_orders = value;
RaisePropertyChanged(nameof(Orders));
}
}
}
public OrderViewModel()
{
Messenger.Default.Register<Source>(this, source => GetOrders(source));
}
private void GetOrders(Source source)
{
if (source.SourceName == "Production")
{
Orders = new ObservableCollection<Order>(){
new Order { ID = 1, IsReleased = false, Name = "Production 1" }
};
}
else
{
Orders = new ObservableCollection<Order>(){
new Order { ID = 2, IsReleased = true, Name = "Test 1" }
};
}
}
Part of HeaderViewModel.cs
private Source _SelectedSource;
public Source SelectedSource
{
get { return _SelectedSource; }
set
{
if (_SelectedSource != value)
{
_SelectedSource = value;
RaisePropertyChanged(nameof(SelectedSource));
GetOrdersCommand.RaiseCanExecuteChanged();
}
}
}
private RelayCommand _GetOrdersCommand;
public RelayCommand GetOrdersCommand
{
get
{
if (_GetOrdersCommand == null)
{
_GetOrdersCommand = new RelayCommand(GetOrders_Execute, GetOrders_CanExecute);
}
return _GetOrdersCommand;
}
}
private void GetOrders_Execute()
{
Messenger.Default.Send(SelectedSource);
}
private bool GetOrders_CanExecute()
{
return SelectedSource != null ? true : false;
}

Instanciate UIElement from XAML

What I'm trying to achieve doesn't sounds like rocket science. What I'm trying to create is a custom control to which I could pass a list of UIElements items directly from XAML so each element could be different and embed different objects (grid / textbox / panel etc ... ).
Here is the xaml code I would like to use:
<wpf:TileListDoubleItem>
<wpf:TileListDoubleItem.FrontItem>
<Grid>
<TextBlock FontFamily="Calibri,Verdana" FontSize="16" FontWeight="Bold" Foreground="White" Text="Hello"></TextBlock>
</Grid>
</wpf:TileListDoubleItem.FrontItem>
<wpf:TileListDoubleItem.BackItem>
<Grid>
<TextBlock FontFamily="Calibri,Verdana" FontSize="16" FontWeight="Bold" Foreground="White" Text="World"></TextBlock>
</Grid>
</wpf:TileListDoubleItem.BackItem>
</wpf:TileListDoubleItem>
And here is my custom control code:
public partial class TileListDoubleItem : UserControl, INotifyPropertyChanged
{
private bool _flipped;
internal bool CanFlip { get { return true; } }
private bool flipped
{
get {
return this._flipped;
}
set {
this._flipped = value;
DisplayItem = this._flipped ? BackItem : FrontItem;
}
}
public ObservableCollection<TileSide> Sides { get; set; }
public ICommand FlipCommand;
public TileListDoubleItem()
{
InitializeComponent();
FlipCommand = new FlipCommand(this);
flipped = false;
}
private UIElement displayItem { get; set; }
public UIElement DisplayItem
{
get { return this.displayItem; }
set {
if (this.displayItem != value)
{
this.displayItem = value;
OnPropertyChanged("DisplayItem");
}
}
}
public void Flip()
{
try
{
flipped = !flipped;
}
catch (Exception ex)
{
throw ex;
}
}
public UIElement FrontItem
{
get { return (UIElement)GetValue(FrontItemProperty); }
set { SetValue(FrontItemProperty, value); }
}
public static readonly DependencyProperty FrontItemProperty =
DependencyProperty.Register("FrontItem", typeof(UIElement), typeof(TileListDoubleItem), new UIPropertyMetadata(null));
public UIElement BackItem
{
get { return (UIElement)GetValue(BackItemProperty); }
set { SetValue(BackItemProperty, value); }
}
public static readonly DependencyProperty BackItemProperty =
DependencyProperty.Register("BackItem", typeof(UIElement), typeof(TileListDoubleItem), new UIPropertyMetadata(null));
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
When I run this, both my FrontItem and BackItem are equal to null and are never set to the UIElement (Grid in this example).
I guess what I'm missing must be very obvious to some people.
Thanks in advance for anyone's help here.
Your properies are set as expected. You can confirm this by for example create two TextBlocks that binds to the properties of your control:
<wpf:TileListDoubleItem x:Name="control">
<wpf:TileListDoubleItem.FrontItem>
<Grid>
<TextBlock FontFamily="Calibri,Verdana" FontSize="16" FontWeight="Bold" Foreground="White" Text="Hello"></TextBlock>
</Grid>
</wpf:TileListDoubleItem.FrontItem>
<wpf:TileListDoubleItem.BackItem>
<Grid>
<TextBlock FontFamily="Calibri,Verdana" FontSize="16" FontWeight="Bold" Foreground="White" Text="World"></TextBlock>
</Grid>
</wpf:TileListDoubleItem.BackItem>
</wpf:TileListDoubleItem>
<TextBlock Text="{Binding FrontItem.Children[0].Text, ElementName=control}" />
<TextBlock Text="{Binding BackItem.Children[0].Text, ElementName=control}" />
Obviously the properties won't be set by the time the constructor of TileListDoubleItem returns. The XAML parser needs to instantiate the object before it can set any of its properties, just like you do when you create an instance of a class yourself.

WPF Binding to member of a class within a class

My first try at C#, WPF, and MVVM. I've looked at several answers and tutorials and just cannot seem to get this right.
I have a View a Model and a ViewModel file (Actually more, but trying to simplify). In the view I want to bind a textbox to a view model member. I also want to bind a button click to a view model method.
The delegate command for Login() works fine, but I can't seem to update the ID property in Acc.ID.
What would I need to change to be able to do both?
I understand I will probably need to implement the PropertyChanged event in the ViewModel instead of the Model...I just don't understand how.
What I can do is set the DataContextto user.Acc in the code behind to directly update the model, but then I obviously cannot bind to the Login() method.
ViewModel.cs
public class LoginVM
{
private ServerInterface _serverInterface;
private ICommand _loginCommand;
private EmployeeAccount _acc;
public ICommand LoginCommand
{
get { return _loginCommand; }
}
public LoginVM()
{
Acc = new EmployeeAccount();
_serverInterface = new ServerInterface();
_loginCommand = new DelegateCommand<String>(Login);
}
public EmployeeAccount Acc { get; set; }
private void Login(object state)
{
this.Acc.ID = _serverInterface.Encrypt(this.Acc.ID);
}
}
View.xaml.cs
public partial class LoginView : Window
{
public LoginView()
{
InitializeComponent();
BindInXaml();
}
private void BindInXaml()
{
base.DataContext = new LoginVM();
}
}
Model.cs
public class EmployeeAccount : INotifyPropertyChanged
{
String _id;
public EmployeeAccount()
{
ID = "5000";
Name = "George Washington";
isAdmin = true;
Pswd = "TheyDont";
}
public String ID
{
get { return _id; }
set
{
_id = value;
this.OnPropertyChanged("ID");
}
}
public string Name { get; set; }
public Boolean isAdmin { get; set; }
public string Pswd { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
.xaml Put in only what matters really
<TextBox x:Name="txtLogInName" Margin="60,43,42,129" TextWrapping="Wrap" Text="{Binding Path=ID, UpdateSourceTrigger=PropertyChanged}" Width="120" Grid.Column="1" Grid.Row="1"/>
<Button x:Name="btnLogIn" Content="Log on" Command="{Binding LoginCommand}" Margin="160,151,10,23" Grid.Column="1" Grid.Row="1" RenderTransformOrigin="1.667,0.545"/>
<TextBlock x:Name="txtBlockPassReset" TextAlignment="Center" Grid.Row="1" Grid.Column="1" RenderTransformOrigin="1.348,1.765" Margin="60,101,42,78">
<Hyperlink>Reset Password</Hyperlink>
</TextBlock>
<PasswordBox x:Name="pswdBoxLoginPass" Grid.Column="1" HorizontalAlignment="Left" Margin="60,72,0,0" Grid.Row="1" VerticalAlignment="Top" Width="120" Password="Password"/>
Try changing your xaml from
<TextBox x:Name="txtLogInName" Margin="60,43,42,129" TextWrapping="Wrap"
Text="{Binding Path=ID, UpdateSourceTrigger=PropertyChanged}"
Width="120" Grid.Column="1" Grid.Row="1"/>
to
<TextBox x:Name="txtLogInName" Margin="60,43,42,129" TextWrapping="Wrap"
Text="{Binding Path=Acc.ID, UpdateSourceTrigger=PropertyChanged}"
Width="120" Grid.Column="1" Grid.Row="1" />

Access collection through MVVM Command

In my ViewModel i Have base Card class and Deck class which contain Observable Collection of Cards. Here is how it is bound in XAML
<GridView ItemsSource="{Binding DeckCollection}" IsItemClickEnabled="True" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Path=??}"
CommandParameter=??
<Button.Content>
<Grid>
<Image
Source="{Binding ImagePath}"
Stretch="None"/>
</Grid>
</Button.Content>
</Button>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Here are my classes
class Deck
{
private ObservableCollection<Card> _deckCollection = new ObservableCollection<Card>();
public ObservableCollection<Card> DeckCollection
{
get { return _deckCollection; }
set { _deckCollection = value; }
}
public Deck()
{
ActionCommand = new MyCommand();
ActionCommand.CanExecuteFunc = obj => true;
ActionCommand.ExecuteFunc = AddToList;
}
public void AddToList(object parameter)
{
var clickedCard = this;
//add Card to list which in this case is not possible
//DeckCollection.Add(this) ?
}
}
class Card
{
public String Name { get; set; }
public int Cost { get; set; }
public String ImagePath { get; set; }
public MyCommand ActionCommand { get; set; }
}
And also MyCommand class
public class MyCommand : ICommand
{
public Predicate<object> CanExecuteFunc { get; set; }
public Action<object> ExecuteFunc { get; set; }
public bool CanExecute(object parameter)
{
return CanExecuteFunc(parameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
ExecuteFunc(parameter);
}
}
I have made suggested changes but right now ActionCommand is not visible within collection, as only properties that belong to Card class can be bound.
EDIT:I have changed my XAML file for following but got some errors
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Deck}, Path=ActionCommand}}">
The property 'AncestorType' was not found in type 'RelativeSource'.
The property 'Path' was not found in type 'RelativeSource'.
The member "AncestorType" is not recognized or is not accessible.
The member "Path" is not recognized or is not accessible.
Unknown member 'AncestorType' on element 'RelativeSource'
Unknown member 'Path' on element 'RelativeSource'
Please help
If you want to have button which adds new items to your collection, I think something like that can be the solution.
In XAML:
<GridView ItemsSource="{Binding DeckCollection}" IsItemClickEnabled="True" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate>
<Button>
<Button.Content>
<Grid>
<Image Source="{Binding ImagePath}"
Stretch="None"/>
</Grid>
</Button.Content>
</Button>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<!-- public property located in Deck class -->
<Button Command="{Binding AddItemCommand}" Content="Add Item"/>
In C#:
class Deck, INotifyPropertyChanged /*custom implementation depends on .NET version, in my case its .NET3.5*/
{
private ObservableCollection<Card> _deckCollection = new ObservableCollection<Card>();
public ObservableCollection<Card> DeckCollection
{
get { return _deckCollection; }
set { _deckCollection = value;
OnPropertyChanged(() => DeckCollection); }
}
// your Add command
public ICommand AddItemCommand { get { return new MyCommand(AddToList); } }
private void AddToList(object parameter)
{
DeckCollection.Add(new Card());
}
public Deck() { }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged<T>(Expression<Func<T>> expression)
{
if (PropertyChanged == null) return;
var body = (MemberExpression)expression.Body;
if (body != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(body.Member.Name));
}
}
The main thing in this situation is that you cannot have the add button inside the collection.

Listbox data binding with Viewmodel not showing up windows phone

I am working on a windows phone application that uses a local database and MVVM. My listbox is not showing the WeatherList that it is binded to. I am adding a single WeatherItem to the local database in the App.xaml.cs file and it's being added successfully, but it is not showing up in the listbox when running the app. I would really appreciate any help. I have the following setup:
MainPage.xaml:
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="ForecastListItemTemplate">
<StackPanel Orientation="Horizontal" Margin="0, 400, 0, 0" Width="480" Height="100">
<StackPanel Width="190" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="{Binding Path=DayOfWeek}" FontFamily="Segoe WP" FontWeight="ExtraBold" FontSize="36" Foreground="#545d61"></TextBlock>
<TextBlock HorizontalAlignment="Center" Text="{Binding Path=ItemDay}" FontFamily="Segoe WP Light" FontSize="36" Foreground="#545d61"></TextBlock>
</StackPanel>
<Image Height="100" Width="100" Source="{Binding Path=ImageSource}"></Image>
<StackPanel Width="190" VerticalAlignment="Center">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<TextBlock Text="↑" FontFamily="Segoe WP" FontWeight="ExtraBold" FontSize="36" Foreground="#545d61"></TextBlock>
<TextBlock HorizontalAlignment="Center" Margin="4, 0, 4, 0" Text="{Binding Path=High}" FontFamily="Segoe WP" FontWeight="ExtraBold" FontSize="36" Foreground="#0da5d0"></TextBlock>
<TextBlock Text="↓" FontFamily="Segoe WP" FontWeight="ExtraBold" FontSize="36" Foreground="#545d61"></TextBlock>
<TextBlock HorizontalAlignment="Center" Margin="4, 0, 4, 0" Text="{Binding Path=Low}" FontFamily="Segoe WP" FontWeight="ExtraBold" FontSize="36" Foreground="#0da5d0"></TextBlock>
</StackPanel>
<TextBlock HorizontalAlignment="Center" Text="{Binding Path=Condition}" FontFamily="Segoe WP Light" FontSize="24" Foreground="#545d61"></TextBlock>
</StackPanel>
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="#e1e4e4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox
x:Name="WeatherListBox"
ItemsSource="{Binding WeatherList}"
Margin="0, 400, 0, 96"
Width="480"
ItemTemplate="{StaticResource ForecastListItemTemplate}">
</ListBox>
</Grid>
WeatherViewModel.cs
public class WeatherViewModel : INotifyPropertyChanged
{
// LINQ to SQL data context for the local database.
private ToDoDataContext weatherDB;
// Class constructor, create the data context object.
public WeatherViewModel()
{
weatherDB = new ToDoDataContext();
}
// All to-do items.
private ObservableCollection<WeatherItem> _weatherList;
public ObservableCollection<WeatherItem> WeatherList
{
get { return _weatherList; }
set
{
_weatherList = value;
NotifyPropertyChanged("WeatherList");
}
}
// Write changes in the data context to the database.
public void SaveChangesToDB()
{
weatherDB.SubmitChanges();
}
// Query database and load the collections and list used by the pivot pages.
public void LoadCollectionsFromDatabase()
{
// Specify the query for all to-do items in the database.
var weatherItemsInDB = from WeatherItem weather in weatherDB.Forecasts
select weather;
// Query the database and load all to-do items.
WeatherList = new ObservableCollection<WeatherItem>(weatherItemsInDB);
}
// Add a to-do item to the database and collections.
public void AddWeatherItem(WeatherItem newWeatherItem)
{
// Add a to-do item to the data context.
weatherDB.Forecasts.InsertOnSubmit(newWeatherItem);
// Save changes to the database.
weatherDB.SubmitChanges();
// Add a to-do item to the "all" observable collection.
WeatherList.Add(newWeatherItem);
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify Silverlight that a property has changed.
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
ToDoDataContext.cs
public class ToDoDataContext : DataContext
{
// Pass the connection string to the base class.
public ToDoDataContext()
: base("Data Source=isostore:/AppDB.sdf")
{ }
public ToDoCategory activeCategory;
// Specify a table for the to-do items.
public Table<ToDoItem> Items;
// Specify a table for the categories.
public Table<ToDoCategory> Categories;
//Weather stuff
public WeatherItem currentWeather;
// Specify a table for the to-do items.
public Table<WeatherItem> Forecasts;
}
[Table]
public class WeatherItem : INotifyPropertyChanged, INotifyPropertyChanging
{
// Define ID: private field, public property, and database column.
private int _weatherItemId;
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int WeatherItemId
{
get { return _weatherItemId; }
set
{
if (_weatherItemId != value)
{
NotifyPropertyChanging("WeatherItemId");
_weatherItemId = value;
NotifyPropertyChanged("WeatherItemId");
}
}
}
// Define item name: private field, public property, and database column.
private string _itemDay;
[Column]
public string ItemDay
{
get { return _itemDay; }
set
{
if (_itemDay != value)
{
NotifyPropertyChanging("ItemDay");
_itemDay = value;
NotifyPropertyChanged("ItemDay");
}
}
}
private string _dayOfWeek;
[Column]
public string DayOfWeek
{
get { return _dayOfWeek; }
set
{
if (_dayOfWeek != value)
{
NotifyPropertyChanging("DayOfWeek");
_dayOfWeek = value;
NotifyPropertyChanged("DayOfWeek");
}
}
}
// Define completion value: private field, public property, and database column.
private string _high;
[Column]
public string High
{
get { return _high; }
set
{
if (_high != value)
{
NotifyPropertyChanging("High");
_high = value;
NotifyPropertyChanged("High");
}
}
}
private string _low;
[Column]
public string Low
{
get { return _low; }
set
{
if (_low != value)
{
NotifyPropertyChanging("Low");
_low = value;
NotifyPropertyChanged("Low");
}
}
}
private string _condition;
[Column]
public string Condition
{
get { return _condition; }
set
{
if (_condition != value)
{
NotifyPropertyChanging("Condition");
_condition = value;
NotifyPropertyChanged("Condition");
}
}
}
private string _imageSource;
[Column]
public string ImageSource
{
get { return _imageSource; }
set
{
if (_imageSource != value)
{
NotifyPropertyChanging("ImageSource");
_imageSource = value;
NotifyPropertyChanged("ImageSource");
}
}
}
// Version column aids update performance.
[Column(IsVersion = true)]
private Binary _version;
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify that a property changed
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
// Used to notify that a property is about to change
private void NotifyPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
#endregion
}
Mainpage.xaml.cs
// Constructor
public MainPage()
{
InitializeComponent();
this.DataContext = App.WeatherViewModel;
}
App.xaml.cs
public partial class App : Application
{
private static ToDoViewModel viewModel;
public static ToDoViewModel ViewModel
{
get { return viewModel; }
}
private static WeatherViewModel weatherViewModel;
public static WeatherViewModel WeatherViewModel
{
get { return weatherViewModel; }
}
/// <summary>
/// Provides easy access to the root frame of the Phone Application.
/// </summary>
/// <returns>The root frame of the Phone Application.</returns>
public PhoneApplicationFrame RootFrame { get; private set; }
/// <summary>
/// Constructor for the Application object.
/// </summary>
public App()
{
// Global handler for uncaught exceptions.
UnhandledException += Application_UnhandledException;
// Standard Silverlight initialization
InitializeComponent();
// Phone-specific initialization
InitializePhoneApplication();
// Show graphics profiling information while debugging.
if (System.Diagnostics.Debugger.IsAttached)
{
// Display the current frame rate counters.
Application.Current.Host.Settings.EnableFrameRateCounter = true;
// Show the areas of the app that are being redrawn in each frame.
//Application.Current.Host.Settings.EnableRedrawRegions = true;
// Enable non-production analysis visualization mode,
// which shows areas of a page that are handed off to GPU with a colored overlay.
//Application.Current.Host.Settings.EnableCacheVisualization = true;
// Disable the application idle detection by setting the UserIdleDetectionMode property of the
// application's PhoneApplicationService object to Disabled.
// Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
// and consume battery power when the user is not using the phone.
PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
}
// Create the database if it does not exist.
using (ToDoDataContext db = new ToDoDataContext())
{
if (db.DatabaseExists() == false)
{
// Create the local database.
db.CreateDatabase();
// Prepopulate the categories.
db.Categories.InsertOnSubmit(new ToDoCategory { Name = "Home" });
db.Categories.InsertOnSubmit(new ToDoCategory { Name = "Work" });
db.Categories.InsertOnSubmit(new ToDoCategory { Name = "School" });
db.Forecasts.InsertOnSubmit(new WeatherItem
{
DayOfWeek = "Mon",
ItemDay = "Sept 4",
Condition = "Mostly Sunny",
High = "105",
Low = "88",
ImageSource = "images/Weather/45.png"
});
// Save categories to the database.
db.SubmitChanges();
}
}
// Create the ViewModel object.
viewModel = new ToDoViewModel();
weatherViewModel = new WeatherViewModel();
// Query the local database and load observable collections.
viewModel.LoadCollectionsFromDatabase();
weatherViewModel.LoadCollectionsFromDatabase();
}
Thanks!
Did you check out the output window for data binding errors?
And in the ctor of the main page, is App.WeatherViewModel already not-null?
You add
WeatherListBox.DataContext = WeatherList;
And you modify binding with
ItemsSource="{Binding}"

Categories