WP7 - Refresh ListBox on navigation GoBack() - c#

I have a MainPage.xaml where is ListBox and Button. When I click on the button then MainPage is navigated to AddPage.xaml. This page is for adding new items, there are two TextBoxes and submit Button. When I click on that submit Button,then data from TextBoxes are saved to XML file and then is called GoBack().
I need to refresh ListBox in my MainPage.xaml when Im going back from AddPage.xaml, but it doesnt work automaticly. How can I do that?
My MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Context> Contexts { get; private set; }
public MainViewModel()
{
this.Contexts = new ObservableCollection<Context>();
}
public bool IsDataLoaded
{
get;
private set;
}
public void LoadData()
{
try
{
var file = IsolatedStorageFile.GetUserStoreForApplication();
XElement xElem;
using (IsolatedStorageFileStream read = file.OpenFile("contexts.xml", FileMode.Open))
{
xElem = XElement.Load(read);
}
var contexts = from context in xElem.Elements("Context")
orderby (string)context.Element("Name")
select context;
foreach (XElement xElemItem in contexts)
{
Contexts.Add(new Context
{
Name = xElemItem.Element("Name").Value.ToString(),
Note = xElemItem.Element("Note").Value.ToString(),
Created = xElemItem.Element("Created").Value.ToString()
});
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and Context.cs
public class Context : INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
private string _note;
public string Note
{
get
{
return _note;
}
set
{
if (value != _note)
{
_note = value;
NotifyPropertyChanged("Note");
}
}
}
private string _created;
public string Created
{
get
{
return _created;
}
set
{
if (value != _created)
{
_created = value;
NotifyPropertyChanged("Created");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

You'll need to tell the main page that there is new data to reload.
At it's simplest, something like this:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.NavigationMode == NavigationMode.Back)
{
(this.DataContext as MainViewModel).LoadData();
}
}

Tip: You aren't raising a property changed notification for your View Model's properties.
On the load event of the MainPage, call LoadData. You should also clear the observable collection when you call the LoadData method before adding anything to it because simply loading the data will cause duplicate entries in your collection.

Related

How to copy custom ObservableCollection elements to another custom ObservableCollection?

I created a MTObservableCollection class that derives from ObservableCollection for multithreading. This is how the function looks like:
public class MTObservableCollection<T> : ObservableCollection<T> where T: LogClassa
{
public MTObservableCollection() { }
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
if (CollectionChanged != null)
foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
{
DispatcherObject dispObj = nh.Target as DispatcherObject;
if (dispObj != null)
{
Dispatcher dispatcher = dispObj.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => nh.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
nh.Invoke(this, e);
}
}
}
And here is my LogClass
public class LogClass : INotifyPropertyChanged
{
private string timestamp;
private string title;
private string message;
private MTObservableCollection<LogClass> childRowLog = new MTObservableCollection<LogClass>();
public string TimeStamp
{
get { return timestamp; }
set
{
if( timestamp != value )
{
timestamp = value;
OnPropertyChanged("TimeStamp");
}
}
}
public string Title
{
get { return title; }
set
{
if( title!= value )
{
title= value;
OnPropertyChanged("Title");
}
}
}
public string Message
{
get { return message; }
set
{
if( message!= value )
{
message= value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
So if I have two MTObservableCollection collections and I add elements to one of them such as:
public static MTObservableCollection<LogClass> parentLogCollection = new MTObservableCollection<LogClass>();
public MTObservableCollection<LogClass> childLogCollection = new MTObservableCollection<LogClass>();
childLogClass.Add( new LogClass { TimeStamp = "time", Title = "title", Message = "message" } );
Now if I want to add childLogCollection to parentLogCollection, I would do this:
parentLogCollection = new MTObservableCollection<LogClass>(childLogCollection);
I would think I need to create an implementation within the MBObservationCollection class which should be
public MTObservableCollection(MTObservableCollection<T> collection)
{
}
I just don't know what to input into it, how would I perform it?
Try changing the parent of your MTObservableCollection like this:
public MTObservableCollection(MTObservableCollection<T> collection) : base(collection)
{
}

MVVM C#, Moving properties from ViewModel to Model Class

I have folder browser dialog that is bound to the SetterName
private void OnClick(object sender, RoutedEventArgs e)
{
var dialog = new FolderBrowserDialog();
var result = dialog.ShowDialog();
if (result == DialogResult.OK && AssociatedObject.DataContext != null)
{
var propertyInfo = AssociatedObject.DataContext.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
.Where(p => p.CanRead && p.CanWrite)
.First(p => p.Name.Equals(SetterName));
string dirName = new DirectoryInfo(dialog.SelectedPath).Name;
FolderName = dirName;
_fileName = System.IO.Path.GetFileName(dirName);
FileName = _fileName;
propertyInfo.SetValue(AssociatedObject.DataContext, dialog.SelectedPath, null);
}
}
And I have the properties set in the ViewModel
public class CommonUseWindowViewModel:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Model New { get; set; }
public ICommand Build { get; set; }
public CommonUseWindowViewModel()
{
Build = new DelegateCommand(ClickedMethod);
}
protected async void ClickedMethod()
{
IsEnabled = false;
var gdbName = FolderName;
var styleFol = StyleName;
var envArray = await QueuedTask.Run(() => Geoprocessing.MakeEnvironmentArray(overwriteoutput: true));
var valueArray = await QueuedTask.Run(() => Geoprocessing.MakeValueArray(gdbName, styleFol));
string toolPath = #"c:\staging\ProBaseMapBuilder\BasemapBuilder.tbx\BasemapCreator";
var gpresult1 = await QueuedTask.Run(() => Geoprocessing.ExecuteToolAsync(toolPath, valueArray, envArray));
MessageBox.Show("All Layers Have Been Added to The Map");
IsEnabled = true;
}
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
OnPropertyChanged("IsEnabled");
}
}
private string _folderName;
public string ShortenedFolderName => Path.GetFileName(_folderName);
public string FolderName
{
get { return _folderName; }
set
{
_folderName = value;
OnPropertyChanged("FolderName");
OnPropertyChanged(nameof(ShortenedFolderName));
}
}
private string _styleName;
public string ShortenedStyleName => Path.GetFileName(_styleName);
public string StyleName
{
get { return _styleName; }
set
{
_styleName = value;
OnPropertyChanged("StyleName");
OnPropertyChanged(nameof(ShortenedStyleName));
}
}
private void OnPropertyChanged(string propertyname)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyname));
}
private void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, args);
}
}
I would like to move the properties to a Model class but when I do the folder dialog raises an error that there is no matching setter name. I think the problem comes not knowing how to bind this to the view. I am wondering if I am referencing namespace correctly in the view
xmlns:FolderDialog="clr-namespace:BasemapCreator.Behaviors"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Behaviors="clr-namespace:ArcGIS.Desktop.Internal.Framework.Behaviors;assembly=ArcGIS.Desktop.Framework" x:Class="BasemapCreator.CommonUseWindow"
xmlns:DataContext="clr-namespace:BasemapCreator.Models"
<TextBox x:Name="gdbName" HorizontalAlignment="Left" Height="30" Margin="56,29,0,0" Text="{Binding Model.FolderName, Mode=TwoWay}" VerticalAlignment="Top" Width="282" AllowDrop="True" Visibility="Hidden">
When I add the Model.FolderName to the text box binding I get the error coming from folder dialog.
here is my model
namespace BasemapCreator.Models
{
public class Model:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
OnPropertyChanged("IsEnabled");
}
}
private string _folderName;
public string ShortenedFolderName => Path.GetFileName(_folderName);
public string FolderName
{
get { return _folderName; }
set
{
_folderName = value;
OnPropertyChanged("FolderName");
OnPropertyChanged(nameof(ShortenedFolderName));
}
}
private string _styleName;
public string ShortenedStyleName => Path.GetFileName(_styleName);
public string StyleName
{
get { return _styleName; }
set
{
_styleName = value;
OnPropertyChanged("StyleName");
OnPropertyChanged(nameof(ShortenedStyleName));
}
}
private void OnPropertyChanged(string propertyname)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyname));
}
private void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, args);
}
}
}
I am using a viewmodel created from a button click
Button Class
namespace BasemapCreator
{
internal class ShowWindow : Button
{
private CommonUseWindow _dlg = null;
protected override void OnClick()
{
if (_dlg != null) return;
_dlg = new CommonUseWindow();
_dlg.Closing += ProWin_Closing;
_dlg.Owner = FrameworkApplication.Current.MainWindow;
_dlg.Show();
}
void ProWin_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_dlg = null;
}
}
}
In the code behind for the view I have the DataContext set to the viewModel, can I change it and still use the viewmodel?
using ArcGIS.Desktop.Framework.Controls;
using BasemapCreator.ViewModels;
using System;
using System.Windows;
using BasemapCreator.Models;
namespace BasemapCreator
{
/// <summary>
/// Interaction logic for CommonUseWindow.xaml
/// </summary>
public partial class CommonUseWindow : ProWindow
{
private CommonUseWindowViewModel _vm = new CommonUseWindowViewModel();
public CommonUseWindow()
{
InitializeComponent();
this.DataContext = _vm;
}
}
}
Here is the code behind for the view,
In your MainWindow code behind...
using YourProjectName.FolderName;
Then in the MainWindow constructor
InitializeComponent();
//usually this is the only code behind in a view
DataContext = new ClassName();

WPF basic Binding not working

Im new to WPF and Iv been breaking my head over this for past couple of days. I am trying to set a basic binding of textbox to a string property. I followed the MS tutorial but nothing seems to be working.
Here's email class, I am trying to bind its subject property to display in a textbox
public class Email : INotifyPropertyChanged
{
private string _subject;
public string Subject
{
get { return _subject; }
set
{
_subject = value;
OnPropertyChanged("Subject");
}
}
private string _contents;
public string Contents
{
get { return _contents; }
set
{
_contents = value;
OnPropertyChanged("Contents");
}
}
private Category _category;
public Category Category
{
get { return _category; }
set
{
_category = value;
OnPropertyChanged("Category");
}
}
public Email()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Here's the email setter inside UserControl that parents the textbox:
private Email _email;
public Email Email
{
get { return _email; }
set
{
_email = value;
if (_email != null)
{
Binding myBinding = new Binding("Subject");
myBinding.Source = _email;
tbSubject.SetBinding(TextBlock.TextProperty, myBinding);
}
}
}
tbSubject is never getting set to anything, its always empty even if email passed is not null and has a subject! If I do just this:
public Email Email
{
get { return _email; }
set
{
_email = value;
if (_email != null)
{
tbSubject.Text = _email.Subject;
}
}
}
it works fine. I dont understand what I am doing wrong.
I think I've got it. Here's the change I had to make:
public partial class EmailContentsTemplate : UserControl, INotifyPropertyChanged
{
private Email _email;
public Email Email
{
get { return _email; }
set
{
_email = value;
OnPropertyChanged("Email");
}
}
public EmailContentsTemplate()
{
InitializeComponent();
DataContext = this;
Binding myBinding = new Binding("Email.Subject");
myBinding.Source = this;
tbSubject.SetBinding(TextBlock.TextProperty, myBinding);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
So I changed source to "this" and made the user control implement INotifyPropertyChanged. Now it works.
Alternatively I got it working through XAML
<TextBox x:Name="tbSubject" Grid.Column="1" Grid.Row="1" Margin="3" Text="{Binding Email.Subject}"/>

WPF DataGrid-DataGridCheckBoxColumn vs2010 c# .net

I am working in vs2010.
I have created a DataGrid which is bounded to
ObservableCollection List;
the Class_CMD looks like this :
public class Class_RetrieveCommand
{
public string CMD { get; set; }
public bool C_R_CMD { get; set; }
public bool S_CMD { get; set; }
public bool C_S_CMD { get; set; }
}
i have 4 delegates which i pass to another window, and this window needs to update the list during runtime. During the runtime i can see the string column of the grid updated all the time but the DataGridCheckBoxColumns are never updated.
the DataGrid -
<DataGrid Background="Transparent" x:Name="DataGrid_CMD" Width="450" MaxHeight="450" Height="Auto" ItemsSource="{Binding}" AutoGenerateColumns="True">
one of the delegates which updates the bool is -
public void UpdateC_S_CMD(string Msg)
{
foreach (Class_CMD c in List.ToArray())
{
if (c.CMD.Equals(Msg))
c.C_S_CMD = true;
}
}
I don't understand why the bool columns are not updated....
can anyone help please?
thanks.
Your class Class_RetrieveCommand needs to implement the INotifyPropertyChanged interface. Otherwise the individual rows databound to the instances of the class don't know that the underlying properties have changed. If you change it to something like this, you should see the changes reflected in your grid:
public class Class_RetrieveCommand : INotifyPropertyChanged
{
private bool _cRCmd;
private bool _cSCmd;
private string _cmd;
private bool _sCmd;
public string CMD
{
get { return _cmd; }
set
{
_cmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("CMD"));
}
}
public bool C_R_CMD
{
get { return _cRCmd; }
set
{
_cRCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("C_R_CMD"));
}
}
public bool S_CMD
{
get { return _sCmd; }
set
{
_sCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("S_CMD"));
}
}
public bool C_S_CMD
{
get { return _cSCmd; }
set
{
_cSCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("C_S_CMD"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
You should implement INotifyPropertyChanged in the Class_RetrieveCommand like this:
public class Class_RetrieveCommand : INotifyPropertyChanged
{
private string _CMD;
public string CMD
{
get { return _CMD; }
set { _CMD = value; OnPropertyChanged("CMD"); }
}
... similar for the other properties
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Unfortunately you can't use auto properties anymore then (except you resort to proxygenerators).

Trigger an event from within another class

In .NET I have a class called Caption. I have another class called Gauge. Within the Gauge class I have a property defined as a Caption.
I am trying to figure out how to do the following:
When a certain property is changed in my Caption class how do I get it to execute a subroutine in the Gauge class? I am thinking I have to declare an event and AddHandlers to fire it off, but I can't think of how to accomplish this.
You'll want to look at implementing the INotifyPropertyChanged interface, which is designed exactly for the purpose - raising an event when a property of a class instance changes.
A good example of usage is given on this MSDN page.
// This class implements a simple customer type
// that implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerName = String.Empty;
private string companyNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerName = "no data";
companyNameValue = "no data";
phoneNumberValue = "no data";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CompanyName
{
get {return this.companyNameValue;}
set
{
if (value != this.companyNameValue)
{
this.companyNameValue = value;
NotifyPropertyChanged("CompanyName");
}
}
}
public string PhoneNumber
{
get { return this.phoneNumberValue; }
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged("PhoneNumber");
}
}
}
}
public class Caption
{
private int myInt;
public event EventHandler MyIntChanged;
private void OnMyIntChanged()
{
var handler = this.MyIntChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public int MyInt
{
get
{
return this.myInt;
}
set
{
if (this.myInt != value)
{
this.myInt = value;
this.OnMyIntChanged();
}
}
}
}
So now, in your guage class:
public class Guage
{
private Caption caption;
public Caption Caption
{
get
{
return this.caption;
}
set
{
if (this.caption!= value)
{
this.caption= value;
this.caption.MyIntChanged += new EventHandler(caption_MyIntChanged);
}
}
}
private void caption_MyIntChanged(object sender, EventArgs e)
{
//do what you gotta do
}
}

Categories