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
Related
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
Problem
I want to refresh my wpf view when a change is made in a List of objects in my application, but it wont register the INotifyChanged method when I change a value.
What I've tried
I went to multiple different stackoverflow pages with sort of the same problem but I don't get it working right. It wont register a change in a object in the list.
my code
below is the code for the MainWindow of the WPF application in wher with the last button click I change the value of XLocation in an object out of a list.
public partial class MainWindow : Window
{
private string filePathArtist { get; set; }
private string filePathGrid { get; set; }
public Game.Game Game { get; set; }
public MainWindow()
{
InitializeComponent();
filePathGrid = String.Empty;
filePathArtist = String.Empty;
}
private void BtnOpen_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
bool? res = openFileDialog.ShowDialog();
if (res == true)
{
string filepathgrid = openFileDialog.FileName;
filePathGrid = filepathgrid;
GridTextBox.Text = filepathgrid;
}
}
private void PickArtistBtn_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
bool? res = openFileDialog.ShowDialog();
if (res == true)
{
string filepathartist = openFileDialog.FileName;
filePathArtist = filepathartist;
ArtistTextBox.Text = filepathartist;
}
}
private void CreateGridBtn_Click(object sender, RoutedEventArgs e)
{
Game = new Game.Game(filePathGrid, filePathArtist);
this.DataContext = Game;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Game.Artists[0].XLocation = 30;
}
}
Next code is the Game class where is implemented a INotfyPropertyChanged on the list of Artists.
public class Game : INotifyPropertyChanged
{
public List<Artist> _artists;
public List<Artist> Artists
{
get
{
return _artists;
}
set
{
_artists = value;
OnPropertyChanged("Artists");
}
}
public List<ITile> Tiles { get; set; }
public Game()
{
}
public Game(string graphPath, string artistPath)
{
IDataParser graphParser = DataFactory.DataFactory.Instance.CreateParser(graphPath);
IDataParser artistParser = DataFactory.DataFactory.Instance.CreateParser(artistPath);
Tiles = graphParser.ParseGridData(graphPath);
Artists = artistParser.ParseArtistData(artistPath);
Test = "new Game";
}
public string Test { get; set; } = "t";
public event PropertyChangedEventHandler? PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Ive also added the INotifyPropertyChanged in the Artists class
public class Artist : INotifyPropertyChanged
{
private float _xLocation;
private float _yLocation;
private int _xVelocity;
private int _yVelocity;
public float XLocation
{
get => _xLocation;
set
{
_xLocation = value;
OnPropertyChanged("XLocation");
}
}
public float ConvertedXLoc
{
get => XLocation * (float)3.75;
set { }
}
public float YLocation
{
get => _yLocation;
set
{
_yLocation = value;
OnPropertyChanged("YLocation");
}
}
public float ConvertedYLoc
{
get => YLocation * (float)3.75;
set { }
}
public int XVelocity
{
get => _xVelocity;
set
{
_xVelocity = value;
}
}
public int YVelocity
{
get => _yVelocity;
set
{
_yVelocity = value;
}
}
public event PropertyChangedEventHandler? PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then here is the Xaml code where I bind the objects to the wpf UI.
<Window x:Class="BroadwayBoogie.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:game="clr-namespace:BroadwayBoogie.Game"
mc:Ignorable="d"
Title="MainWindow" Height="900" Width="900"
>
<Window.DataContext>
<game:Game/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="201*"/>
<ColumnDefinition Width="199*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="21*"/>
<RowDefinition Height="401*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Button x:Name="BtnOpen" Content="Pick Grid" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Click="BtnOpen_Click"/>
<TextBox x:Name="GridTextBox" HorizontalAlignment="Center" TextWrapping="NoWrap" VerticalAlignment="Center" Width="266" />
<Button x:Name="PickArtistBtn" Content="Pick Artist" HorizontalAlignment="Left" Margin="356,0,0,0" VerticalAlignment="Center" Click="PickArtistBtn_Click" RenderTransformOrigin="-0.135,0.647"/>
<TextBox x:Name="ArtistTextBox" HorizontalAlignment="Left" Margin="30,14,0,0" TextWrapping="NoWrap" VerticalAlignment="Top" Width="231" Grid.Column="1"/>
<Button x:Name="CreateGridBtn" Grid.Column="1" Content="Create Grid" HorizontalAlignment="Left" Margin="311,14,0,0" VerticalAlignment="Top" Click="CreateGridBtn_Click"/>
<Canvas Width="800" Height="800" Grid.ColumnSpan="2" Margin="49,15,51,27" Grid.Row="1" Background="DarkSeaGreen" Grid.RowSpan="2">
<ItemsControl Name="tilesItemsControl" ItemsSource="{Binding Tiles}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<Rectangle
Width="15"
Height="15"
Fill="{Binding Color}"
Canvas.Left ="{Binding ConvertedXLoc}"
Canvas.Top="{Binding ConvertedYLoc}" />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Name="ArtistItemsControl" ItemsSource="{Binding Artists}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<Rectangle
Width="3.75"
Height="3.75"
Fill="Black"
Canvas.Left ="{Binding ConvertedXLoc}"
Canvas.Top="{Binding ConvertedYLoc}" />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
<Grid/>
<Button Content="Button" HorizontalAlignment="Left" Margin="4,0,0,0" Grid.Row="2" VerticalAlignment="Center" Click="Button_Click"/>
</Grid>
So with the press of the button I added for testing purposes. It changes a value in the List and then the PropertyChanged method should detect that but it doesn't detect it it just skips it.
Question
So my basic question is, how do I detect the change of a property from objects out of the List of Artists.
OnPropertyChanged will only be executed when the property itself is changed. A new item in a list is not a property change. That's the reason why no updates happens.
Instead a List, try an ObservableCollection. An ObservableCollection implements an additional INotifyCollectionChanged which makes the UI able to react on changing items in the list.
In my Code does my Function OnPropertyChanged not update the UI, despite the Fact that it does work everywhere else in my Programm. I try to upgrade the Content of the Button, so that if the User presses it, it will change it's Content. It does Update the value but then won't show it in the UI for some weird Reason. Any help is appreciated...
The View:
public partial class CreateMP : Window
{
public CreateMP()
{
InitializeComponent();
}
public CreateMP(string UserName)
{
CreateMPViewModel.User = UserName;
InitializeComponent();
}
}
The ViewModel:
public class CreateMPViewModel : Window
{
private string _ButtonContent = "Create Server";
private bool _ButtonEnable = true;
public event PropertyChangedEventHandler PropertyChanged;
private readonly TcpListener listener;
private TcpClient client;
public ICommand ButtonCommand_Back { get; set; }
public ICommand ButtonCommand_CreateGame { get; set; }
public string ButtonContent
{
get
{
return _ButtonContent;
}
set
{
if (value != _ButtonContent)
{
_ButtonContent = value;
OnPropertyChanged("ButtonContent");
}
}
}
public bool ButtonEnable
{
get
{
return _ButtonEnable;
}
set
{
if (value != _ButtonEnable)
{
_ButtonEnable = value;
OnPropertyChanged("ButtonEnable");
}
}
}
public static string User { get; set; }
public string IPAdresse { get; set; }
public int Passwort { get; set; }
public CreateMPViewModel()
{
ButtonCommand_Back = new DelegateCommand(BackButtonClick);
ButtonCommand_CreateGame = new DelegateCommand(CreateGameClick);
int Port = 51246;
Random rnd = new Random();
Password = rnd.Next(0, 99999);
IPAddress localAdd = IPAddress.Parse(IPAdresse);
listener = new TcpListener(localAdd, Port);
listener.Start();
}
private void BackButtonClick()
{
client = GameHandler.CurrentClient;
Application.Current.MainWindow.Close();
if (client != null)
{
client.Close();
}
if (listener != null)
{
listener.Stop();
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void CreateGameClick()
{
ButtonEnable = false;
ButtonContent = "Waiting for Player...";
//More Code here after the Update of the Button
}
}
The XAML:
<Window x:Class="CreateMP"
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"
mc:Ignorable="d"
Title="Game" Height="450" Width="400"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize" WindowStyle="None">
<Window.DataContext>
<local1:CreateMPViewModel/>
</Window.DataContext>
<Grid>
<Label Name ="CreateGame" Content="Create Game" Margin="109,46,118,322" FontSize="26" Foreground="#0074BC"/>
<Button Name ="btnCreateGame" Content="{Binding ButtonContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="35" Margin="118,267,118,0" VerticalAlignment="Top" Background="#0074BC" Foreground="White" Command="{Binding ButtonCommand_CreateGame}" IsEnabled="{Binding ButtonEnable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Name ="btnBack" Content="Back" Height="35" Margin="118,387,118,0" VerticalAlignment="Top" Background="#0074BC" Foreground="White" Command="{Binding ButtonCommand_Back}"/>
<Label Content="IP4-Adress:" HorizontalAlignment="Left" Height="30" Margin="92,138,0,0" VerticalAlignment="Top" Width="90" Foreground="#0074BC"/>
<Label Content="Password:" HorizontalAlignment="Left" Height="30" Margin="92,168,0,0" VerticalAlignment="Top" Width="90" Foreground="#0074BC"/>
<Label Content="{Binding IPAdresse}" HorizontalAlignment="Left" Height="30" Margin="187,138,0,0" VerticalAlignment="Top" Width="127" Foreground="#0074BC"/>
<Label Content="{Binding Password}" HorizontalAlignment="Left" Height="30" Margin="187,168,0,0" VerticalAlignment="Top" Width="127" Foreground="#0074BC"/>
</Grid>
</Window>
The CreateMPViewModel class must inherit from INotifyPropertyChanged.
public class CreateMPViewModel : Window, INotifyPropertyChanged
{
//........................................
//........................................
}
I have a problem with the listviewItem, is that when you change the data if they do it but they are not saved in the interface when you click on another item
This problem happens when binding the textbox to the listviewItem
MainPage.xaml
<Grid RequestedTheme="Light">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="818*" />
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBox
x:Name="titulo"
Grid.Row="0"
FontSize="40"
PlaceholderText="Ingresa tu titulo"
KeyDown="Titulo_KeyDown"
/>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<ListView
x:Name="listNotas"
Width="450"
Background="DimGray"
SelectionChanged="ListNotas_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel>
<TextBlock Text="{Binding title, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<RichEditBox
x:Name="editor"
Width="760"
HorizontalAlignment="Stretch" />
</StackPanel>
<GridView
Name="stpanel"
Grid.Row="2"
Height="50">
<TextBlock Text="" Name="Tester"/>
</GridView>
MainPage.xaml.cs
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json" );
public ObservableCollection<Notes> Mynotes;
public MainPage()
{
this.InitializeComponent();
// Load data of Notas.json to Listview
LoadUpdate();
}
private void LoadUpdate()
{
using (StreamReader file = File.OpenText(editpath))
{
var json = file.ReadToEnd();
baseNotes mainnotes = JsonConvert.DeserializeObject<baseNotes>(json);
Mynotes = new ObservableCollection<Notes>();
foreach (var item in mainnotes.notes)
{
Mynotes.Add(new Notes { title = item.title });
}
listNotas.ItemsSource = null;
listNotas.ItemsSource = Mynotes;
listNotas.SelectedIndex = 0;
}
}
private void ListNotas_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string json = File.ReadAllText(editpath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
titulo.Text = jsonObj["notes"][listNotas.SelectedIndex]["title"];
}
private void Titulo_KeyDown(object sender, KeyRoutedEventArgs e)
{
#region
string json = File.ReadAllText(editpath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
int indice = listNotas.SelectedIndex;
jsonObj["notes"][indice]["title"] = titulo.Text;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj);
File.WriteAllText(editpath, output);
// Show json file text in RicheditBox
editor.TextDocument.SetText(Windows.UI.Text.TextSetOptions.None, output);
//Problem
Binding myBinding = new Binding();
myBinding.Source = Mynotes[indice];
myBinding.Path = new PropertyPath("title");
myBinding.Mode = BindingMode.TwoWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(titulo, TextBox.TextProperty, myBinding);
#endregion
}
Model: Notes.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
namespace Realtimejsonedit
{
public class Notes : INotifyPropertyChanged
{
public int created { get; set; }
//public string title { get; set; }
private string Title;
public string title
{
get { return Title; }
set {
Title = value;
NotifyPropertyChanged("title");
}
}
public string text { get; set; }
public int id { get; set; }
public int updated { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class baseNotes
{
public List<Notes> notes { get; set; }
}
}
as I say the problem as I am doing the binding but when executing ListNotas.SelectionChanged the values that were saved in the json file are changed, but they do not remain in the listviewitem, although the binding is in the Keydown event and not in ListNotas. SelectionChanged.
the problem:
https://i.imgur.com/IGcd8iz.gif
What I want to achieve:
https://i.imgur.com/KnkbQw9.gif
UWP - How to save ListViewItem state if the data source has changed?
The problem is that you set bind repeatedly in Titulo_KeyDown event. For your requirement, you could bind ListView SelectItem once. For more please refer the following steps:
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json");
public ObservableCollection<Notes> Mynotes { get; set; }
public ViewModel()
{
LoadUpdate();
SetSelectIndex(0);
}
private void SetSelectIndex(int index)
{
SelectItem = Mynotes[index];
}
private void LoadUpdate()
{
using (StreamReader file = File.OpenText(editpath))
{
var json = file.ReadToEnd();
baseNotes mainnotes = JsonConvert.DeserializeObject<baseNotes>(json);
Mynotes = new ObservableCollection<Notes>();
foreach (var item in mainnotes.notes)
{
Mynotes.Add(new Notes { title = item.title });
}
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Notes _selectItem;
public event PropertyChangedEventHandler PropertyChanged;
public Notes SelectItem
{
get
{
return _selectItem;
}
set
{
_selectItem = value;
OnPropertyChanged();
}
}
}
Xaml
<Page.DataContext>
<local:ViewModel />
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="818*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBox
x:Name="titulo"
Grid.Row="0"
FontSize="40"
PlaceholderText="Ingresa tu titulo"
Text="{Binding SelectItem.title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="Titulo_TextChanged"
/>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<ListView
x:Name="listNotas"
Width="450"
Background="DimGray"
ItemsSource="{Binding Mynotes}"
SelectedItem="{Binding SelectItem, Mode=TwoWay}"
>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<RichEditBox
x:Name="editor"
Width="760"
HorizontalAlignment="Stretch"
/>
</StackPanel>
<GridView
Name="stpanel"
Grid.Row="2"
Height="50"
>
<TextBlock Name="Tester" Text="" />
</GridView>
</Grid>
Code behind (write the data to json)
public sealed partial class MainPage : Page
{
private dynamic jsonObj;
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json");
public ObservableCollection<Notes> Mynotes;
public MainPage()
{
this.InitializeComponent();
string json = File.ReadAllText(editpath);
jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
}
private void Titulo_TextChanged(object sender, TextChangedEventArgs e)
{
#region
int indice = listNotas.SelectedIndex;
jsonObj["notes"][indice]["title"] = titulo.Text;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj);
editor.TextDocument.SetText(Windows.UI.Text.TextSetOptions.None, output);
File.WriteAllText(editpath, output);
#endregion
}
}
This is sample project.
I have a longlistselector to show a gallery of the projects that I have in my app. Every item of the longlistselector have an image of the project, the name and an image to share it in social networks. The problem is that I need know when I touch the share image to leave to a different page that allow us to share it. This is the gallery longlistselector xaml:
<phone:LongListSelector x:Name="GaleryLongListSelector" SelectionChanged="GaleryLongListSelector_SelectionChanged" Margin="0,0,0,15">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Margin="12,2,0,4" Height="100" toolkit:TiltEffect.IsTiltEnabled="True" Grid.Column="0">
<Image Width="80" RenderTransformOrigin="0.5,0.5" Height="80" Source="{Binding ThumbImage}">
<Image.RenderTransform>
<RotateTransform Angle="90"/>
</Image.RenderTransform>
</Image>
<!--<StackPanel Orientation="Vertical">-->
<TextBlock x:Name="txtProjectName" Margin="20,0" VerticalAlignment="Center" Text="{Binding Name}" Style="{StaticResource PhoneTextNormalStyle}" FontSize="{StaticResource PhoneFontSizeExtraLarge}" />
<!--<ScrollViewer VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Visible">
<TextBlock x:Name="txtProjectDescript" Text="Aqui iria una descripcion muy larga del faldksjfjkldjfkldajsfkljaslfkjasldfjlasdjfkl" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" FontSize="{StaticResource PhoneFontSizeLarge}"/>
</ScrollViewer>-->
<!--</StackPanel>-->
</StackPanel>
<Image Source="/Images/share.png" Height="50" Tap="Image_Tap" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu IsZoomEnabled="True" x:Name="ContextMenu">
<toolkit:MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=LocalizedResources.MainPagePanoramaItemGalleryContextMenuDelete}" Click="Delete_Click"/>
<toolkit:MenuItem Header="{Binding Source={StaticResource LocalizedStrings}, Path=LocalizedResources.MainPagePanoramaItemGalleryContextMenuRename}" Click="Rename_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</Grid>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
I read the item selected in a "SelectionChanged" event like this:
private void GaleryLongListSelector_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (GaleryLongListSelector != null && GaleryLongListSelector.SelectedItem != null)
{
var selectedItem = (Project)GaleryLongListSelector.SelectedItem;
var id = selectedItem.ID;
NavigationService.Navigate(new Uri("/ProjectViewPage.xaml?projectID=" + id.ToString(), UriKind.Relative));
}
}
I can use the tap event of the image but using this method I can't find the index of the longlistselector item touched.
Thanks everyone!!
Use the IndexOf() method on your ItemsSource colelction..
var dataItems = GaleryLongListSelector.ItemsSource as List<Project>;
var selectedItem = (Project)GaleryLongListSelector.SelectedItem;
var indexOfSelectedItem = dataItems.IndexOf(selectedItem)
public partial class MainPage : PhoneApplicationPage
{
public Product selectedItemData;
// Constructor
public MainPage()
{
InitializeComponent();
lsttest.ItemsSource = App.lstp;
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
private void Add_Click_1(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri(#"/Page2.xaml", UriKind.Relative));
}
private void lsttest_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
selectedItemData = e.AddedItems[0] as Product;
NavigationService.Navigate(new Uri(#"/Page2.xaml", UriKind.Relative));
}
// Sample code for building a localized ApplicationBar
//private void BuildLocalizedApplicationBar()
//{
// // Set the page's ApplicationBar to a new instance of ApplicationBar.
// ApplicationBar = new ApplicationBar();
// // Create a new button and set the text value to the localized string from AppResources.
// ApplicationBarIconButton appBarButton = new ApplicationBarIconButton(new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative));
// appBarButton.Text = AppResources.AppBarButtonText;
// ApplicationBar.Buttons.Add(appBarButton);
// // Create a new menu item with the localized string from AppResources.
// ApplicationBarMenuItem appBarMenuItem = new ApplicationBarMenuItem(AppResources.AppBarMenuItemText);
// ApplicationBar.MenuItems.Add(appBarMenuItem);
//}
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
Page2 destinationpage = e.Content as Page2;
if (destinationpage != null)
{
// Change property of destination page
destinationpage.GetProduct = selectedItemData;
}
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
//Page2 destinationpage = e.Content as Page2;
//if (destinationpage != null)
//{
// // Change property of destination page
// destinationpage.GetProduct = selectedItemData;
//}
}
}
public partial class Page2 : PhoneApplicationPage
{
public Product GetProduct { get; set; }
public Page2()
{
InitializeComponent();
}
private void Add_Click_1(object sender, RoutedEventArgs e)
{
Product p2 = stkProduct.DataContext as Product;
// p2.Name = txtName1.Text;
if (string.IsNullOrEmpty(p2.ID))
{
p2.ID = DateTime.Now.ToString("yyyyMMddhhmmsstt");
App.lstp.Add(p2);
}
else
{
int index = App.lstp.IndexOf(p2);
App.lstp.Remove(GetProduct);
App.lstp.Insert(index, p2);
}
NavigationService.Navigate(new Uri(#"/MainPage.xaml", UriKind.Relative));
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (GetProduct == null)
{
GetProduct = new Product();
ApplicationBarIconButton btn = (ApplicationBarIconButton)ApplicationBar.Buttons[0];
btn.Text = "Add";
btn.IconUri = new Uri("/Assets/check.png", UriKind.Relative);
}
else
{
ApplicationBarIconButton btn = (ApplicationBarIconButton)ApplicationBar.Buttons[0];
btn.Text = "save";
btn.IconUri = new Uri("/Assets/save.png", UriKind.Relative);
}
stkProduct.DataContext = GetProduct;
}
private void ApplicationBarIconButton_Click_1(object sender, EventArgs e)
{
Product p2 = stkProduct.DataContext as Product;
// p2.Name = txtName1.Text;
if (string.IsNullOrEmpty(p2.ID))
{
p2.ID = DateTime.Now.ToString("yyyyMMddhhmmsstt");
App.lstp.Add(p2);
}
else
{
int index = App.lstp.IndexOf(p2);
App.lstp.Remove(GetProduct);
App.lstp.Insert(index, p2);
}
NavigationService.Navigate(new Uri(#"/MainPage.xaml", UriKind.Relative));
}
}
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="lsttest" SelectionChanged="lsttest_SelectionChanged_1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox x:Name="txtname" Text="{Binding Name}"></TextBox>
<TextBox x:Name="txtID" Text="{Binding ID}" Visibility="Collapsed"></TextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
using System;
using System.IO.IsolatedStorage;
namespace MobileCartWL.HelperClasses
{
public class Helper
{
#region appSettings
public static IsolatedStorageSettings appSettings;
#endregion
//This function is used to insert Key,Value pair information in the Isolated Storage memory
#region InsertDetailInMemory
public static void InsertDetailInMemory(string key, object value)
{
try
{
appSettings = IsolatedStorageSettings.ApplicationSettings;
if (!appSettings.Contains(key))
appSettings.Add(key, value);
else
appSettings[key] = value;
appSettings.Save();
}
catch (Exception) { }
}
#endregion
//This function is used to remove Key,Value pair information from the Isolated Storage memory
#region RemoveDetailInMemory
public static void RemoveDetailInMemory(string key)
{
appSettings = IsolatedStorageSettings.ApplicationSettings;
if (appSettings.Contains(key))
{
appSettings.Remove(key);
appSettings.Save();
}
}
#endregion
//This function is used to check the existing of Key,Value pair information in the Isolated Storage memory
#region IsExistKeyInMemory
/// <summary>
/// Check if Specified Key Is Exists or not
/// </summary>
/// <param name="key">Key</param>
/// <returns>true if it exists otherwise return false</returns>
public static bool IsExistKeyInMemory(string key)
{
appSettings = IsolatedStorageSettings.ApplicationSettings;
if (appSettings.Contains(key))
{
return true;
}
else
{
return false;
}
}
#endregion
//This function is used to get Key,Value pair information from the Isolated Storage memory
#region GetDetailFromMemory
/// <summary>
/// Get value from memory of Specified Key
/// </summary>
/// <param name="key">Key</param>
/// <returns>value as Object of Specified Key</returns>
public static object GetDetailFromMemory(string key)
{
object value = string.Empty;
try
{
appSettings = IsolatedStorageSettings.ApplicationSettings;
if (appSettings.Contains(key))
{
value = appSettings[key];
}
return value;
}
catch (Exception)
{
return value;
}
}
#endregion
}
}