MvxSpinner in Mvvmcross - mvvmcross

I have three MvxSpinners in my android view.
These spinners are binded to three different lists.
and Mode of data binding is TwoWay for these spinners.i.e. when this view is
displayed,all of these three spinners are get displayed with the predefined values.
When user change the value in first spinner,then second spinner will be clear and
get loaded with new values based on the selected value in first spinner.
How can I achieve this?

There's many ways to accomplish this, where the code placement is really up to you. Overall the idea would be to have a "SelectedItem" object that you can pass into your method and "Load" the next List.
Please keep in mind that this code is more traditional MVVM, but can easily be converted to MVVMCross equivalent. I believe all these types should be supported by MVVMCross.
private MyFirstObject _selectedFirstObject;
public MyFirstObject SelectedFirstObject
{
get { return _selectedFirstObject; }
set
{
_selectedFirstObject = value;
RaisePropertyChanged("SelectedFirstObject");
if(value != null)
LoadMySecondObjects(value);
}
}
private ObservableCollection<MyFirstObject> _myFirstObjects;
public ObservableCollection<MyFirstObject> MyFirstObjects
{
get { return _myFirstObjects; }
set
{
_myFirstObjects = value;
RaisePropertyChanged("MyFirstObjects");
}
}
private ObservableCollection<MySecondObject> _mySecondObjects;
public ObservableCollection<MySecondObject> MySecondObjects
{
get { return _mySecondObjects; }
set
{
_mySecondObjects = value;
RaisePropertyChanged("MySecondObjects");
}
}
public void LoadMySecondObjects(MyFirstObject current)
{
//Wherever you're pulling data from
MySecondObjects = MyDataService.GetAll(current);
}
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;

I had the same problem but only if you add null value (as a default value) to your ItemsSource and try to reset SelectedItem to null. SelectedItem is changed in ViewModel but not in the spinner. In that case there's number of solutions but I used message to inform View to set selected item
public class SpinnerSelectionChanged : MvxMessage
{
public SpinnerSelectionChanged(object sender, string spinnerName, int position): base(sender)
{
SpinnerName = spinnerName;
Position = position;
}
public string SpinnerName { get; set; }
public int Position { get; set; }
}
in View
private void OnSpinnerSelectionChanged(SpinnerSelectionChanged obj)
{
switch (obj.SpinnerName)
{
case "City":
_spinnerCity.SetSelection(obj.Position);
break;
case "Office":
_spinnerOffice.SetSelection(obj.Position);
break;
}
}

Related

My static variables in my Blazor Server app are keeping their values, even if I refresh the page or even I close the tab and login again. Why?

I have a Blazor server app. Some variables on a specific razor page (main.razor) are defined as static because I want that these variables keep their values when the client navigates to other pages in the same project and comes back again to main.razor. So far it is working good.
But when I refresh the complete page, or even close the tab and reopen my app (login again), I see that the static variables still keep their values. How can prevent this? Of course I want that the values return to their default values (like 0 or ""), when the client makes a login or refreshes the page with F5. How can I do that?
I have defined the related variables in the following way:
private static StringBuilder log = new StringBuilder();
public static string testvar1= "";
public static int testvar2= 0;
Statics exist for the lifetime of the application instance which explains the behaviour you see.
You need to be maintaining state. At one end of the spectrum you can implement a State Management system such as Fluxor. At the other just create a user class, set it up as a service and inject it as a Scoped Service. Or you can build a middle-of-the-road solution.
This is mine.
A generic UIStateService that maintains a Dictionary of (state)objects against a Guid.
public class UIStateService
{
private Dictionary<Guid, object> _stateItems = new Dictionary<Guid, object>();
public void AddStateData(Guid Id, object value)
{
if (_stateItems.ContainsKey(Id))
_stateItems[Id] = value;
else
_stateItems.Add(Id, value);
}
public void ClearStateData(Guid Id)
{
if (_stateItems.ContainsKey(Id))
_stateItems.Remove(Id);
}
public bool TryGetStateData<T>(Guid Id, out T? value)
{
value = default;
if (Id == Guid.Empty)
return false;
var isdata = _stateItems.ContainsKey(Id);
var val = isdata
? _stateItems[Id]
: default;
if (val is T)
{
value = (T)val;
return true;
}
return false;
}
}
Set it up as a service:
builder.Services.AddScoped<UIStateService>();
Next define a simple template ComponentBase page that contains the common page code:
using Blazr.UI;
using Microsoft.AspNetCore.Components;
namespace BlazorApp2.Pages
{
public class StatePage : ComponentBase
{
// this provides a guid for this specific page during the lifetime of the application runtime
// we use this as the reference to store the state data against
private static Guid RouteId = Guid.NewGuid();
[Inject] protected UIStateService UIStateService { get; set; } = default!;
protected void SaveState<T>(T state) where T : class, new()
{
if (RouteId != Guid.Empty)
this.UIStateService.AddStateData(RouteId, state);
}
protected bool GetState<T>( out T value) where T : class, new()
{
value = new T();
if (RouteId != Guid.Empty && this.UIStateService.TryGetStateData<T>(RouteId, out T? returnedState))
{
value = returnedState ?? new T();
return true;
}
else
return false;
}
}
}
And use it in a page:
#page "/"
#inherits StatePage
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
<div class="p-2">
<button class="btn btn-primary" #onclick=SetData>Set Data</button>
</div>
<div class="p-3 text-primary">
State Time : #stateData.StateTime;
</div>
#code {
private MyStateData stateData = new MyStateData();
protected override void OnInitialized()
{
if (this.GetState<MyStateData>(out MyStateData value))
this.stateData = value;
else
this.SaveState<MyStateData>(this.stateData);
}
private void SetData()
{
this.stateData.StateTime = DateTime.Now.ToLongTimeString();
SaveState<MyStateData>(this.stateData);
}
public class MyStateData
{
public string StateTime { get; set; } = DateTime.Now.ToLongTimeString();
}
}
You can now navigate around the application and the state will be maintained for the page.
You can apply an observer/notification pattern to the state object to trigger automatic state updates if you wish.

Adding an index via LINQ to Windows Phone database

We have a C# Windows Phone application and I am trying to make use of dbschemaupdater.AddIndex().
However, I am unsure of how to define the fields associated with the index and cannot find any online examples that seem relevant.
Our database tables are defined as classes via SQLMetal, e.g.
[global::System.Data.Linq.Mapping.TableAttribute(Name = "PDA_AppActiveLog")]
public partial class PDA_AppActiveLog : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
[Column(IsVersion = true)]
private Binary version;
private int _AppActiveLogID;
private DateTime _EventTime;
private string _EventCode;
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnAppActiveLogIDChanging(int value);
partial void OnAppActiveLogIDChanged();
partial void OnEventTimeChanging(DateTime value);
partial void OnEventTimeChanged();
partial void OnEventCodeChanging(string value);
partial void OnEventCodeChanged();
#endregion
public PDA_AppActiveLog()
{
OnCreated();
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_AppActiveLogID", AutoSync = AutoSync.OnInsert, DbType = "Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
public int AppActiveLogID
{
get
{
return this._AppActiveLogID;
}
set
{
if ((this._AppActiveLogID != value))
{
this.OnAppActiveLogIDChanging(value);
this.SendPropertyChanging();
this._AppActiveLogID = value;
this.SendPropertyChanged("AppActiveLogID");
this.OnAppActiveLogIDChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_EventTime", DbType = "DateTime NOT NULL", CanBeNull = false)]
public DateTime EventTime
{
get
{
return this._EventTime;
}
set
{
if ((this._EventTime != value))
{
this.OnEventTimeChanging(value);
this.SendPropertyChanging();
this._EventTime = value;
this.SendPropertyChanged("EventTime");
this.OnEventTimeChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_EventCode", DbType = "NVarChar(30)", UpdateCheck = UpdateCheck.Never, CanBeNull = true)]
public string EventCode
{
get
{
return this._EventCode;
}
set
{
if ((this._EventCode != value))
{
this.OnEventCodeChanging(value);
this.SendPropertyChanging();
this._EventCode = value;
this.SendPropertyChanged("EventCode");
this.OnEventCodeChanged();
}
}
}
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void SendPropertyChanging()
{
if ((this.PropertyChanging != null))
{
this.PropertyChanging(this, emptyChangingEventArgs);
}
}
protected virtual void SendPropertyChanged(String propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Within our code, we add something like:
if (dbSchema.DatabaseSchemaVersion == 8)
{
dbSchema.AddTable<PDA_AppActiveLog>();
dbSchema.DatabaseSchemaVersion = 9;
//dbSchema.AddIndex<PDA_AppActiveLog>("EventCode");
dbSchema.Execute();
dbSchema = dc.CreateDatabaseSchemaUpdater();
}
However, I am unsure how to define which fields belong to the new index.
It seems from this article, that the functionality is there:
http://justinangel.net/AllWp7MangoAPIs#linq2sql
However, all the examples I've seen show the database definition code differently, e.g.:
http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394022(v=vs.105).aspx#BKMK_Preparingversion3Addinganindexconsideringmultipleupgradepaths
// Index added in version 3 of the application.
[Index(Columns = "Priority", Name = "PriorityIndex")]
I am unsure if I can make equivalent changes, but even if I am, then I can no longer use SQLMetal to pre-generate the classes unless I want to modify them everytime afterwards?
What is the best way to get an index added?
Thanks.

Binding StringElement (MT.D) with MvvmCross

We are using some MT.D StringElements, and their Value Property is bound to properties in the ViewModel.
The initial value is correctly shown but when the ViewModel changes some values and triggers PropertyChanged then the StringElements contain the good value but the display is not refreshed.
If we scroll the Controller or touch the StringElement then it is refreshed: the correct value is displayed.
Do you have any idea?
This is our ViewController
public class ContactView : MvxDialogViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
var bindings = this.CreateInlineBindingTarget<ContactViewModel> ();
Root = new RootElement()
{
new Section()
{
new StringElement("Company Name").Bind(bindings, vm => vm.CompanyName)
}
}
}
}
This is our ViewModel (simplified)
public class ContactViewModel : MvxViewModel
{
private string companyName;
public string CompanyName{
get{return companyName;}
set{companyName = value; RaisePropertyChanged(() => CompanyName);}
}
public async Task Init(string id)
{
var contact = await someService.SomeMethodAsync();
CompanyName = contact.CompanyName;
}
}
I found two solutions to my problem:
If I use UIView.Transition to replace the content then, in the new View, nothing is refreshed when I change the ViewModel (unless I scroll or tap it) UNLESS if the ViewModel properties have some default value non null and non empty
If I don't transition but use another method like this one to replace the content:
Sample code
MasterNavigationController.PopToRootViewController(false);
MasterNavigationController.ViewControllers = new UIViewController[] { viewController };
In this case the content is replaced and the view is refreshed when a ViewModel property changes: everything works correctly in this case.
I tried a viewmodel like:
public class FirstViewModel
: MvxViewModel
{
private Timer _timer;
private int _count;
public FirstViewModel()
{
_timer = new Timer(DoThis, null, 1000, 1000);
}
private void DoThis(object state)
{
_count++;
TextProperty = _count.ToString();
}
private string _textProperty = "T";
public string TextProperty
{
get { return _textProperty; }
set { _textProperty = value; RaisePropertyChanged(() => TextProperty); }
}
}
with a dialog view defined like:
Root = new RootElement("Example Root")
{
new Section("Debut in:")
{
new EntryElement("Login", "Enter Login name").Bind(bindings, vm => vm.TextProperty)
},
new Section("Debug out:")
{
new StringElement("Value is:").Bind(bindings, vm => vm.TextProperty),
};
It worked fine - ticking up every second...

Binding to IEnumerable rather than IList this can be inefficient - how inefficient it is?

In order to find walk-around for absence RelativeSource in MvvmCross, I used Stuart's suggestion and implemented WrappingList
MVVMCross changing ViewModel within a MvxBindableListView
However, I see this trace every bind it happens and I wonder, how worse it is:
Binding to IEnumerable rather than IList - this can be inefficient,
especially for large lists
Maybe there are any other suggestions?
public class WrappingCommandsList<T> : IList<WrappingCommandsList<T>.Wrapped>
{
private readonly List<T> _realList;
private readonly Action<T> _realActionOnClick;
public class Wrapped
{
public IMvxCommand ClickCommand { get; set; }
public T TheItem { get; set; }
}
public WrappingCommandsList(List<T> realList, Action<T> realActionOnClick)
{
_realList = realList;
_realActionOnClick = realActionOnClick;
}
private Wrapped Wrap(T item)
{
return new Wrapped()
{
ClickCommand = new MvxCommand(() => _realActionOnClick(item)),
TheItem = item
};
}
public WrappingCommandsList<T>.Wrapped this[int index]
{
get
{
return Wrap(_realList[index]);
}
set
{
throw new NotImplementedException();
}
}
public int Count
{
get { return _realList.Count; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<WrappingCommandsList<T>.Wrapped> GetEnumerator()
{
foreach (var item in _realList)
{
yield return Wrap(item);
}
}
How inefficient depends on the length of your list and how far down it the used has scrolled.
eg if the user is showing item 93,256 on the screen then the only way for the list adapter to find item 93,256 is to get the enumerator and to call MoveNext 93,256 times.
Whereas if your list only has 5 items the problem is bounded by 5.
For your particular WrappingCommandsList try implementing IList as well as IList<T> - the mvx code can't generate the IList<T> accessors at runtime because of xamarin.ios AoT compilation restrictions.

linq2sql missing event model?

How come the "Table" classes Generated in the Dbml do not contain useful events like
OnBeforeInsert
OnBeforeUpdate
OnAfterInsert
etc.
Am I missing something?
This question is related to frustration trying to set timestamp columns.
UPDATE
I created the following method of doing this neatly what does everyone think?
public class Model
{
internal virtual void OnBeforeInsert()
{
}
internal virtual void OnBeforeUpdate()
{
}
}
public partial class DbDataContext
{
public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode)
{
foreach (var insert in this.GetChangeSet().Inserts)
{
if (insert is Model)
{
((Model)insert).OnBeforeInsert();
}
}
foreach (var update in this.GetChangeSet().Updates)
{
if (update is Model)
{
((Model)update).OnBeforeUpdate();
}
}
base.SubmitChanges(failureMode);
}
}
public partial class Address : Model
{
internal override void OnBeforeInsert()
{
var created = DateTime.Now;
this._Modified = created;
this._Created = created;
}
}
I had a similar issue like this recently.
There is a partial method in the generated class for "OnValidate". Simply declaring the method in your partial will force it to be called (vb.net does not support partial methods like c#) or in c# simply declare a partial method.
The method is passed a System.Data.Linq.ChangeAction enum that is either: Delete, Insert, Update, or None.
Below is a sample of what you did using the built in partial method.
public partial class Address
{
private partial void OnValidate(System.Data.Linq.ChangeAction action)
{
if (action == System.Data.Linq.ChangeAction.Insert)
{
var created = DateTime.Now;
this._Modified = created;
this._Created = created;
} else if (action == System.Data.Linq.ChangeAction.Update) {
this._Modified = DateTime.Now;
}
}
}