adding DataItems from one page to another in Windows Store Apps? - c#

I am creating windows store app in VS2012 c#/xaml using WindowsStore GridApp Template.
And I am using Group and Items pages that this template has.
In Group page I am displaying a list of Rooms - datasource for this are RoomObjects
public class RoomsObject : LivingDataCommon
{
public RoomsObject()
: base(String.Empty, String.Empty)
{
}
public RoomsObject(String ID, String title)
: base(ID, title)
{ }
//adds Actors to collection of a Room, will be used for Rooms pages
private ObservableCollection<ActorsObject> _actors = new ObservableCollection<ActorsObject>();
public ObservableCollection<ActorsObject> Actors
{
get { return this._actors; }
}
}
In Item page I am displaying a list of Actors that each Room has - datasource for this are ActorsObjects
public class ActorsObject : LivingDataCommon
{
public ActorsObject()
: base(String.Empty, String.Empty)
{
}
public ActorsObject(String ID, String title, Boolean homepage,String function, RoomsObject room, double currentValue, ActorsType type, AllActors allactors)
: base(ID, title)
{
this._function = function;
this._room = room;
this._currentValue = currentValue;
this._type = type;
this._homepage = homepage;
this._all = allactors;
}
//set home page appearance
private Boolean _homepage = false;
public static Boolean Homepage = false;
//sets value of an actor
private double _currentValue;
public double CurrentValue
{
get { return this._currentValue; }
set { this.SetProperty(ref this._currentValue, value); }
}
//sets and gets function code
private string _function = string.Empty;
public string Function
{
get { return this._function; }
set { this.SetProperty(ref this._function, value); }
}
//gets room properity
private RoomsObject _room;
public RoomsObject Room
{
get { return this._room; }
set { this.SetProperty(ref this._room, value); }
}
private ActorsType _type;
public ActorsType Type
{
get { return this._type; }
set { this.SetProperty(ref this._type, value); }
}
private AllActors _all;
public AllActors All
{
get { return this._all; }
set { this.SetProperty(ref this._all, value); }
}
}
When I select an Actor in Items page my appbar appears and I need on my pinButton to allow that Actor to be displayed at Home.xaml as well.
I am assuming that I should create an empty ObservableCollection an add selected items to it, and then use that collection as data source for Home.xaml, but I am new at c#, I cant get it work..
Please any suggestions, code, or some different ways to do this?

Hmm.. just to give you some input. I would probably create a "global" static class for this, which you can access from your entire app (public static class PinnedActors). Within this class you have your static ObservableCollection.

Related

How to get a list of all errors in a column on a WPF DataGrid control?

I have a DataGrid control bound to an ObservableCollection of a model type that implements INotifyDataErrorInfo. The DataGrid displays the error info correctly on cells in the PhoneNumber column. However, I want to display the number of phone numbers with error to the user before they submit the data to the database (say, a number with a tooltip somewhere on the page). I've scoured the internet for any clue about this, but nothing. Here's the implementation of the model class:
'''
public class ContactModel : ObservableObject, INotifyDataErrorInfo
{
private readonly List<string> _errors = new();
private string _firstName = String.Empty;
public string FirstName
{
get => _firstName;
set
{
_firstName = value;
OnPropertyChanged(nameof(FirstName));
}
}
private string _lastName = String.Empty;
public string LastName
{
get => _lastName;
set
{
_lastName = value;
OnPropertyChanged(nameof(LastName));
}
}
private string _phoneNumber = string.Empty;
public string PhoneNumber
{
get => _phoneNumber;
set
{
_phoneNumber = value;
OnPropertyChanged(nameof(PhoneNumber));
}
}
public bool HasErrors
{
get
{
return _errors.Any();
}
}
public IEnumerable GetErrors(string? propertyName)
{
switch (propertyName)
{
case nameof(PhoneNumber):
if (string.IsNullOrEmpty(nameof(PhoneNumber))
{
_errors.Add("Phone number is blank");
}
break;
case nameof(FirstName):
// do nothing
break;
case nameof(LastName):
// do nothing
break;
default:
break;
}
return _errors;
}
public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
}
'''
this is a missunderstanding on INotifyDataErrorInfo implementation
on this way
you can validate only one property. if two property have errors, you cannot find error of each property
you cannot find model is valid or not, until GetError called.
add ValidationErrors property
make "_errors" property as public property as ObservableCollection
private ObservableCollection<ValidationResult> _errors;
public ObservableCollection<ValidationResult> Errors
{
get{return _errors;}
set{_errors = value;
OnPropertyChanged(nameof(Errors));
}
}
then construct it at model cunstructor.
then invoke validation procedure on property setter.
private string _phoneNumber = string.Empty;
public string PhoneNumber
{
get => _phoneNumber;
set
{
_errors.RemoveRange(_errors.Where(w=> w.Membernames.Containes(nameof(PhoneNumber))));
_phoneNumber = value;
if (string.IsNullOrEmpty(value)
{
_errors.Add(new ValidationResult("Phone number is blank",new string[]{nameof(PhoneNumber)}));
ErrorsChanged?.invoke(this,new DataErrorsChangedEventArgs(nameof(PhoneNumber)));
}
OnPropertyChanged(nameof(PhoneNumber));
}
}
change GetError to
public IEnumerable GetErrors(string? propertyName)
{
return _errors.Where(w=> w.Membernames.Containes(propertyName);
}
so you can add a listbox (or a complex control called ValidationSummary) to your form and bind it's source to ValidationErrors property of your model.
best practice implementation of InotifyPropertyInfo is to Implement it on basemodel (ObservableObject in your sample) class.

MvvmCross - Passing a string with IMvxNavigationService

I'm currently working on a Xamarin.iOS project that uses a web-api to gather data. However, I'm running into some problems trying to pass the user input from a textfield to the Tableview that gets the result from the api.
To do this I've followed the example on the MvvmCross documentation.
The problem is that the input from the Textfield never reaches the 'Filter' property in my TableviewController's viewmodel. I think I'm not passing the string object correctly to my IMvxNavigationService when called.
To clarify, in my UserinputViewController I'm binding the textfield's text like so:
[MvxFromStoryboard(StoryboardName = "Main")]
public partial class SearchEventView : MvxViewController
{
public SearchEventView (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
MvxFluentBindingDescriptionSet<SearchEventView, SearchEventViewModel> set = new MvxFluentBindingDescriptionSet<SearchEventView, SearchEventViewModel>(this);
set.Bind(btnSearch).To(vm => vm.SearchEventCommand);
set.Bind(txtSearchFilter).For(s => s.Text).To(vm => vm.SearchFilter);
set.Apply();
}
}
The Viewmodel linked to this ViewController looks like this:
public class SearchEventViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
private string _searchFilter;
public string SearchFilter
{
get { return _searchFilter; }
set { _searchFilter = value; RaisePropertyChanged(() => SearchFilter); }
}
public SearchEventViewModel(IMvxNavigationService mvxNavigationService)
{
this._navigationService = mvxNavigationService;
}
public IMvxCommand SearchEventCommand {
get {
return new MvxCommand<string>(SearchEvent);
}
}
private async void SearchEvent(string filter)
{
await _navigationService.Navigate<EventListViewModel, string>(filter);
}
}
And finally, TableviewController's viewmodel looks like this:
public class EventListViewModel : MvxViewModel<string>
{
private readonly ITicketMasterService _ticketMasterService;
private readonly IMvxNavigationService _navigationService;
private List<Event> _events;
public List<Event> Events
{
get { return _events; }
set { _events = value; RaisePropertyChanged(() => Events); }
}
private string _filter;
public string Filter
{
get { return _filter; }
set { _filter = value; RaisePropertyChanged(() => Filter); }
}
public EventListViewModel(ITicketMasterService ticketMasterService, IMvxNavigationService mvxNavigationService)
{
this._ticketMasterService = ticketMasterService;
this._navigationService = mvxNavigationService;
}
public IMvxCommand EventDetailCommand {
get {
return new MvxCommand<Event>(EventDetail);
}
}
private void EventDetail(Event detailEvent)
{
_navigationService.Navigate<EventDetailViewModel, Event>(detailEvent);
}
public override void Prepare(string parameter)
{
this.Filter = parameter;
}
public override async Task Initialize()
{
await base.Initialize();
//Do heavy work and data loading here
this.Events = await _ticketMasterService.GetEvents(Filter);
}
}
Whenever trying to run, the string object 'parameter' in my TableviewController's Prepare function remains 'null' and I have no idea how to fix it. Any help is greatly appreciated!
I believe the issue is with your command setup
new MvxCommand<string>(SearchEvent);
As this command is being bound to a standard UIButton. It will not pass through a parameter value of your filter but null instead. So the string parameter generic can be removed. Additionally, as you want to execute an asynchronous method I would suggest rather using MvxAsyncCommand
new MvxAsyncCommand(SearchEvent);
Then in terms of SearchEvent method you can remove the parameter. The value of filter is bound to your SearchFilter property. It is this property's value that you want to send as the navigation parameter.
private async Task SearchEvent()
{
await _navigationService.Navigate<EventListViewModel, string>(SearchFilter);
}

NavigationService WithParam of Caliburn Micro for WP

As is well known, CM doesn't support passing a object of complex type through NavigationService like MVVM Light. So I searched for a workaround and did it like this.
There are two viewmodels: MainPageViewModel and SubPageViewModel.
I first defined 3 classes, namely GlobalData, SnapshotCache and StockSnapshot. StockSnapshot is the type of which the object I want to pass between the 2 viewmodels.
public class SnapshotCache : Dictionary<string, StockSnapshot>
{
public StockSnapshot GetFromCache(string key)
{
if (ContainsKey(key))
return this[key];
return null;
}
}
public class GlobalData
{
private GlobalData()
{
}
private static GlobalData _current;
public static GlobalData Current
{
get
{
if (_current == null)
_current = new GlobalData();
return _current;
}
set { _current = value; }
}
private SnapshotCache _cachedStops;
public SnapshotCache Snapshots
{
get
{
if (_cachedStops == null)
_cachedStops = new SnapshotCache();
return _cachedStops;
}
}
}
public class StockSnapshot
{
public string Symbol { get; set; }
public string Message { get; set; }
}
Next, I call the navigation service on MainPageViewModel like this:
StockSnapshot snap = new StockSnapshot {Symbol="1", Message = "The SampleText is here again!" };
GlobalData.Current.Snapshots[snap.Symbol] = snap;
NavigationService.UriFor<SubPageViewModel>().WithParam(p=>p.Symbol,snap.Symbol).Navigate();
And on SubPageViewModel I've got this:
private string _symbol;
public string Symbol
{
get { return _symbol; }
set
{
_symbol = value;
NotifyOfPropertyChange(() => Symbol);
}
}
public StockSnapshot Snapshot
{
get { return GlobalData.Current.Snapshots[Symbol]; }
}
And that's where the problem lies. When I run the program, I find out that it always runs to the getter of Snapshot first, when Symbol hasn't been initialized yet. So later I've tried adding some extra code to eliminate the ArgumentNullException so that it can run to the setter of Symbol and then everything goes fine except that the UI doesn't get updated anyway.
Could anyone tell me where I've got wrong?
Thx in advance!!
Why not just use:
private string _symbol;
public string Symbol
{
get { return _symbol;}
set
{
_symbol = value;
NotifyOfPropertyChange(() => Symbol);
NotifyOfPropertyChange(() => Snapshot);
}
}
public StockSnapshot Snapshot
{
get { return Symbol!=null? GlobalData.Current.Snapshots[Symbol]:null; }
}
In this case you don't try and get the data from GlobalData when Symbol is null (sensible approach anyway!) and when "Symbol" is set you call NotifyOfPropertyChange() on Snapshot to force a re-get of the property.

I want to set an object to a property grid item with a button that will pop up a form. Not working as expected

So I have a property grid that I want to have an object bound to. When the application is running a button will show up and display a form which will allow that object to have its properties set and returned back to the property that called the form.
Here is what I have so far:
I cant get the property to show up in my property grid. Basically I want to use a form to fill in other items in the property grid.
I hope my question was clear enough...
public class OptoSetupFormEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, System.IServiceProvider provider, object value)
{
IWindowsFormsEditorService svc = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
DataTemp opto = value as DataTemp;
if (svc != null && opto != null)
{
using (OptoSigmaSetup form = new OptoSigmaSetup())
{
if (svc.ShowDialog(form) == DialogResult.OK)
{
opto.Direction = form.Direction;
opto.FunctionToCall = form.FunctionToCall;
opto.Duration = form.Duration;
// OptoSigmaTest.Command = form.
}
}
}
return opto; // can also replace the wrapper object here
}
}
[Editor(typeof(OptoSetupFormEditor),typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
public DataTemp Test1
{
set
{
this.Duration = value.Duration ;
this.Direction = value.Direction;
this.FunctionUsed = value.FunctionToCall;
}
}
[ReadOnly(true), Category("Setup")]
public string FunctionUsed
{
get { return functionUsed; }
set { functionUsed = value; }
}
[ReadOnly(true), Category("Setup")]
public int Duration
{
get { return duration; }
set { duration = value; }
}
[ReadOnly(true),Category("Setup")]
public string Direction
{
get { return direction; }
set { direction = value; }
}
public class DataTemp
{
private int duration = 0;
private string direction = "Positive";
private string functionToCall = "Home";
public string FunctionToCall
{
get { return functionToCall; }
set { functionToCall = value; }
}
public int Duration
{
get { return duration; }
set { duration = value; }
}
public string Direction
{
get { return direction; }
set { direction = value; }
}
}
Thanks in advance. If it needs more clarification, please let me know
Have you tried augmenting the properties you wish to display with the Browsable attribute?
[Browsable(true)]
[ReadOnly(true), Category("Setup")]
public string FunctionUsed
{
get { return functionUsed; }
set { functionUsed = value; }
}
MSDN: BrowsableAttribute
EDIT:
Hm, according to MSDN, properties without this attribute, or those which have the attribute specified with a value of true, should display in Property windows; so this is a suggestion left wanting.

which winforms control/approach to bind to a List<> collection of custom objects?

Which control approach can i use to quickly provide visual editing of my List collection.
The in-memory collection I have is below.
My requirements are basically to:
provide a means on my winform form, to allow add/view/edit of the List of ConfigFileDTO information, BUT
Only the "PATH" field of the ConfigFileDTO needs to be made a available to the user, therefore the use could:
add a new PATH to the list,
delete PATHs, hence deleting the ConfigFileDTO from the list,
and edit the list, allowing one of the PATH's in the list to be changed.
My code
private static List<ConfigFileDTO> files;
public class ConfigFileDTO
{
private string filename, content_type, path;
private int file_size;
private DateTime updated_at;
public ConfigFileDTO() { }
public int FileSize {
get { return this.file_size; }
set { this.file_size = value; }
}
public string ContentType {
get { return this.content_type; }
set { this.content_type = value; }
}
public string Filename {
get { return this.filename; }
set { this.filename = value; }
}
public DateTime UpdatedAt {
get { return this.updated_at; }
set { this.updated_at = value; }
}
public string Path {
get { return this.path; }
set { this.path = value; }
}
}
Thanks
If you only want the Path column to be manipulated, then it is usually better to simply set up the column bindings (for things like DataGridView) manually; however, you can also use things like [Browsable(false)] (removes a property from display) and [ReadOnly(true)] (treat a property as read-only even if it has a setter) to control how properties (/columns) are handled.
If you want to control how new instances are created, inherit from BindingList<T> and override AddNewCore().

Categories

Resources