I'm trying to make the TextBox show the stringBody property of the CurrentDialog property of the window. Here's the XAML part:
<TextBox x:Name="ComposerBox" Height="90.302" Margin="311,0,141.355,10"
...
Text="{Binding Body}"
ScrollViewer.CanContentScroll="True" SpellCheck.IsEnabled="True"
VerticalAlignment="Bottom">
Here is a string from the windows constructor:
MessagingBox.DataContext = CurrentDialog;
I've also tried to set DataContext to this with no result.
Here's how CurrentDialog is defined:
private MessageDialog CurrentDialog { get; set; }
Here's the MessageDialog class definition:
[Serializable][DataContract]
public class MessageDialog
{
public string Name { get; private set; }
public UserData User { get; private set; }
private List<Message> Dialog = new List<Message>();
public string Body { get; private set; }
public MessageDialog(UserData data)
{
Name = data.Username;
User = data;
Body = "";
}
public void Add(Message msg)
{
Dialog.Add(msg);
Body += $"{msg.From}: {msg.Body} \n\n";
}
}
}
The binding doesn't work at all. I also want it to be one-way.
Text="{Binding CurrentPerson.Body}"
Not sure why the binding path contains CurrentPerson, when it should be CurrentDialog, but even that isn't supposed to be there. Since the DataContext is already set to CurrentDialog, you can simply bind the text to :
Text="{Binding Body}"
You need to implement INotifyPropertyChanged, so the WPF know when the property changed:
[Serializable][DataContract]
public class MessageDialog : INotifyPropertyChanged
{
#region public string Body
private string m_Body;
public string Body
{
get { return m_Body; }
private set
{
if (m_Body == value)
return;
m_Body = value;
this.NotifyPropertyChanged();
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Related
Is there a way to put a Page inside a <Grid/>, <StackPanel/>, <ContentControl/> or <ScrollViewer/> as content from code using a constructor call?
I expect such things:
XAML:
<Grid>
<ScrollViewer Content="{Binding Panel0}"/>
</Grid>
C#:
public class TestWindowViewModel : Page
{
public string Name { get; private set; }
public string Description { get; private set; }
public TestWindowViewModel(string name, string description)
{
Name = name;
Description = description;
}
}
_
public partial class SomeViewModel : Page
{
public TestWindowViewModel Panel0;
public SomeViewModel()
{
Panel0 = new TestWindowViewModel("panelName", "panelDescription");
InitializeComponent();
}
}
You can use a Frame tag
<ScrollViewer>
<Frame content = "{Binding MyPage}"/>
</ScrollViewer>
If you don't want to have a prop in your ViewModel then you should be able to do
<ScrollViewer>
<Frame>
<Frame.Content>
<locals:MyPage>
</Frame.Content>
</ScrollViewer>
Keep in mind you have something called TestWindowViewModel and it inherits Page. This is not a ViewModel. Instead it is a normal page.
You want something that looks like this:
public class NotifyPropertyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Page myPage;
public Page MyPage
{
get { return myPage; }
set
{
myPage = value;
NotifyPropertyChanged();
}
}
}
and you can go a level farther and make an abstract class:
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and then you can just inherit ViewModel like so:
public class TestWindow: Page
{
public TestWindow()
{
InitializeComponent();
}
}
public class TestWindowViewModel : ViewModel
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged();
}
}
private string description;
public string Description
{
get { return description; }
set
{
Description = value;
NotifyPropertyChanged();
}
}
}
Once you get this all seperated out correctly you can use the frame and do the same for the SomePage and SomePageViewModel and then you can use actual binding on the Frame Content from the ViewModel. I know this is long winded, but if you start out right on setting up a good MVVM setup you will save yourself headache if you ever get into Async and what not.
in xaml:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Frame x:Name="CurrentPage" NavigationUIVisibility="Hidden"></Frame>
</ScrollViewer>
in cs:
CurrentPage.Content = content;
where content is Page
In the shown code i need to know the coding to be replaced in place of question mark in the code. I need to delete,edit and update the item in the list view without writing any code in code behind. I only want to do these operations by bindin view with view model through Icommand
This a class in my model Playlist.cs
namespace MvvmDemo.Models
{
public class Playlist
{
public string Title { get; set; }
}
}
This is a class in my viewmodel PlaylistsViewModel.cs
namespace MvvmDemo.ViewModels
{
public class PlaylistsViewModel
{
public ObservableCollection Playlists { get; private set; } = new ObservableCollection();
public ICommand AddPlaylistCommand { get; private set; }
public ICommand DeletePlaylistCommand { get; private set; }
public ICommand EditPlaylistCommand { get; private set; }
public PlaylistsViewModel()
{
AddPlaylistCommand = new Command(AddPlaylist);
DeletePlaylistCommand = new Command(DeletePlaylist);
}
public void AddPlaylist()
{
var newPlaylist = "Playlist " + (Playlists.Count + 1);
Playlists.Add(new Playlist { Title = newPlaylist });
}
public void DeletePlaylist()
{
????????????????
}
public void EditPlaylist()
{
????????????????
}
}
}
you have to make the command is parameterised and pass binding data through the parameter.
and from that data you can get the index value of selected.using that remove the item from the list.
Playlists.RemoveAt("INDEX_NUMBER");
To update it in the view use "INotifyProperty" also
If you want to delete and edit item in ListView, firstly, you should need to use ICommand, then you could need to use INotifyPropertyChanged to implement Inotify.
I do one sample that you can take a look. Choosing one Item and long press with the left mouse button, you will see two ways, delete Item and Edit Item action.
<ContentPage.Content>
<StackLayout>
<ListView
x:Name="mylistview"
ItemsSource="{Binding lists}"
SelectedItem="{Binding selecteditem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem
Command="{Binding BindingContext.DeletePlaylistCommand, Source={x:Reference Name=mylistview}}"
IsDestructive="true"
Text="Delete Item" />
<MenuItem
Command="{Binding BindingContext.EditPlaylistCommand, Source={x:Reference Name=mylistview}}"
IsDestructive="true"
Text="Edit Item" />
</ViewCell.ContextActions>
<StackLayout Padding="15,0">
<Label Text="{Binding Title}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page19 : ContentPage, INotifyPropertyChanged
{
public ObservableCollection<Playlist> lists { get; set; }
//public RelayCommand1 AddPlaylistCommand { get; set; }
public RelayCommand DeletePlaylistCommand { get; set; }
public RelayCommand EditPlaylistCommand { get; set; }
private Playlist _selecteditem;
public Playlist selecteditem
{
get { return _selecteditem; }
set
{
_selecteditem = value;
RaisePropertyChanged("selecteditem");
}
}
public Page19 ()
{
InitializeComponent ();
lists = new ObservableCollection<Playlist>()
{
new Playlist(){Id=1,Title="list 1"},
new Playlist(){Id=2, Title="list 2"},
new Playlist(){Id=3,Title="list 3"},
new Playlist(){Id=4,Title="list 4"},
new Playlist(){Id=5,Title="list 5"},
new Playlist(){Id=6,Title="list 6"},
};
DeletePlaylistCommand = new RelayCommand(DeletePlaylist);
EditPlaylistCommand = new RelayCommand(EditPlaylist);
selecteditem = lists[0];
this.BindingContext = this;
}
public void AddPlaylist()
{
}
public void DeletePlaylist()
{
Playlist item = selecteditem;
lists.Remove(item);
}
public void EditPlaylist()
{
Playlist item = selecteditem;
int id = item.Id;
foreach(Playlist playl in lists.Where(a=>a.Id==id))
{
playl.Title = "chenge title";
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Playlist: INotifyPropertyChanged
{
private int _Id;
public int Id
{
get { return _Id; }
set
{
_Id = value;
RaisePropertyChanged("Id");
}
}
private string _Title;
public string Title
{
get { return _Title;}
set
{
_Title = value;
RaisePropertyChanged("Title");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the RelayCommd:
public class RelayCommand : ICommand
{
readonly Action _execute;
public RelayCommand(Action execute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_execute();
}
}
You can use observablecollection. It will reflect add,remove operation of item to the listview. And for editing item you have to raise property changed for all property you are editing.To simplify that property changed you can implement property changed event to your Playlist model class.
Like
public void DeletePlaylist()
{
Playlists.Remove(newPlaylist);
}
public void EditPlaylist()
{
newPlaylist.Title="Refreshed Playlist"
}
public class Playlist:INotifyPropertyChanged
{
private string title;
public string Title
{
get{return title;}
set{title=value;
NotifyPropertyChanged();}
}
}
I want to read some data from database and do some process on them and then view them in the view.
I read a lot about MVVM and now I am confused.
Imaging I read a person entity from database with Name attribute.
please make a small code and show me how should I make my model and ViewModel.
I guess it we will be something like this :
public class PersonModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string Name;
public string name
{
get
{
return Name;
}
set
{
Name = value;
onpropertychanged("name");
}
}
public PersonModel( string s)
{
name = s;
}
public void onpropertychanged(string PName)
{
if (PropertyChanged !=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PName));
}
}
}
public class PersonViewModel
{
public ObservableCollection <PersonModel> list { get; set; }
public PersonViewModel()
{
list = new ObservableCollection<model>();
list.Add(new model("abc"));
list.Add(new model("def"));
}
public void change()
{
list[1].name = "changed";
}
}
public class ViewModelBase
{
public PersonViewModel vperson { get; set; }
public ViewModelBase()
{
vperson = new PersonViewModel();
vperson.change();
}
}
Edite : Where should database connections be?
Edite :
<Grid>
<TextBox Text="{Binding vperson.list[1].name}" />
</Grid>
</Window>
I edited your classes and is working
public class PersonModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (_name == value) return;
_name = value;
OnPropertyChanged();
}
}
public PersonModel(string name)
{
_name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class PersonViewModel
{
public ObservableCollection<PersonModel> Items { get; set; }
public PersonViewModel()
{
Items = new ObservableCollection<PersonModel> { new PersonModel("abc"), new PersonModel("def") };
}
public void Change()
{
Items[1].Name = "changed";
}
}
public class ViewModelBase
{
public PersonViewModel PersonViewModel { get; set; }
public ViewModelBase()
{
PersonViewModel = new PersonViewModel();
PersonViewModel.Change();
}
}
//Use the dataContext in this way, will help you with the strong type
xmlns:viewModels="clr-namespace:WpfApp1.ViewModels"
<Window.DataContext>
<viewModels:ViewModelBase />
</Window.DataContext>
<Grid>
<TextBox Text="{Binding PersonViewModel.Items[1].Name}" />
</Grid>
I am trying to bind the data displayed in a DataGrid to a dynamic list of object (WhisperModel) which is inside another object(WhisperReader). The DataGrid only displays the headers, but no values. How can I make the DataGrid dynamically update itself when the list "whispers" is changed?
Main Window XAML:
<DataGrid x:Name="whisperDataGrid" Margin="10,69,10,10" IsReadOnly="True" ItemsSource="{Binding}"/>
Main Window C#
public partial class MainWindow : Window
{
private WhisperReader wr;
public MainWindow()
{
InitializeComponent();
wr = new WhisperReader();
whisperDataGrid.DataContext = wr.whispers;
}
WhisperReader:
class WhisperReader
{
public ObservableCollection<WhisperModel> whispers { get; private set; }
public WhisperReader()
{
whispers = new ObservableCollection<WhisperModel>();
}
WhisperModel:
class WhisperModel
{
public DateTime sentTime { get; set; }
public string sender { get; set; }
public string message { get; set; }
}
I think your problem is that it doesn't know when to update itself because:
You have made the whispers list the data context.
The properties that you are binding to don't use INotifyPropertyChanged.
WhisperReader and WhisperModel are not public
All bindings must be public, must be properties, and must call the PropertyChanged method.
The PropertyChanged function triggers the binding updates.
Try this...
public partial class MainWindow : Window
{
private WhisperReader wr;
public MainWindow()
{
InitializeComponent();
wr = new WhisperReader();
whisperDataGrid.DataContext = wr;
}
public class WhisperReader : INotifyPropertyChanged
{
ObservableCollection<WhisperModel> _whispers;
public ObservableCollection<WhisperModel> whispers
{
get { return _whispers; }
private set
{
_whispers = value;
NotifyPropertyChanged();
}
}
public WhisperReader()
{
whispers = new ObservableCollection<WhisperModel>();
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class WhisperModel : INotifyPropertyChanged
{
public DateTime sentTime { get; set; }
private string _sender;
public string sender
{
get { return _sender; }
set { _sender = value; NotifyPropertyChanged();
}
private string _message;
public string message
{
get { return _message; }
set { _message = value; NotifyPropertyChanged();
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
<DataGrid x:Name="whisperDataGrid" Margin="10,69,10,10" IsReadOnly="True" AutoGenerateColumns="True" ItemsSource="{Binding whispers}"/>
I am using a ListBox in my Metro UI media player, but the ListBox doesn't show any text when I run it. I don't have any errors or so, but there is no text. I have also tried to use a ListView and not a ListBox, but the result was the same. What can I do ?
I am using the next code for the ListBox on the XAML part of the page
<ListBox x:Name="ItemListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding PathToFile}"
FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
<TextBlock Text="{Binding HasVideo}"
FontSize="16" Margin="15,0,0,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and the next code on the page C# code :
public static ListBox ListBoxIstance = null;
public MainPage()
{
InitializeComponent();
ListBoxIstance = ItemListBox;
ItemListBox.ItemsSource = Data_Repository.MediaData.MediaList;
ItemListBox.DataContext = Data_Repository.MediaData.MediaList;
}
where MediaList is a list declared as it follows
public static List<MediaFile> MediaList = new List<MediaFile>();
and MediaFile is a class
public class MediaFile
{
public TimeSpan Duration = TimeSpan.Zero;
public bool HasAudio = false;
public bool HasVideo = false;
public String PathToFile = null;
public MediaFile(string _pathToFile)
{
PathToFile = _pathToFile;
}
}
I am using the next code to update the MediaList and the ItemListBox
foreach (var pathToFile in files)
{
MediaList.Add(new MediaFile(pathToFile.Path));
MainPage.ListBoxIstance.UpdateLayout();
}
EDIT : After some suggestions :
That's how the MediaFile class looks like :
public class MediaFile : INotifyPropertyChanged
{
private bool _hasAudio;
public bool HasAudio
{
get { return _hasAudio; }
set
{
_hasAudio = value;
OnPropertyChanged("HasAudio");
}
}
private bool _hasVideo;
public bool HasVideo
{
get { return _hasVideo; }
set
{
_hasVideo = value;
OnPropertyChanged("HasVideo");
}
}
private String _pathToFile;
public String PathToFile
{
get { return _pathToFile; }
set
{
_pathToFile = value;
OnPropertyChanged("PathToFile");
}
}
public MediaFile(string pathToFile)
{
PathToFile = pathToFile;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
and I am using
public static ObservableCollection<MediaFile> MediaList = new ObservableCollection<MediaFile>();
You can't bind to public fields in your item class. Turn the class members into public properties:
public class MediaFile
{
public TimeSpan Duration { get; set; }
public bool HasAudio { get; set; }
public bool HasVideo { get; set; }
public String PathToFile { get; set; }
public MediaFile(string _pathToFile)
{
PathToFile = _pathToFile;
}
}
Then use an ObservableCollection instead of a List. That would automatically update the ItemsSource binding when items are added or removed. No need to call UpdateLayout.
public ObservableCollection<MediaFile> MediaList =
new ObservableCollection<MediaFile>();
If you also want to update the UI when any of the property values changes after a MediaFile object has been added to the list, you also have to implement the INotifyPropertyChanged interface:
public class MediaFile : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool hasVideo
public bool HasVideo
{
get { return hasVideo; }
set
{
hasVideo = value;
OnPropertyChanged("HasVideo");
}
}
// other properties
}
You can only bind with properties and not with fields. Change fields to properties.
public bool HasVideo {get;set;}
public String PathToFile {get;set;}