ListView Not showing anything about observable collection data binding - c#

In the view, I have a ListView that should be shown a with a data binding with an Observable collection of string, but not shown anything
If instead of listview I put a label and the observable collection turns it into a simple string I see the data
In the Main view:
<ListView Grid.Row="1" Grid.Column="1" ItemsSource="{Binding SerialsPorts}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding SerialPortName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the Main ViewModel
class BrightnessSerialsPortsViewModel : INotifyPropertyChanged, IBrightnessSerialsPortsViewModel
{
//readonly IPageDialogService pageDialogService;
readonly IBrightnessSerialsPortsManager manager;
public BrightnessSerialsPortsViewModel()
{
//pageDialogService = new PageDialogService();
manager = new BrightnessSerialsPortsManager();
manager.BrightnessInfoUpdated += OnBrightnessInfoUpdated;
manager.DeviceSerialPortsInfoUpdated += OnDeviceSerialsPortsInfoUpdated;
PageAppearingCommand = new Command(OnPageAppearing);
PageDisappearingCommand = new Command(OnPageDisappearing);
}
void OnPageAppearing()
{
//pageDialogService.DisplayAlert("Invoke Command Demo Page", "Appearing event fired.", "OK");
manager.GetBrightness();
manager.GetSerialsPorts();
}
void OnPageDisappearing()
{
//pageDialogService.DisplayAlert("Invoke Command Demo Page", "Disappearing event fired.", "OK");
SerialTest = "";
}
private void OnDeviceSerialsPortsInfoUpdated(object sender, IDeviceSerialsPortsInfoEventArgs e)
{
foreach(string device in e.DeviceSerialsPorts.Devices)
{
ISerialsPortsViewModel serialsPortsViewModel = new SerialsPortsViewModel(device);
SerialsPorts.Add(serialsPortsViewModel);
SerialTest += device + Environment.NewLine;
}
}
private void OnBrightnessInfoUpdated(object sender, IBrightnessInfoEventArgs e)
{
float f = e.DeviceBrightness.Brightness;
decimal dec = new decimal(f);
Brightness = (double) dec;
}
//public ICommand ChangeBrightnessCommand { get; set; }
public ICommand PageAppearingCommand { get; private set; }
public ICommand PageDisappearingCommand { get; private set; }
public ICommand ChangeBrightnessCommand => new RelayCommand(() => ExcecuteChangeBrightnessCommand());
public void ExcecuteChangeBrightnessCommand()
{
}
private ObservableCollection<ISerialsPortsViewModel> serialsPorts = new ObservableCollection<ISerialsPortsViewModel>();
public ObservableCollection<ISerialsPortsViewModel> SerialsPorts { get=> serialsPorts ; set { serialsPorts = value; OnPropertyChanged(nameof(SerialsPorts)); } }
private string serialstest = "";
public string SerialTest { get => serialstest; set {serialstest = value ; OnPropertyChanged(nameof(SerialTest)); } }
private double brightness = 1.0;
public double Brightness { get => brightness; set {brightness = value ; OnPropertyChanged(nameof(Brightness)); } }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
In the SerialPorts ViewModel:
public SerialsPortsViewModel(string serialPortName)
{
SerialPortName = serialPortName;
}
private string serialPortName;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string SerialPortName { get=> serialPortName; set {serialPortName = value ; OnPropertyChanged(nameof(SerialPortName)); } }
What am I doing wrong?

Solved change the View to this:
<ListView Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding SerialsPorts}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell Height="60">
<StackLayout Orientation="Horizontal">
<BoxView BackgroundColor="Blue" WidthRequest="10" Margin="0,0,0,10" />
<StackLayout BackgroundColor="White" Orientation="Vertical" Margin="5,5,10,5">
<Label Text="{Binding SerialPortName}" FontAttributes="Bold" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Related

Adding a character to a tab title when modifying text

i'm working on making a notepad++ equivalent in C# using MVVM design pattern for an university assignment. I've created the tabs successfully but now I have a problem adding the little "*" to the tabname when the content changes from the original and making it disappear upon saving. How can this be implemented ?
Here is the code for the tabcontrol:
<TabControl Margin="10,26,10,10" Grid.Column="2" ItemsSource="{Binding FileTabs}" SelectedIndex="{Binding CurrentSelectedTab}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FileTabName}" />
<Button Command="{Binding Close,
RelativeSource={RelativeSource AncestorType={x:Type local:FileMenuCommands}},
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" Width="20" Height="20" Content="X"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding FileTabContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AcceptsReturn="True" AcceptsTab="True" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
The model for the tabfile:
using System;
using System.Collections.Generic;
using System.Text;
namespace Notepad___.Model
{
class FileTabProvider
{
public string FileTabName { get; set; }
public string FileFullPath { get; set; }
public string FileTabContent { get; set; }
public FileTabProvider(string FileTabName, string FileFullPath, string FileTabContent)
{
this.FileTabName = FileTabName;
this.FileFullPath = FileFullPath;
this.FileTabContent = FileTabContent;
}
}
}
Also the two save functions created in the view model of the mainwindow:
private void SaveFile(object parameter)
{
if (FileTabs[CurrentSelectedTab].FileFullPath == "")
SaveAsFile(parameter);
else
File.WriteAllText(FileTabs[CurrentSelectedTab].FileFullPath, FileTabs[CurrentSelectedTab].FileTabContent.ToString());
}
private void SaveAsFile(object parameter)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
if (saveFileDialog.ShowDialog() == true)
File.WriteAllText(saveFileDialog.FileName, FileTabs[CurrentSelectedTab].FileTabContent.ToString());
}
Implement the INotifyPropertyChanged interface in your view model and change the FileTabName property whenever the FileTabContent property is set. Something like this:
class FileTabProvider : INotifyPropertyChanged
{
private string _originalFileTabName;
private string _fileTabName;
public string FileTabName
{
get { return _fileTabName; }
set { _fileTabName = value; OnPropertyChanged(nameof(FileTabName)); }
}
public string FileFullPath { get; set; }
private string _fileTabContent;
public string FileTabContent
{
get { return _fileTabContent; }
set
{
_fileTabContent = value;
FileTabName += "*";
}
}
public FileTabProvider(string fileTabName, string fileFullPath, string fileTabContent)
{
_fileTabName = fileTabName;
FileFullPath = fileFullPath;
_fileTabContent = fileTabContent;
}
public void Save() => FileTabName = _originalFileTabName;
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Whenever you save, you need to remember to reset the filename:
private void SaveFile(object parameter)
{
var tab = FileTabs[CurrentSelectedTab];
if (tab.FileFullPath == "")
SaveAsFile(parameter);
else
File.WriteAllText(tab.FileFullPath, tab.FileTabContent.ToString());
tab.Save();
}
You can do it this way:
<Grid>
<TabControl>
<TabItem Header="ABC" x:Name="TabItem1" KeyDown="TabItem1_OnKeyDown">
<TabItem.Content>
<Grid>
<TextBox Text="{Binding YourTextProp}" TextChanged="TextBoxBase_OnTextChanged"/>
</Grid>
</TabItem.Content>
</TabItem>
</TabControl>
</Grid>
public MainWindow()
{
InitializeComponent();
}
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
if (TabItem1.Header is string tabItemHeader && !tabItemHeader.Contains("*"))
{
tabItemHeader += "*";
TabItem1.Header = tabItemHeader;
}
}
private void TabItem1_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.S && Keyboard.Modifiers == ModifierKeys.Control && TabItem1.Header is string tabItemHeader)
{
tabItemHeader = tabItemHeader.Substring(0, tabItemHeader.Length - 1);
TabItem1.Header = tabItemHeader;
}
}

I have a question regarding Xamarin.Forms Navigation. How to pass variable values from one page to other?

I have a MainPage.xaml file with the following collection view
<CollectionView ItemsSource="{Binding AllNotes}"
SelectionMode="Single"
SelectedItem="{Binding SelectedNote}"
SelectionChangedCommand="{Binding SelectedNoteChangedCommand}"
Grid.Row="2" Grid.ColumnSpan="2">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Frame>
<Label Text="{Binding .}" FontSize="Title"/>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
In the MainPageView.cs file I took the value of the selected note using the following code
public string selectedNote;
public string SelectedNote
{
get => selectedNote;
set
{
selectedNote = value;
var args = new PropertyChangedEventArgs(nameof(SelectedNote));
PropertyChanged?.Invoke(this, args);
}
}
The SelectedNoteChangeCommand redirects to the DetailPage where the selected note should get printed. The SelectedNoteChangeCommand has the following code
SelectedNoteChangedCommand = new Command(async () =>
{
var detailVM = new ListPageViewDetail.DetailPageView(SelectedNote);
var detailPage = new List.DetailPage();
detailPage.BindingContext = detailVM;
await Application.Current.MainPage.Navigation.PushAsync(detailPage);
});
Now if I display the value of SelectedNote on the same page it shows up but it does not show up on a label field in DetailPage
The DetailPage.xaml has a label field as
<Label Text="{Binding NoteText}" FontSize="Title" Grid.Row="0"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
The DetailPageView.cs file has a constructor as
public DetailPageView(string note)
{
NoteText = note;
DismissPageCommand = new Command(async () =>
{
await Application.Current.MainPage.Navigation.PopAsync();
});
}
Now I want to ask is how to pass the SelectedNote variable value to other pages? The NoteText or note has empty value.
According to your description, you want to pass value between ContentPage, I create sample that you can take a look.
First COntentPage:
<CollectionView
ItemsLayout="VerticalList"
ItemsSource="{Binding AllNotes}"
SelectedItem="{Binding selectednote}"
SelectionChangedCommand="{Binding SelectedNoteChangedCommand}"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding .}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
public partial class Page4 : ContentPage, INotifyPropertyChanged
{
public ObservableCollection<string> AllNotes { get; set; }
private string _selectednote;
public string selectednote
{
get { return _selectednote; }
set
{
_selectednote = value;
RaisePropertyChanged("selectednote");
}
}
public RelayCommand1 SelectedNoteChangedCommand { get; set; }
public Page4()
{
InitializeComponent();
AllNotes = new ObservableCollection<string>()
{
"test 1",
"test 2",
"test 3",
"test 4",
"test 5",
"test 6"
};
selectednote = AllNotes[0];
SelectedNoteChangedCommand = new RelayCommand1(obj=>passdata((string)selectednote));
this.BindingContext = this;
}
private void passdata(string selectednote)
{
Navigation.PushAsync(new Page5(selectednote));
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The RelayCommand1 class, inheriting ICommand, that can pass parameter.
public class RelayCommand1 : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public RelayCommand1(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand1(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_execute(parameter);
}
}
The second ContentPage:
<StackLayout>
<Label
HorizontalOptions="CenterAndExpand"
Text="{Binding .}"
VerticalOptions="CenterAndExpand" />
</StackLayout>
public Page5(string str)
{
InitializeComponent();
this.BindingContext = str;
}
The screenshot:

Progress Bar Not Updating Xamarin Forms MVVM

I know this has been asked before but I've spent ages and nothing has helped.
I'm trying to update a progress bar from a ViewModel however it will not update.
Recipe.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="FitnessScript.Views.Recipes">
<ContentPage.Content>
<StackLayout>
<Label Text="Please Enter Ingredients and Requirements!"
HorizontalOptions="Center"
VerticalOptions="Start" HorizontalTextAlignment="Center" TextType="Text"
Margin="0,20,0,0"
FontSize="25"/>
<Label Text="Enter Ingredients" Margin="5"/>
<Entry x:Name="Ingredients"
Text="{Binding Ingredients}"
Placeholder="Ingredients"
PlaceholderColor="LightGray" />
<Label Text="Enter Calories" Margin="5"/>
<Entry x:Name="Calories"
Text="{Binding Calories}"
Placeholder="Calories"
PlaceholderColor="LightGray" />
<Button x:Name="RecipeSearchBtn"
Text="Find Recipes"
Command="{Binding RequestRecipeCommand}" />
<ProgressBar x:Name="ProgressB"
Progress="{Binding ProgressValue}"
ProgressColor="Purple"
IsVisible="True"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Recipes.xmal.cs
namespace FitnessScript.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Recipes : ContentPage
{
RecipeSearchViewModel recipeSearchViewModel;
public Recipes()
{
recipeSearchViewModel = new RecipeSearchViewModel();
InitializeComponent();
BindingContext = recipeSearchViewModel;
}
}
}
RecipeSearchViewModel
namespace FitnessScript.ViewModels
{
public class RecipeSearchViewModel : BaseViewModel
{
private static readonly IRecipeService _recipeService = new RecipeService();
private readonly BackgroundWorker worker;
#region Getters/Setters
string _ingredients;
public string Ingredients
{
get { return _ingredients; }
set
{
_ingredients = value;
OnPropertyChanged("Ingredients");
}
}
int _calories;
public int Calories
{
get { return _calories; }
set
{
_calories = value;
OnPropertyChanged("Calories");
}
}
float _progressValue;
public float ProgressValue
{
get { return _progressValue; }
set
{
_progressValue = value;
OnPropertyChanged("ProgressValue");
}
}
#endregion
public RecipeSearchViewModel()
{
this.worker = new BackgroundWorker();
}
public Command RequestRecipeCommand
{
get
{
return new Command(async () => await RequestRecipe());
}
}
private async Task RequestRecipe()
{
await Task.Run(() =>
{
Device.BeginInvokeOnMainThread(() =>
{ ProgressValue = 1; }
);
});
List<string> ingredientsList = await _recipeService.GetRecipe(Ingredients, Calories);
App.Current.MainPage.DisplayAlert("Success", $"{Ingredients}, {Calories}", "Close");
}
}
}
I Have tired many different alternatives, such as setting ProgressValue to Double and Decimal, forcing the UI thread, with and without adding a parameter to OnPropertyChange(). I've attempted background works too, just nothing sadly.
I'm debugging using a S10+ via USB as I prefer it to emulation.
The overall aim is to press the RecipeSearchBtn, do the logic, and update the progress bar along with it, however for debugging purposes I just want to change the progress to 100% when the button command executes
Any help would be appreaciated, thanks
Also I have tried the Activity Indicator however similar issues, it never showed while debugging though my phone when setting the visibility ect to true through binding IsBool
About binding ActivityIndicator isvisible, I do one sample that you can take a look:
Please take a look the following code, ActivityIndicator display firstly, clicking button to load data, setting ActivityIndicator isVisible and IsRunning as false.
<StackLayout>
<Button
x:Name="btn1"
Command="{Binding command1}"
Text="load data" />
<ActivityIndicator
HeightRequest="50"
IsRunning="{Binding isvisible}"
IsVisible="{Binding isvisible}"
WidthRequest="50"
Color="Red" />
<ListView ItemsSource="{Binding students}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding name}" />
<Label Text="{Binding age}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new studentviewmodel();
}
}
public class studentviewmodel:ViewModelBase
{
public ObservableCollection<studentmodel> students { get; set; }
public Command command1 { get; set; }
private bool _isvisible;
public bool isvisible
{
get { return _isvisible; }
set
{
_isvisible = value;
RaisePropertyChanged("isvisible");
}
}
public studentviewmodel()
{
command1 = new Command(loaddata);
isvisible = true;
students = new ObservableCollection<studentmodel>();
}
private async void loaddata()
{
//call service to do other something.
await Task.Delay(5000);
students.Add(new studentmodel() { name = "cherry", age = 29 });
students.Add(new studentmodel() { name = "barry", age = 30 });
students.Add(new studentmodel() { name = "annine", age = 15 });
isvisible = false;
}
}
public class studentmodel
{
public string name { get; set; }
public int age { get; set; }
}
The ViewModelBase is the class that implementing INotifyPropertyChanged, to notify data changed.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The screenshot:

UWP - How to save ListViewItem state if the data source has changed?

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.

How to prevent all buttons text in a listview from changing xamarin forms

I am wondering if someone could please help me or point me in the right direction. I have a Listview that binds to a Viewmodel , This listview has a label and a button . The listview is populated with music. When i press on the play button all the buttons that are populated with name of song in the listview has their name changed to stop , How do i prevent that from happening. Please find code below.
public class MusicViewModel : INotifyPropertyChanged
{
public MusicViewModel()
{
GetMusic();
CommandText = "Play";
}
public List<AudioModel> _audioList;
public List<AudioModel> AudioList
{
get { return _audioList; }
set
{
_audioList = value;
OnPropertyChanged();
}
}
async void GetMusic()
{
var audioService = new AudioServices();
AudioList = await audioService.GetAudioAsync();
}
private string _commandText;
public string CommandText
{
get { return _commandText; }
set
{
_commandText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CommandText"));
}
}
public ICommand _playCommand;
public ICommand PlayCommand
{
get
{
return _playCommand ?? (_playCommand = new Command((obj) =>
{
var item = (obj as AudioModel);
var id = item.souID;
if (CommandText == "Play")
{
CrossMediaManager.Current.Play("http://Example.co.za/Example.asp?fldPRK=souID&PRKvalue=" + id + "&fldBLOB=sousound&TableName=Sounds_Sound_Details&ACT=BLOB&width=250&square=yes", MediaFileType.Audio);
CommandText = "Stop";
}
else if (CommandText == "Stop")
{
CrossMediaManager.Current.Stop();
CommandText = "Play";
}
}));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
<ListView ItemsSource="{Binding AudioList}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid x:Name="Item">
<StackLayout>
<Label Text="{Binding souName}" />
<Button Text="{Binding Source={x:Reference Name=RINGTONE},Path=BindingContext.CommandText}"
Command="{Binding Source={x:Reference Name=RINGTONE},Path=BindingContext.PlayCommand}"
CommandParameter="{Binding Source={Reference Item},Path=BindingContext}"/>
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You are binding each item to the same, shared command in the MusicViewModel. That is why all items are updated when you change it.
What you probably want is to add the command to the AudioModel. That way it will only update for that specific entry.

Categories

Resources