I coding WPF App with Mahapps.Metro.
At that time, I’m cording a process that will display a dialog asking the user to select a directory and then display that directory in a Textbox.
Wrote the code shown below and got it to work, but the selected directory was not display in the Textbox.
xaml source:
<mah:MetroWindow x:Class="LogAnalysisProto_2.ToolLancher"
...
d:DataContext="{d:DesignInstance {x:Type local:ToolLancherViewModel},IsDesignTimeCreatable=True}"
mc:Ignorable="d"
Title="Sample" Height="610" Width="970"
>
<Grid>
<StackPanel Orientation="Horizontal">
<Label Style="{DynamicResource CommonLabel}" Content="Target Folder"/>
<Button x:Name="targetFolderOpen" Style="{DynamicResource MahApps.Styles.Button.Flat}" HorizontalAlignment="Center" Width="100" Margin="50,0,0,0" BorderBrush="{x:Null}" Background="{x:Null}" Click="targetFolderButton_Click">
<iconPacks:PackIconModern Width="30" Height="20" Kind="Folder" VerticalAlignment="Center"/>
</Button>
<TextBox x:Name="targetFolderTextBox" Text="{Binding TargetFolderPath}" Style="{DynamicResource MahApps.Styles.TextBox}" Height="32" Width="660" VerticalAlignment="Center"></TextBox>
</StackPanel>
</Grid>
</mah:MetroWindow>
code behind:
public partial class ToolLancher : MetroWindow
{
public ToolLancher()
{
DataContext = new ToolLancherViewModel();
InitializeComponent();
}
private void targetFolderButton_Click(object sender, RoutedEventArgs e)
{
var vm = DataContext as ToolLancherViewModel;
vm.PickTargetFolder();
//targetFolderTextBox.Text = vm.TargetFolderPath;
}
}
In ViewModel source(in ToolLancherViewModel):
public class ToolLancherViewModel : BaseViewModel
{
...
private string m_TargetFolderPath { get; set; }
/// <summary>
/// TargetFolder Property
/// </summary>
public string TargetFolderPath
{
get { return m_TargetFolderPath; }
set
{
m_TargetFolderPath = value;
IsExistsTargetDirectory = Directory.Exists(value);
NotifyPropertyChanged(nameof(TargetFolderPath));
}
}
/// <summary>
/// Select dialog path
/// </summary>
public void PickTargetFolder()
{
var path = PickFolder("TargetFolderBrowse");
if (string.IsNullOrEmpty(path)) return;
TargetFolderPath = path;
IsExistsTargetDirectory = true;
NotifyPropertyChanged(nameof(TargetFolderPath));
NotifyPropertyChanged(nameof(IsExistsTargetDirectory));
}
public static string PickFolder(string dialogTitle = "")
{
using (var folderPicker = new CommonOpenFileDialog()
{
Title = dialogTitle == string.Empty ? "SelectFolder" : dialogTitle,
InitialDirectory = initialDirectory,
IsFolderPicker = true,
})
{
if (folderPicker.ShowDialog() != CommonFileDialogResult.Ok)
{
return string.Empty;
}
return folderPicker.FileName;
}
}
}
BaseViewModel
public class BaseViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
var e = new PropertyChangedEventArgs(propertyName);
PropertyChanged?.Invoke(this, e);
}
}
Selecting Directory
After the Directory Select
There is no error.
Also, the binding method is already coded by me in another source, which is similarly written and works as expected.
In addition, the directory will appear in the TextBox without problems when specified in the following way.
private void targetFolderButton_Click(object sender, RoutedEventArgs e)
{
var vm = DataContext as ToolLancherViewModel;
vm.PickTargetFolder();
// Uncomment out this line
targetFolderTextBox.Text = vm.TargetFolderPath;
}
Correctly displayed example
Related
I'm facing a problem where I can't reload a serialiazed object (using NewtonsoftJson) into cascading comboboxes. I'm also using the Prism MVVM lib.
While at first start my application is working as expected:
so I am able to select values from the second combobox based on the first combobox, but when I save the model and reload it I've two main issues:
The SelectedItem property never get set (even the debugger shows that it's not null)
The second combobox stays empty eventhough the values seems to be loaded, that looks like:
What am I doing wrong here? Also, I don't like the ComboboxSelectionChanged approach, so maybe someone can point me to the MVVM based approach.
Here is the minimal working example:
MainWindow.xaml.cs
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ViewModel viewModel;
public MainWindow()
{
InitializeComponent();
viewModel = new ViewModel();
ConstructRandomData();
DataContext = viewModel;
}
private void ConstructRandomData()
{
// Construct data
for (int i = 0; i < 5; i++)
{
var ids = new List<SubId>();
for (int r = 0; r < 10; r++)
{
ids.Add(
new SubId
{
Name = $"Id_{i}.{r}"
}
);
}
viewModel.MainIds.Add(
new MainId
{
Name = $"MainId{i}",
SubIds = ids
});
}
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox combo = sender as ComboBox;
if (combo.SelectedItem is MainId selectedItem)
{
var subIdList = (from mainId in viewModel.MainIds
where mainId.Name.Equals(selectedItem.Name)
select mainId.SubIds).First();
viewModel.SubIds.Clear();
viewModel.SubIds.AddRange(subIdList.ToArray());
}
}
private void SaveButtton_Click(object sender, RoutedEventArgs e)
{
File.WriteAllText("savedData.json", JsonConvert.SerializeObject(viewModel));
}
private void LoadButton_Click(object sender, RoutedEventArgs e)
{
ViewModel deserializedModel = JsonConvert.DeserializeObject<ViewModel>(File.ReadAllText("savedData.json"));
viewModel.MainIds = deserializedModel.MainIds;
viewModel.SubIds = deserializedModel.SubIds;
}
}
public class ViewModel : BindableBase
{
public ObservableCollection<MainId> MainIds { get; set; } = new ObservableCollection<MainId>();
public ObservableCollection<SubId> SubIds { get; set; } = new ObservableCollection<SubId>();
private MainId mainId;
public MainId SelectedMainId
{
get { return mainId; }
set { SetProperty(ref mainId, value); }
}
private SubId selectedId;
public SubId SelectedId
{
get { return selectedId; }
set { SetProperty(ref selectedId, value); }
}
}
public class MainId : BindableBase
{
private string name;
public string Name
{
get { return name; }
set
{
SetProperty(ref name, value);
}
}
public List<SubId> SubIds { get; set; } = new List<SubId>();
}
public class SubId : BindableBase
{
private string name;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
}
The MainWindow.xaml
<Window x:Class="CascadingComboBox.MainWindow"
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:CascadingComboBox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Margin="30">
<ComboBox Margin="5" Width="150"
ItemsSource="{Binding MainIds}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedMainId}"
SelectionChanged="ComboBox_SelectionChanged"/>
<ComboBox Margin="5" Width="150"
ItemsSource="{Binding SubIds}"
SelectedItem="{Binding SelectedId}"
DisplayMemberPath="Name"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
<Button Margin="5" Width="50" Content="Save" Click="SaveButtton_Click" />
<Button Margin="5" Width="50" Content="Load" Click="LoadButton_Click"/>
</StackPanel>
</StackPanel>
</Window>
During serialization, SelectedItem contains an object from ComboBoxItems collection.
But after deserialization this is no longer true: now SelectedItem is a new instance, even though it has same content as one of the items in ComboBoxItems. This is how Json.NET works by default.
You can fix that by changing PreserveReferencesHandling option like this:
var jsonSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
JsonConvert.SerializeObject(model, Formatting.Indented, jsonSettings);
...
model = JsonConvert.DeserializeObject<List<Person>>(json, jsonSettings);
https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_PreserveReferencesHandling.htm
I have a WPF of tasks each has status of completed or pending, I will view all of these items into a list box, the tasks are serialized into XML file and these items are linked to UI through ObersvableCollection.
What I'm looking for it so be able to filter the tasks on the view and be able to edit or create new taks and save into the XML file.
I was thinking about creating a new class called FilterTask.cs and have static method Completed() which return ObersvableCollection item but I am not sure if this will affect the serialization process.
here is my code
main window XAML
<Window x:Class="UIToDoList_2.MainWindow"
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:UIToDoList_2"
mc:Ignorable="d"
Title="To Do List" Height="290" Width="500" ResizeMode="CanMinimize">
<Grid Margin="0,0,0,0">
<StackPanel Name="MainPanel">
<StackPanel Name="ViewTaskPanel" Margin="10">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Task" Margin="0,0,10,0"/>
<StackPanel Orientation="Vertical">
<ListBox x:Name="UITasks" Width="300" Height="100" VerticalAlignment="Top" Margin="0,0,10,5"
DisplayMemberPath="Name"/>
<RadioButton x:Name="ShowAll" Content="Show All"/>
<RadioButton x:Name="ShowCompleted" Content="Show Completed" />
<RadioButton x:Name="ShowPending" Content="Show Pending"/>
</StackPanel>
<StackPanel>
<Button Content="Mark As Completed" Margin="0,0,0,5"/>
<Button Content="Mark As Pending" Margin="0,0,0,5"/>
<Button x:Name="DltTaskBtn" Content="Delete Task"
Margin="0,0,0,5" Click="DltTaskBtn_Click"/>
<Button x:Name="RenameBtn" Content="Rename" Click="RenameBtn_Click"/>
</StackPanel>
</StackPanel>
</StackPanel>
<StackPanel Name="AddTaskPanel" Margin="10">
<TextBlock Text="Add New Task"/>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="Task Name" Margin="0,0,10,0"/>
<TextBox Name="TaskNameTxt" Width="260"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="AddTaskBtn" Content="Add" Margin="5" Click="AddTaskBtn_Click"/>
<Button x:Name="ClearTaskTxt" Content="Clear" Margin="5" Click="ClearTaskTxt_Click"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Grid>
</Window>
Task.cs
public class Task : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return this._Name; }
set
{
if (this._Name != value)
{
this._Name = value;
this.NotifyPropertyChanged();
}
}
}
private Status _Status;
public Status Status
{
get { return this._Status; }
set
{
if (this._Status != value)
{
this._Status = value;
this.NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propName = "")
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public enum Status
{
Pending = 0,
Completed = 1
}
TaskSerialization.cs
public class TaskSerialization
{
public string FileName { get; set; }
public TaskSerialization(string fileName)
{
FileName = fileName;
}
/// <summary>
/// Serialize the task list to XML File
/// </summary>
/// <param name="tasks"></param>
public void SerializeTasks(List<Task> tasks)
{
StreamWriter sw = new StreamWriter(FileName);
XmlSerializer sr = new XmlSerializer(typeof(List<Task>));
sr.Serialize(sw, tasks);
sw.Close();
}
/// <summary>
/// Deserialize the taks list form XML file and return as ObservableCollection item
/// </summary>
/// <returns></returns>
public ObservableCollection<Task> DeserializeTasks()
{
XmlSerializer sr = new XmlSerializer(typeof(List<Task>));
FileStream fs = new FileStream(FileName, FileMode.Open);
return new ObservableCollection<Task>((List<Task>)sr.Deserialize(fs));
}
}
Mainwindow.cs
namespace UIToDoList_2
{
public partial class MainWindow : Window
{
private readonly string fileName = "ToDoList.xml";
private readonly TaskSerialization taskSrlz;
ObservableCollection<Task> tasks = new ObservableCollection<Task>();
public MainWindow()
{
InitializeComponent();
taskSrlz = new TaskSerialization(fileName);
tasks = taskSrlz.DeserializeTasks();
UITasks.ItemsSource = tasks;
}
private void AddTaskBtn_Click(object sender, RoutedEventArgs e)
{
AddTask();
}
private void DltTaskBtn_Click(object sender, RoutedEventArgs e)
{
DeleteTask();
}
private void AddTask()
{
var taskName = TaskNameTxt.Text;
if (!string.IsNullOrWhiteSpace(taskName))
{
tasks.Add(new Task() { Name = TaskNameTxt.Text, Status = Status.Pending });
taskSrlz.SerializeTasks(new List<Task>(tasks));
}
else
MessageBox.Show("Task name cannot be null", "Error in Task name", MessageBoxButton.OK, MessageBoxImage.Error);
}
private void DeleteTask()
{
if(UITasks.SelectedItem != null)
{
var selectedTask = (UITasks.SelectedItem as Task);
tasks.Remove(selectedTask);
taskSrlz.SerializeTasks(new List<Task>(tasks));
}
}
private void RenameBtn_Click(object sender, RoutedEventArgs e)
{
if (UITasks.SelectedItem != null)
{
(UITasks.SelectedItem as Task).Name = TaskNameTxt.Text;
taskSrlz.SerializeTasks(new List<Task>(tasks));
}
}
private void ClearTaskTxt_Click(object sender, RoutedEventArgs e)
{
TaskNameTxt.Clear();
}
}
}
I will mark this as answer based on clemens comment to look into binding into collections https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/data-binding-overview#binding-to-collections
I have text block on my UI. I would like to display some text on the text block dynamically. I have implemented it as given in the below code. however i do not see the values updating dynamically. I do see only the last updated value on UI text block. I have included a delay to notice the change.
Please provide any solution or comment for more info.Thank you in advance.
Code:
namespace TxtBlock
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
SomeObjectClass obj = new SomeObjectClass();
public MainWindow()
{
InitializeComponent();
txtName.DataContext = obj;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
obj.Name = "Hello World";
Thread.Sleep(2000);
obj.Name = "Third";
}
}
class SomeObjectClass : INotifyPropertyChanged
{
private string _name = "hello";
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
XAML: <Window x:Class="TxtBlock.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="237,170,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<TextBlock HorizontalAlignment="Left" Margin="237,256,0,0" TextWrapping="Wrap" x:Name="txtName" Text="{Binding Name}" VerticalAlignment="Top"/>
</Grid>
</Window>
You need to Run in Background thread to update your values in UI TextBlock
Code:
public partial class TextBlockExample : Window
{
ThreadExampleViewModel viewModel = new ThreadExampleViewModel();
public TextBlockExample()
{
InitializeComponent();
this.DataContext = viewModel;
}
private void btnClick_Click(object sender, RoutedEventArgs e)
{
/// Background thread Thread to run your logic
Thread thread = new Thread(YourLogicToUpdateTextBlock);
thread.IsBackground = true;
thread.Start();
}
private void YourLogicToUpdateTextBlock()
{
/// Example i am updating with i value.
for (int i = 0; i < 1000; i++)
{
viewModel.Name = i + " Conut";
Thread.Sleep(1000);
}
}
}
<Grid>
<StackPanel>
<TextBlock x:Name="txtName" Text="{Binding Name}" Height="30" Width="100" Margin="10"/>
<Button x:Name="btnClick" Content="Click" Height="30" Width="100" Margin="10" Click="btnClick_Click"/>
</StackPanel>
</Grid>
public class ThreadExampleViewModel : INotifyPropertyChanged
{
private string name = "Hello";
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
I am creating a to do list application. At the moment I want to add a new to do list from todolistPage.xaml and after adding, I want to take the data to be able to view in the MainPage.xaml . I am able to view it from the todolistPage but not sure how to bring it to to another page. Hope to have some help. Thanks.
Below are my codes
MainPage.xaml.cs
namespace PivotApp3
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
DataContext = App.ViewModel;
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
// Load data for the ViewModel Items
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var si = mLongListSelector.SelectedItem as PivotApp3.ViewModels.ItemViewModel;
if (mLongListSelector.SelectedItem == null)
return;
if (si.LineOne.Equals("+ To Do List"))
NavigationService.Navigate(new Uri("/todolistPage.xaml", UriKind.Relative));
else if (si.LineOne.Equals("+ Reminder"))
NavigationService.Navigate(new Uri("/reminderPage.xaml", UriKind.Relative));
// Reset selected item to null (no selection)
mLongListSelector.SelectedItem = null;
}
}
MainPage.xaml
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<!-- LOCALIZATION NOTE:
To localize the displayed strings copy their values to appropriately named
keys in the app's neutral language resource file (AppResources.resx) then
replace the hard-coded text value between the attributes' quotation marks
with the binding clause whose path points to that string name.
For example:
Text="{Binding Path=LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}"
This binding points to the template's string resource named "ApplicationTitle".
Adding supported languages in the Project Properties tab will create a
new resx file per language that can carry the translated values of your
UI strings. The binding in these examples will cause the value of the
attributes to be drawn from the .resx file that matches the
CurrentUICulture of the app at run time.
-->
<!--Pivot Control-->
<phone:Pivot Title="DAILY ROUTINE">
<!--Pivot item one-->
<phone:PivotItem Header="activity">
<!--Double line list with text wrapping-->
<phone:LongListSelector x:Name="mLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="LongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</phone:PivotItem>
<!--Pivot item two-->
<phone:PivotItem Header="today">
</phone:PivotItem>
</phone:Pivot>
<!--Uncomment to see an alignment grid to help ensure your controls are
aligned on common boundaries. The image has a top margin of -32px to
account for the System Tray. Set this to 0 (or remove the margin altogether)
if the System Tray is hidden.
Before shipping remove this XAML and the image itself.-->
<!--<Image Source="/Assets/AlignmentGrid.png" VerticalAlignment="Top" Height="800" Width="480" Margin="0,-32,0,0" Grid.Row="0" IsHitTestVisible="False" />-->
</Grid>
todolistPage.xaml.cs
namespace PivotApp3
{
public partial class todolistPage : PhoneApplicationPage, INotifyPropertyChanged
{
// Data context for the local database
private ToDoDataContext toDoDB;
// Define an observable collection property that controls can bind to.
private ObservableCollection<ToDoItem> _toDoItems;
public ObservableCollection<ToDoItem> ToDoItems
{
get
{
return _toDoItems;
}
set
{
if (_toDoItems != value)
{
_toDoItems = value;
NotifyPropertyChanged("ToDoItems");
}
}
}
//constructor
public todolistPage()
{
InitializeComponent();
// Connect to the database and instantiate data context.
toDoDB = new ToDoDataContext(ToDoDataContext.DBConnectionString);
// Data context and observable collection are children of the main page.
this.DataContext = this;
}
private void deleteTaskButton_Click(object sender, RoutedEventArgs e)
{
// Cast parameter as a button.
var button = sender as Button;
if (button != null)
{
// Get a handle for the to-do item bound to the button.
ToDoItem toDoForDelete = button.DataContext as ToDoItem;
// Remove the to-do item from the observable collection.
ToDoItems.Remove(toDoForDelete);
// Remove the to-do item from the local database.
toDoDB.ToDoItems.DeleteOnSubmit(toDoForDelete);
// Save changes to the database.
toDoDB.SubmitChanges();
// Put the focus back to the main page.
this.Focus();
}
}
private void newToDoTextBox_GotFocus(object sender, RoutedEventArgs e)
{
// Clear the text box when it gets focus.
newToDoTextBox.Text = String.Empty;
}
private void newToDoAddButton_Click(object sender, RoutedEventArgs e)
{
// Create a new to-do item based on the text box.
ToDoItem newToDo = new ToDoItem { ItemName = newToDoTextBox.Text };
// Add a to-do item to the observable collection.
ToDoItems.Add(newToDo);
// Add a to-do item to the local database.
toDoDB.ToDoItems.InsertOnSubmit(newToDo);
}
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
// Call the base method.
base.OnNavigatedFrom(e);
// Save changes to the database.
toDoDB.SubmitChanges();
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// Define the query to gather all of the to-do items.
var toDoItemsInDB = from ToDoItem todo in toDoDB.ToDoItems
select todo;
// Execute the query and place the results into a collection.
ToDoItems = new ObservableCollection<ToDoItem>(toDoItemsInDB);
// Call the base method.
base.OnNavigatedTo(e);
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify the app that a property has changed.
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public class ToDoDataContext : DataContext
{
// Specify the connection string as a static, used in main page and app.xaml.
public static string DBConnectionString = "Data Source=isostore:/ToDo.sdf";
// Pass the connection string to the base class.
public ToDoDataContext(string connectionString)
: base(connectionString)
{ }
// Specify a single table for the to-do items.
public Table<ToDoItem> ToDoItems;
}
[Table]
public class ToDoItem : INotifyPropertyChanged, INotifyPropertyChanging
{
// Define ID: private field, public property and database column.
private int _toDoItemId;
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int ToDoItemId
{
get
{
return _toDoItemId;
}
set
{
if (_toDoItemId != value)
{
NotifyPropertyChanging("ToDoItemId");
_toDoItemId = value;
NotifyPropertyChanged("ToDoItemId");
}
}
}
// Define item name: private field, public property and database column.
private string _itemName;
[Column]
public string ItemName
{
get
{
return _itemName;
}
set
{
if (_itemName != value)
{
NotifyPropertyChanging("ItemName");
_itemName = value;
NotifyPropertyChanged("ItemName");
}
}
}
// Define completion value: private field, public property and database column.
private bool _isComplete;
[Column]
public bool IsComplete
{
get
{
return _isComplete;
}
set
{
if (_isComplete != value)
{
NotifyPropertyChanging("IsComplete");
_isComplete = value;
NotifyPropertyChanged("IsComplete");
}
}
}
// Version column aids update performance.
[Column(IsVersion = true)]
private Binary _version;
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify the page that a data context 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 the data context that a data context property is about to change
private void NotifyPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
#endregion
}
todolistPage.xaml
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="TO DO LIST" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="add" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!-- Bind the list box to the observable collection. -->
<ListBox x:Name="toDoItemsListBox" ItemsSource="{Binding ToDoItems}"
Grid.Row="1" Margin="12,0,28,210" Width="440">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Width="440">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<CheckBox
IsChecked="{Binding IsComplete, Mode=TwoWay}"
Grid.Column="0"
VerticalAlignment="Center"/>
<TextBlock
Text="{Binding ItemName}"
FontSize="{StaticResource PhoneFontSizeLarge}"
Grid.Column="1"
VerticalAlignment="Center"/>
<Button
Grid.Column="2"
x:Name="deleteTaskButton"
BorderThickness="0"
Margin="0"
Click="deleteTaskButton_Click">
<Image Source="appbar.delete.rest.png"/>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="2" Margin="12,465,12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*"/>
<ColumnDefinition Width="9*"/>
</Grid.ColumnDefinitions>
<TextBox
x:Name="newToDoTextBox"
Grid.Column="0"
Text="add new task"
FontFamily="{StaticResource PhoneFontFamilyLight}"
GotFocus="newToDoTextBox_GotFocus" Margin="0,-65,0,104" Grid.ColumnSpan="2"/>
<Button
Content="add"
x:Name="newToDoAddButton"
Click="newToDoAddButton_Click" Margin="150,43,130,10" Grid.ColumnSpan="2"/>
</Grid>
</Grid>
Database Created
using (ToDoDataContext db = new ToDoDataContext(ToDoDataContext.DBConnectionString))
{
if (db.DatabaseExists() == false)
{
//Create the database
db.CreateDatabase();
}
}
Put your database into App.xaml.cs:
// Data context for the local database
public ToDoDataContext toDoDB;
add this code to App.xaml.cs:
public new static App Current
{
get
{
return (App)Application.Current;
}
}
Then you can access your database everywhere by using:
App.Current.toDoDB...
You can create class to manipulate data in sql and make it avaiable from App.xaml.cs
Here is sample code
ToDoDataViewModel class:
public class ToDoDataViewModel : INotifyPropertyChanged
{
ToDoDataContext db;
public ToDoDataViewModel(string connectionString)
{
db = new ToDoDataContext(connectionString);
}
private ObservableCollection<ToDoItem> _toDoItems;
public ObservableCollection<ToDoItem> ToDoItems
{
get { return this._toDoItems; }
set
{
this._toDoItems = value;
NotifyPropertyChanged("ToDoItems");
}
}
public void LoadCollectionsFromDatabase()
{
var toDos = from todo in db.ToDoItems
select todo;
_toDoItems = new ObservableCollection<ToDoItem>(toDos);
}
public void InsertToDoItem(ToDoItem item)
{
db.ToDoItems.InsertOnSubmit(item);
_toDoItems.Add(item);
db.SubmitChanges();
}
public void DeleteToDoItem(ToDoItem item)
{
db.ToDoItems.DeleteOnSubmit(item);
_toDoItems.Remove(item);
db.SubmitChanges();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In App.xaml.cs:
public partial class App : Application
{
private static ToDoDataViewModel _viewModel;
public static ToDoDataViewModel ViewModel
{
get { return _viewModel; }
}
//other methods of App
public App()
{
//place this code at the and of the contructor
CreateDb();
}
private void CreateDb()
{
using(var db=new ToDoDataContext(ToDoDataContext.DBConnectionString))
{
if(!db.DatabaseExists())
{
db.CreateDatabase();
}
}
_viewModel=new ToDoDataViewModel(ToDoDataContext.DBConnectionString);
_viewModel.LoadCollectionsFromDatabase();
}
}
And place this code in the constructors of your pages:
this.DataContext=App.ViewModel;
This way you separated your database logic from application logic
Now you can update your newToDoAddButton_Click method as following:
private void newToDoAddButton_Click(object sender, RoutedEventArgs e)
{
// Create a new to-do item based on the text box.
ToDoItem newToDo = new ToDoItem { ItemName = newToDoTextBox.Text };
//Add to-do item to the local database
App.ViewModel.InsertToDoItem(newToDo);
this.Focus();
}
And your deleteTaskButton_Click method:
private void deleteTaskButton_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button != null)
{
// Get a handle for the to-do item bound to the button.
ToDoItem toDoForDelete = button.DataContext as ToDoItem;
// Remove the to-do item from the local database.
App.ViewModel.DeleteToDoItem(toDoForDelete);
this.Focus();
}
}
I would like to navigate to another page from the specific list but at the moment no matter what list i click, it will still go to the same page. What should I do. I am still new here and to learn more. Thanks.
Below are the codes.
MainPage.xaml
<!--Pivot Control-->
<phone:Pivot Title="DAILY ROUTINE">
<!--Pivot item one-->
<phone:PivotItem Header="activity">
<!--Double line list with text wrapping-->
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="LongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</phone:PivotItem>
<!--Pivot item two-->
<phone:PivotItem Header="today">
</phone:PivotItem>
</phone:Pivot>
MainPage.xaml.cs
namespace PivotApp3
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
DataContext = App.ViewModel;
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
// Load data for the ViewModel Items
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
NavigationService.Navigate(new Uri("/todolistPage.xaml", UriKind.Relative));
}
}
}
todolistPage.xaml
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="TO DO LIST" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="add" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
</Grid>
</Grid>
MainViewModel.cs
namespace PivotApp3.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Items = new ObservableCollection<ItemViewModel>();
}
/// <summary>
/// A collection for ItemViewModel objects.
/// </summary>
public ObservableCollection<ItemViewModel> Items { get; private set; }
private string _sampleProperty = "Sample Runtime Property Value";
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding
/// </summary>
/// <returns></returns>
public string SampleProperty
{
get
{
return _sampleProperty;
}
set
{
if (value != _sampleProperty)
{
_sampleProperty = value;
NotifyPropertyChanged("SampleProperty");
}
}
}
/// <summary>
/// Sample property that returns a localized string
/// </summary>
public string LocalizedSampleProperty
{
get
{
return AppResources.SampleProperty;
}
}
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Creates and adds a few ItemViewModel objects into the Items collection.
/// </summary>
public void LoadData()
{
// Sample data; replace with real data
this.Items.Add(new ItemViewModel() { LineOne = "+ To Do List" });
this.Items.Add(new ItemViewModel() { LineOne = "+ Reminder" });
this.Items.Add(new ItemViewModel() { LineOne = "+ Expenses" });
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
ItemViewModel.cs
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace PivotApp3.ViewModels
{
public class ItemViewModel : INotifyPropertyChanged
{
private string _lineOne;
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding.
/// </summary>
/// <returns></returns>
public string LineOne
{
get
{
return _lineOne;
}
set
{
if (value != _lineOne)
{
_lineOne = value;
NotifyPropertyChanged("LineOne");
}
}
}
private string _lineTwo;
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding.
/// </summary>
/// <returns></returns>
public string LineTwo
{
get
{
return _lineTwo;
}
set
{
if (value != _lineTwo)
{
_lineTwo = value;
NotifyPropertyChanged("LineTwo");
}
}
}
private string _lineThree;
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding.
/// </summary>
/// <returns></returns>
public string LineThree
{
get
{
return _lineThree;
}
set
{
if (value != _lineThree)
{
_lineThree = value;
NotifyPropertyChanged("LineThree");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
The most trivial solution is:
private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var si = MainLongListSelector.SelectedItem as PivotApp3.ViewModels.ItemViewModel;
if(si.LineOne.Equals("+ To Do List"))
NavigationService.Navigate(new Uri("/todolistPage.xaml", UriKind.Relative));
else if(si.LineOne.Equals("another"))
NavigationService.Navigate(new Uri("/another.xaml", UriKind.Relative));
}
You don't have to use ItemViewModel to bind some data to the list. If you will use your own class - than you can place URI "behind" your item and use it. (It will be better solution).
Example:
public void LoadData()
{
// Sample data; replace with real data
this.Items.Add(new ItemViewModel() { LineOne = "+ To Do List", GotoUri = new Uri("/todolistPage.xaml", UriKind.Relative) });
this.Items.Add(new ItemViewModel() { LineOne = "+ Reminder", GotoUri = new Uri("/other.xaml", UriKind.Relative) });
this.Items.Add(new ItemViewModel() { LineOne = "+ Expenses", GotoUri = new Uri("/other2.xaml", UriKind.Relative) });
this.IsDataLoaded = true;
}
Then:
public class ItemViewModel : INotifyPropertyChanged
{
public Uri GotoUri {get; set;}
//...
}
private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var si = MainLongListSelector.SelectedItem as PivotApp3.ViewModels.ItemViewModel;
if(si != null)
NavigationService.Navigate(si.GotoUri, UriKind.Relative));
}