Sending data from Main to Detail and Updating Object in MainViewMode - data-binding

I have the following recyclerview where it contains list of TestViewModel objects. In this object I have age, gender and name properties. I am trying to achieve when user click on a list item, it takes user to detail view where user could able to update and click on the save button, then it updates the selected item properties.
Issue:
The following piece of code in MainViewModel where I receive the message from DetailViewModel works when user enter values in the detail and updating each property,
private void OnMessageReceived(TestMessage obj)
{
_selectedItem.Age = obj.messageTest.Age;
_selectedItem.Name = obj.messageTest.Name;
_selectedItem.Gender = obj.messageTest.Gender;
}
but the following piece of code does not work where I am trying to update the object by itself directly.
private void OnMessageReceived(TestMessage obj)
{
_selectedItem= obj.messageTest;
RaisePropertyChanged(() => SelectedItem);
}
Code Implementation is as follows:
<MvxRecyclerView
android:id="#+id/TestRecyclerView"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
local:MvxBind="ItemsSource TestsViews; ; ItemClick ItemSelected" />
MainViewModel
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
ShowViewModel<DetailViewModel>(
new DetailViewModel.Parameter
{
Age = _selectedItem.Age,
Name = _selectedItem.Name,
Gender = _selectedItem.Gender,
});
RaisePropertyChanged(() => SelectedItem);
}
}
public virtual ICommand ItemSelected
{
get
{
return new MvxCommand<TestViewModel>(item =>
{
SelectedItem = item;
});
}
}
private void OnMessageReceived(TestMessage obj)
{
_selectedItem.Age= obj.messageTest.Age;
_selectedItem.Name= obj.messageTest.Name;
_selectedItem.Gender= obj.messageTest.Gender;
}
TestMessage
public class TestMessage : MvxMessage
{
public MainModel messageTest { get; set; }
public TestMessage(object sender, MainModel editTest) : base(sender)
{
messageTest = editTest;
}
}
DetailViewModel
public TestViewModel EditTest
{
get { return _editTest; }
set
{
_editTest = value;
RaisePropertyChanged(() => EditTest);
}
}
public DetailViewModel(IMvxMessenger messenger)
{
_messenger = messenger;
}
public void Save()
{
UpdateValues();
}
public void UpdateValues()
{
var message = new TestMessage(this, _editTest);
_messenger.Publish(message, typeof(TestMessage));
}
public void Init(Parameter param)
{
_editTest = new TestViewModel();
_editTest.Age = param.Age;
_editTest.Name = param.Name;
_editTest.Gender = param.Gender;
public class Parameter
{
public double Age { get; set; }
public int Gender { get; set; }
public string Name { get; set; }
}
DetailView xml
<EditText
style="#style/InputNumbersEditText"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="44dp"
android:gravity="center_vertical|right"
android:hint="00.000"
local:MvxBind="Text EditTest.Age, Converter=Nullable;" />
TestViewModel
public class TestViewModel : BaseViewModel
{
public string? Name { get; set; }
public double? Age { get; set; }
public int? Gender { get; set; }
}
NullableValueConverter
public class NullableValueConverter : MvxValueConverter
{
public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (string.IsNullOrEmpty(value?.ToString()))
{
return parameter;
}
return value;
}
public override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrEmpty(value.ToString()))
{
return null;
}
return value;
}
}

private void OnMessageReceived(TestMessage obj)
{
_selectedItem= obj.messageTest;
RaisePropertyChanged(() => SelectedItem);
}
This can't work, because your are just changing the reference of _selectedItem to point to another object. But this object is not included in the list that is used to show in the recycler view. You are not updating a object, just a reference! You should definitely have a look at the basics of C# to understand this kind of data structure. E.g. Reference vs. Value Type
Your code is a bit faulty.
You SelectedItem has the type MainViewModel
Your click command gets a item of type TestViewModel
public virtual ICommand ItemSelected
{
get
{
return new MvxCommand<TestViewModel>(item =>
{
SelectedItem = item;
});
}
}
Normally this should work:
private void OnMessageReceived(TestMessage obj)
{
_selectedItem.Age= obj.messageTest.Age;
_selectedItem.Name= obj.messageTest.Name;
_selectedItem.Gender= obj.messageTest.Gender;
}
but with a TestViewModel like
public class TestViewModel : BaseViewModel
{
private string? name;
public string? Name { get{ return name; } set { SetProperty(ref name, value); } }
// same for Age and Gender
}
SetProperty sets the value and calls the OnPropertyChanged event.

Updated Answer
Assigning the _selectedItem a new TestViewModel breaks the reference link it has to the TestViewModel held in the TestsViews data source list. While assigning the individual properties maintains the reference to the orginal TestViewModel in the TestsViews list.
Orginal Answer
As you are updating the backing field, _selectedItem, so when you receive the message event the RaisePropertyChanged event defined in the set of your SelectedItem property will never run. You will have to manually trigger the RaisePropertyChanged in order to trigger the binding update.
Try changing your current method:
private void OnMessageReceived(TestMessage obj)
{
_selectedItem= obj.messageTest;
}
To raise the property changed event:
private void OnMessageReceived(TestMessage obj)
{
_selectedItem = obj.messageTest;
RaisePropertyChanged(() => SelectedItem);
}

Related

List parameter on child component not updating

I have a child component for filtering a search (DropdownFilter) which takes an input of a list of suggestions and a function to update that list.
For some reason DropdownFilter.Suggestions isn't being updated after it is initially set and I don't know how to update it again. Any information about how to update the property after it is initially bound would be great!
DropdownFilter.razor:
<input id="search" #onfocus="SearchFocused" #onblur="SearchUnfocused" #oninput="UpdateSearchText" />
#foreach (var suggestion in Suggestions)
{
<p>#suggestion</p>
}
#code {
[Parameter]
public Action<string> SearchFieldChanged { get; set; }
//[Parameter]
//public RenderFragment<TSuggestion> SuggestionTemplate { get; set; }
[Parameter]
public List<string> Suggestions { get; set; }
private bool searchFocus = false;
private void SearchFocused(FocusEventArgs args) {
searchFocus = true;
//UpdateSearchText();
}
private void SearchUnfocused(FocusEventArgs args) => searchFocus = false;
private void UpdateSearchText(ChangeEventArgs args)
{
SearchFieldChanged.Invoke((string)args.Value);
}
public void Refresh() {
StateHasChanged();
}
}
Index.razor:
#page "/example"
<div class="container-fluid dropdown-holder">
<DropdownFilter #ref="dropdown" Suggestions="#maskResults" SearchFieldChanged="UpdateSearchResults" />
</div>
#code {
DropdownFilter dropdown;
public class MaskResult {
public string name;
}
static readonly string[] allMasks = {
"Electric",
"Water",
"Ground",
"Fire",
"Bug"
};
public List<string> maskResults = allMasks.ToList();
private void UpdateSearchResults(string search)
{
search = search.ToLower();
maskResults = allMasks.Where((mask) =>
{
return mask.ToLower().StartsWith(search);
}).ToList();
dropdown.Refresh();
}
}
I think that you are trying to create a Datalist, please check this answer:"
datalist How to bind selected item to object
If you add a StateHasChanged() call just here it should work:
private void UpdateSearchResults(string search)
{
search = search.ToLower();
maskResults = allMasks.Where((mask) =>
{
return mask.ToLower().StartsWith(search);
}).ToList();
StateHasChanged(); // Add this line
dropdown.Refresh();
}
As I understand, if you update manually a Parameter of a component, there are some cases where Blazor does not get automatically the info that it needs to update its components. So if you call StateHasChanged, it will reevaluate all Parameters of the childreen of the component where you do the call.
I'll let someone correct me if I am wrong.
Thanks and good luck :)

Clear entry text from ViewModel using RelayCommand

I would like to clear entry text from my ViewModel which is binded there. In the code below I tried it by using a RelayCommand, but it doesn't work.
What i want to accomplish: When clicking button named AddQuestionToQuiz, a function is executed by using Command on the button. The function OnCreateQuizClick(), located in my ViewModel, is triggerd and this function needs to clear my entry text, which i don't get for the moment.
I also tried to use a regular Command instead of using a RelayCommand, but also here it doesn't want to work.
EDIT: UNDERNEATH CODE WORKS FINE - GOT UPDATED
Code is used to clear entry text when clicking on a button from your ViewModel, implementing INotifyPropertyChanged Interface
.xaml - code
<Button x:Name="AddQuestionToQuiz" WidthRequest="200" Command="{Binding CreateQuizCommand}" Style="{StaticResource ButtonStyle}" Text="Add question to quiz"></Button>
ViewModel - code
internal class CreateQuizPageViewModel : INotifyPropertyChanged
{
// Quiz Name Input
public String QuizNameInput { get; set; }
private String quizQuestionInput = "";
public String QuizQuestionInput
{
get { return quizQuestionInput; }
set { quizQuestionInput = value; OnPropertyChanged(); }
}
public RelayCommand CreateQuizCommand { get; set; }
public CreateQuizPageViewModel()
{
CreateQuizCommand = new RelayCommand(OnCreateQuizClick);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnCreateQuizClick()
{
QuizQuestionInput = "";
}
}
EDIT: VIEWMODEL UPDATED
.xaml - code
<Button x:Name="AddQuestionToQuiz" WidthRequest="200" Command="{Binding CreateQuizCommand}" Style="{StaticResource ButtonStyle}" Text="Add question to quiz"></Button>
ViewModel - code
internal class CreateQuizPageViewModel : INotifyPropertyChanged
{
// Quiz Name Input
public String QuizNameInput { get; set; }
private String quizQuestionInput = "";
public String QuizQuestionInput
{
get { return quizQuestionInput; }
set { quizQuestionInput = value; OnPropertyChanged(); }
}
public RelayCommand CreateQuizCommand { get; set; }
public CreateQuizPageViewModel()
{
CreateQuizCommand = new RelayCommand(OnCreateQuizClick);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnCreateQuizClick()
{
QuizQuestionInput = "";
}
}

Save Data in Database using Unit Of Work Pattern

I have try to save data from database using Unit OF Work Pattern.But I got error.My code is:
Unit of Work code:
private PousadaManagementContext MinhaPousadaContext { get; }
public IPousadaRepository PousadaRepository { get; private set; }
public UnitOfWork
(
PousadaManagementContext minhaPousadaContext,
IPousadaRepository pousadaRepository
)
{
this.MinhaPousadaContext = minhaPousadaContext;
PousadaRepository = pousadaRepository;
}
public async Task<int> CompleteAsync()
{
return await MinhaPousadaContext.SaveChangesAsync();
}
public int Complete()
{
return MinhaPousadaContext.SaveChanges();
}
public void Dispose() => MinhaPousadaContext.Dispose();
public interface IUnitOfWork : IDisposable
{
int Complete();
Task<int> CompleteAsync ();
}
businessclass:
private UnitOfWork managementUoW { get; }
public PousadaBusiness(IUnitOfWork ManagementUoW)
{
ManagementUoW = managementUoW;
}
public async Task<Pousada> Save(Pousada p)
{
return await managementUoW.PousadaRepository.Save(p);
}
Here In Save method p all properties have value but managementUoW is null.
How to Solve this error.

Android changing order of items run-time in Realm Recyclerview

I need to order the list of items based on a field say starredAt
I am loading the data in the recyclerview from Realm DB using RealmRecyclerView by thorbenprimke
The field changes it value on user's action i.e when user presses star button the item should be moved to top.
For this I am just updating the starredAt field of the object.
The items are already sorted by starredAt so realm loads the updated list but it randomly adds one more item to the recyclerview.
CheatSheet.java
public class CheatSheet extends RealmObject {
#PrimaryKey
private String id;
private RealmList<Item> items;
private String title;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public RealmList<Item> getItems() {
return items;
}
public void setItems(RealmList<Item> items) {
this.items = items;
}
}
Item.java
public class Item extends RealmObject {
#PrimaryKey
private String id;
private String description;
private Date starredAt;
public Item() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getStarredAt() {
return starredAt;
}
public void setStarredAt(Date starredAt) {
this.starredAt = starredAt;
}
}
CheatSheetActivity.java
public class MainActivity extends AppCompatActivity {
RealmRecyclerView revItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setData();
}
private void setData() {
rvItems = (RealmRecyclerView) findViewById(R.id.rev_items);
RealmResults<Item> items = Realm.getDefaultInstance().where(CheatSheet.class)
.equalTo("id", "some-id").findFirst().getItems()
.where()
.findAllSorted("starredAt", Sort.DESCENDING);
ItemRealmListAdapter itemRealmListAdapter =
new ItemRealmListAdapter(this, items,
true, true);
rvItems.setAdapter(itemRealmListAdapter);
}
ItemRealmListAdapter.java
public class ItemRealmListAdapter extends RealmBasedRecyclerViewAdapter<Item,
ItemRealmListAdapter.ItemViewHolder> {
RealmResults<Item> mItems;
public ItemRealmListAdapter(Context context, RealmResults<Item> realmResults,
boolean automaticUpdate, boolean animateResults) {
super(context, realmResults, automaticUpdate, animateResults);
this.mItems = realmResults;
}
#Override
public ItemViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int i) {
return new ItemViewHolder(LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_layout_cs_text, viewGroup, false));
}
public Item getItem(int position) {
return mItems.get(position);
}
#Override
public void onBindRealmViewHolder(ItemViewHolder itemViewHolder, int position) {
itemViewHolder.txtBody.setText(getItem(position).getDescription());
if (getItem(position).getStarredAt() != null) {
itemViewHolder.imvStar.setImageResource(R.drawable.ic_star_yellow);
}
itemViewHolder.imvStar.setOnClickListener(v -> handleStarClick(v,position));
}
private void handleStarClick(View v, int position) {
if (getItem(position).getStarredAt() != null) {
((ImageView) v).setImageResource(R.drawable.ic_star);
CheatSheetStorage.unStarItem("some-id", getItem(position));
} else {
((ImageView) v).setImageResource(R.drawable.ic_star_yellow);
CheatSheetStorage.starItem("some-id", getItem(position));
}
}
public static class ItemViewHolder extends RealmViewHolder {
#Bind(R.id.txt_cheat_sheet)
TextView txtBody;
#Bind(R.id.img_star)
ImageView imvStar;
public ItemViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
CheatSheetStorage.java
public class CheatSheetStorage {
public static void unStarItem(String cheatSheetId, Item item) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
CheatSheet cheatSheet = getCheatSheetById(cheatSheetId);
Item itemDB = cheatSheet.getItems().where().equalTo("id", item.getId()).findFirst();
itemDB.setStarredAt(null);
realm.commitTransaction();
}
public static void starItem(String cheatSheetId, Item item) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
CheatSheet cheatSheet = getCheatSheetById(cheatSheetId);
Item itemDB = cheatSheet.getItems().where().equalTo("id", item.getId()).findFirst();
itemDB.setStarredAt(new Date());
realm.commitTransaction();
}
}
Please refer following screenshots for clearer idea :
Screenshot before starring
Screenshot after starring the sixth item
#Rohan-Peshkar - You will have to provide a animateExtraColumnName value to the adapter. For the animations, the adapter keeps track of the items and since that item's id doesn't change, the list isn't updated. With an additional column (in your case that should be the starredAt column - as long as it is stored as an Integer), the diffing algorithm will detect a change and the order is updated.
For reference: https://github.com/thorbenprimke/realm-recyclerview/blob/2835a543dce20993d8f98a4f773fa0e67132ce52/library/src/main/java/io/realm/RealmBasedRecyclerViewAdapter.java#L177
You can also check out the MainActivity in the example folder. The example changes a row's text from "ABC" to "Updated ABC" and the list recognizes the change because both the primary key and the quote field are used to basically create a composite key for diffing purposes.

Create a log everytime When methods in an interface class are called

I want to update a log file(txt) everytime when methods in a an interface class are called?
Is there any way to do this other than writing code in every method to create log?
Here's my 30 mins. you'll have to implement the logging code somewhere so you have to create another abstraction for your code. thus an abstract class is needed. i think. this is very quick and dirty.
public interface IService<T>
{
List<T> GetAll();
bool Add(T obj);
}
then you'll need the abstract class where you'll need to implement your logging routine
public abstract class Service<T> : IService<T>
{
private void log()
{
/// TODO : do log routine here
}
public bool Add(T obj)
{
try
{
log();
return AddWithLogging(obj);
}
finally
{
log();
}
}
public List<T> GetAll()
{
try
{
log();
return GetAllWithLog();
}
finally
{
log();
}
}
protected abstract List<T> GetAllWithLog();
protected abstract bool AddWithLogging(T obj);
}
as for your concrete classes
public class EmployeeService : Service<Employee>
{
protected override List<Employee> GetAllWithLog()
{
return new List<Employee>() { new Employee() { Id = 0, Name = "test" } };
}
protected override bool AddWithLogging(Employee obj)
{
/// TODO : do add logic here
return true;
}
}
public class CompanyService : Service<Company>
{
protected override List<Company> GetAllWithLog()
{
return new List<Company>() { new Company() { Id = 0, Name = "test" } };
}
protected override bool AddWithLogging(Company obj)
{
/// TODO : do add logic here
return true;
}
}
public class Employee
{
public int Id {get;set;}
public string Name { get; set;}
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
then on your implementation you can just..
static void Main(string[] args)
{
IService<Employee> employee = new EmployeeService();
List<Employee> employees = employee.GetAll();
foreach (var item in employees)
{
Console.WriteLine(item.Name);
}
IService<Company> company = new CompanyService();
List<Company> companies = company.GetAll();
foreach (var item in companies)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
hope this helps!
I think you would have to use Aspect Oriented Programming to achieve that. Read http://www.sharpcrafters.com/aop.net
I think you meant class (instead of interface)
Two options I can think of:
Implementing INotifyPropertyChanged which is in lines of writing code in every method
or
to adopt on of the AOP frameworks in the article http://www.codeproject.com/KB/cs/AOP_Frameworks_Rating.aspx if that is not a major leap

Resources